Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Design fine-grained access control within record manager #70

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
109 changes: 109 additions & 0 deletions src/main/java/cz/cvut/kbss/study/model/Role.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
package cz.cvut.kbss.study.model;

import cz.cvut.kbss.jopa.model.annotations.Individual;

public enum Role {

// TODO deprecated -- should be removed.
@Individual(iri=Vocabulary.s_i_administrator)
administrator(Vocabulary.s_i_administrator),
// TODO deprecated -- should be removed.
@Individual(iri = Vocabulary.s_i_user)
user(Vocabulary.s_i_user),

@Individual(iri = Vocabulary.s_i_impersonate_role)
impersonate(Vocabulary.s_i_impersonate_role),

@Individual(iri = Vocabulary.s_i_delete_all_records_role)
deleteAllRecords(Vocabulary.s_i_delete_all_records_role),

@Individual(iri = Vocabulary.s_i_view_all_records_role)
viewAllRecords(Vocabulary.s_i_view_all_records_role),

@Individual(iri = Vocabulary.s_i_edit_all_records_role)
editAllRecords(Vocabulary.s_i_edit_all_records_role),

@Individual(iri = Vocabulary.s_i_delete_organization_records_role)
deleteOrganizationRecords(Vocabulary.s_i_delete_organization_records_role),

@Individual(iri = Vocabulary.s_i_view_organization_records_role)
viewOrganizationRecords(Vocabulary.s_i_view_organization_records_role),

@Individual(iri = Vocabulary.s_i_edit_organization_records_role)
editOrganizationRecords(Vocabulary.s_i_edit_organization_records_role),

@Individual(iri = Vocabulary.s_i_edit_users_role)
editUsers(Vocabulary.s_i_edit_users_role),

@Individual(iri = Vocabulary.s_i_complete_records_role)
completeRecords(Vocabulary.s_i_complete_records_role),

@Individual(iri = Vocabulary.s_i_reject_records_role)
rejectRecords(Vocabulary.s_i_reject_records_role),

@Individual(iri = Vocabulary.s_i_publish_records_role)
publishRecords(Vocabulary.s_i_publish_records_role),

@Individual(iri = Vocabulary.s_i_import_codelists_role)
importCodelists(Vocabulary.s_i_import_codelists_role);

private final String iri;

Role(String iri) {
this.iri = iri;
}

public String getIri() {
return iri;
}

/**
* Returns {@link Role} with the specified IRI.
*
* @param iri role identifier
* @return matching {@code Role}
* @throws IllegalArgumentException When no matching role is found
*/
public static Role fromIri(String iri) {
for (Role r : values()) {
if (r.getIri().equals(iri)) {
return r;
}
}
throw new IllegalArgumentException("Unknown role identifier '" + iri + "'.");
}

/**
* Returns {@link Role} with the specified constant name.
*
* @param name role name
* @return matching {@code Role}
* @throws IllegalArgumentException When no matching role is found
*/
public static Role fromName(String name) {
for (Role r : values()) {
if (r.name().equalsIgnoreCase(name)) {
return r;
}
}
throw new IllegalArgumentException("Unknown role '" + name + "'.");
}

/**
* Returns a {@link Role} with the specified IRI or constant name.
* <p>
* This function first tries to find the enum constant by IRI. If it is not found, constant name matching is
* attempted.
*
* @param identification Constant IRI or name to find match by
* @return matching {@code Role}
* @throws IllegalArgumentException When no matching role is found
*/
public static Role fromIriOrName(String identification) {
try {
return fromIri(identification);
} catch (IllegalArgumentException e) {
return fromName(identification);
}
}
}
59 changes: 59 additions & 0 deletions src/main/java/cz/cvut/kbss/study/model/RoleGroup.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package cz.cvut.kbss.study.model;
import cz.cvut.kbss.jopa.model.annotations.*;
import cz.cvut.kbss.study.model.util.HasDerivableUri;
import cz.cvut.kbss.study.model.util.HasOwlKey;
import cz.cvut.kbss.study.util.Constants;

import java.net.URI;
import java.util.HashSet;
import java.util.Set;

@OWLClass(iri = Vocabulary.s_c_role_group)
public class RoleGroup implements HasDerivableUri {

@Id
private URI uri;

@OWLAnnotationProperty(iri = Vocabulary.s_p_label)
private String name;

@OWLObjectProperty(iri = Vocabulary.s_p_has_role)
private Set<Role> roles;

public void addRole(Role role){
if(roles == null){
roles = new HashSet<>();
}
roles.add(role);
}


public URI getUri() {
return uri;
}

public void setUri(URI uri) {
this.uri = uri;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public Set<Role> getRoles() {
return roles;
}

public void setRoles(Set<Role> roles) {
this.roles = roles;
}

@Override
public void generateUri() {
this.uri = URI.create(Constants.BASE_URI + name);
}
}
27 changes: 26 additions & 1 deletion src/main/java/cz/cvut/kbss/study/model/User.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import cz.cvut.kbss.study.model.util.HasDerivableUri;
import cz.cvut.kbss.study.util.Constants;
import cz.cvut.kbss.study.util.IdentificationUtils;
import cz.cvut.kbss.study.util.RoleAssignmentUtil;
import org.springframework.security.crypto.password.PasswordEncoder;

import java.io.Serializable;
Expand Down Expand Up @@ -61,14 +62,37 @@ public class User implements HasDerivableUri, Serializable {
@OWLObjectProperty(iri = Vocabulary.s_p_is_member_of, fetch = FetchType.EAGER)
private Institution institution;

@OWLDataProperty(iri = Vocabulary.s_p_has_role_group)
private String roleGroup;

@OWLObjectProperty(iri = Vocabulary.s_p_has_role_group)
private RoleGroup rg;

@Types
private Set<String> types;

public String getRoleGroup() {
return roleGroup;
}

public void setRoleGroup(String roleGroup) {
this.roleGroup = roleGroup;
this.types.clear();
this.types = RoleAssignmentUtil.assignRolesForGroup(this.roleGroup);
}

public User() {
this.types = new HashSet<>();
types.add(Vocabulary.s_c_doctor);
}

public void setRg(RoleGroup rg) {
this.rg = rg;
}
public RoleGroup getRg() {
return rg;
}

@Override
public URI getUri() {
return uri;
Expand Down Expand Up @@ -216,7 +240,8 @@ public User copy() {
copy.setInstitution(institution);
copy.setIsInvited(isInvited);
copy.setToken(token);
types.forEach(copy::addType);
copy.setRoleGroup(roleGroup);
copy.setRg(rg);
return copy;
}

Expand Down
31 changes: 31 additions & 0 deletions src/main/java/cz/cvut/kbss/study/persistence/dao/RoleGroupDao.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package cz.cvut.kbss.study.persistence.dao;

import cz.cvut.kbss.jopa.exceptions.NoResultException;
import cz.cvut.kbss.jopa.model.EntityManager;
import cz.cvut.kbss.study.model.RoleGroup;
import cz.cvut.kbss.study.util.Constants;
import org.springframework.stereotype.Repository;
import java.net.URI;
import cz.cvut.kbss.study.model.Vocabulary;

@Repository
public class RoleGroupDao extends DerivableUriDao<RoleGroup> {

protected RoleGroupDao(EntityManager em) {
super(RoleGroup.class, em);
}

public RoleGroup findByName(String name) {
if (name == null) {
return null;
}
try {
return em.createNativeQuery("SELECT ?x WHERE { ?x ?hasName ?name . }", RoleGroup.class)
.setParameter("hasName", URI.create(Vocabulary.s_p_label))
.setParameter("name", name, Constants.PU_LANGUAGE).getSingleResult();
} catch (NoResultException e) {
return null;
}
}

}
15 changes: 14 additions & 1 deletion src/main/java/cz/cvut/kbss/study/security/SecurityConstants.java
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,19 @@ private SecurityConstants() {
public static final int SESSION_TIMEOUT = 12 * 60 * 60;

public static final String ROLE_USER = "ROLE_USER";

public static final String ROLE_ADMIN = "ROLE_ADMIN";

public static final String ROLE_COMPLETE_RECORDS = "rm_complete_records";
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need those constants? Why Role enum is not enough for this case? (same way as we discussed in frontend)

public static final String ROLE_DELETE_ALL_RECORDS = "rm_delete_all_records";
public static final String ROLE_DELETE_ORGANIZATION_RECORDS = "rm_delete_organization_records";
public static final String ROLE_EDIT_ALL_RECORDS = "rm_edit_all_records";
public static final String ROLE_EDIT_ORGANIZATION_RECORDS = "rm_edit_organization_records";
public static final String ROLE_EDIT_USERS = "rm_edit_users";
public static final String ROLE_IMPORT_CODELISTS = "rm_import_codelists";
public static final String ROLE_PUBLISH_RECORDS = "rm_publish_records";
public static final String ROLE_REJECT_RECORDS = "rm_reject_records";
public static final String ROLE_VIEW_ALL_RECORDS = "rm_view_all_records";
public static final String ROLE_VIEW_ORGANIZATION_RECORDS = "rm_view_organization_records";


}
13 changes: 12 additions & 1 deletion src/main/java/cz/cvut/kbss/study/security/model/Role.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,18 @@

public enum Role {
USER(SecurityConstants.ROLE_USER, Vocabulary.s_c_doctor),
ADMIN(SecurityConstants.ROLE_ADMIN, Vocabulary.s_c_administrator);
ADMIN(SecurityConstants.ROLE_ADMIN, Vocabulary.s_c_administrator),
COMPLETE_RECORDS(SecurityConstants.ROLE_COMPLETE_RECORDS, Vocabulary.s_i_complete_records_role),
DELETE_ALL_RECORDS(SecurityConstants.ROLE_DELETE_ALL_RECORDS, Vocabulary.s_i_delete_all_records_role),
DELETE_ORGANIZATIONS_RECORDS(SecurityConstants.ROLE_DELETE_ORGANIZATION_RECORDS, Vocabulary.s_i_delete_organization_records_role),
EDIT_ALL_RECORDS(SecurityConstants.ROLE_EDIT_ALL_RECORDS, Vocabulary.s_i_edit_all_records_role),
EDIT_ORGANIZATIONS_RECORDS(SecurityConstants.ROLE_EDIT_ORGANIZATION_RECORDS, Vocabulary.s_i_edit_organization_records_role),
EDIT_USERS(SecurityConstants.ROLE_EDIT_USERS, Vocabulary.s_i_edit_users_role),
IMPORT_CODELISTS(SecurityConstants.ROLE_IMPORT_CODELISTS, Vocabulary.s_i_import_codelists_role),
PUBLISH_RECORDS(SecurityConstants.ROLE_PUBLISH_RECORDS, Vocabulary.s_i_publish_records_role),
REJECT_RECORDS(SecurityConstants.ROLE_REJECT_RECORDS, Vocabulary.s_i_reject_records_role),
VIEW_ALL_RECORDS(SecurityConstants.ROLE_VIEW_ALL_RECORDS, Vocabulary.s_i_view_all_records_role),
VIEW_ORGANIZATIONS_RECORDS(SecurityConstants.ROLE_VIEW_ORGANIZATION_RECORDS, Vocabulary.s_i_view_organization_records_role);

private final String name;
private final String type;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package cz.cvut.kbss.study.service;

import cz.cvut.kbss.study.model.RoleGroup;
import org.springframework.stereotype.Service;

public interface RoleGroupService {
RoleGroup findByName(String name);
}
12 changes: 11 additions & 1 deletion src/main/java/cz/cvut/kbss/study/service/SystemInitializer.java
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
package cz.cvut.kbss.study.service;

import cz.cvut.kbss.study.model.Institution;
import cz.cvut.kbss.study.model.RoleGroup;
import cz.cvut.kbss.study.model.User;
import cz.cvut.kbss.study.model.Vocabulary;
import cz.cvut.kbss.study.util.Constants;
import jakarta.annotation.PostConstruct;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.stereotype.Service;

import java.net.URI;

@ConditionalOnProperty(prefix = "security", name = "provider", havingValue = "internal", matchIfMissing = true)
@Service
public class SystemInitializer {
Expand All @@ -17,15 +21,19 @@ public class SystemInitializer {

private static final String ADMIN_USERNAME = "admin";
private static final String INSTITUTION_NAME = "admin_institution";
private static final String ROLE_GROUP_NAME = "admin-role-group";

private final UserService userService;

private final InstitutionService institutionService;

private final RoleGroupService roleGroupService;

public SystemInitializer(UserService userService,
InstitutionService institutionService) {
InstitutionService institutionService, RoleGroupService roleGroupService) {
this.userService = userService;
this.institutionService = institutionService;
this.roleGroupService = roleGroupService;
}

@PostConstruct
Expand All @@ -52,7 +60,9 @@ private void addDefaultAdministrator() {
admin.setPassword("5y5t3mAdm1n.");
admin.setInstitution(institutionService.findByName(INSTITUTION_NAME));
admin.setIsInvited(true);
admin.setRoleGroup(Constants.OPERATOR_ADMIN);
admin.getTypes().add(Vocabulary.s_c_administrator);
admin.setRg(roleGroupService.findByName(ROLE_GROUP_NAME));
LOG.debug("Persisting default administrator {}", admin);
userService.persist(admin);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package cz.cvut.kbss.study.service.repository;

import cz.cvut.kbss.study.model.RoleGroup;
import cz.cvut.kbss.study.persistence.dao.RoleGroupDao;
import cz.cvut.kbss.study.service.RoleGroupService;
import org.springframework.stereotype.Service;

@Service
public class RepositoryRoleGroup implements RoleGroupService {

private final RoleGroupDao roleGroupDao;

public RepositoryRoleGroup(RoleGroupDao roleGroupDao) {
this.roleGroupDao = roleGroupDao;
}

@Override
public RoleGroup findByName(String name) {
return roleGroupDao.findByName(name);
}
}
6 changes: 6 additions & 0 deletions src/main/java/cz/cvut/kbss/study/util/Constants.java
Original file line number Diff line number Diff line change
Expand Up @@ -68,4 +68,10 @@ private Constants() {
* Excel MIME type
*/
public static final String MEDIA_TYPE_EXCEL = "application/vnd.ms-excel";

public static final String OPERATOR_ADMIN = "OPERATOR_ADMIN";
public static final String OPERATOR_USER = "OPERATOR_USER";
public static final String SUPPLIER_ADMIN = "SUPPLIER_ADMIN";
public static final String SUPPLIER_USER = "SUPPLIER_USER";
public static final String EXTERNAL_USER = "EXTERNAL_USER";
}
Loading
Loading