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

Migration to role group #71

Open
wants to merge 17 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
314d448
[DOES NOT WORK] Migrate to role groups
blcham Sep 19, 2024
703e734
[kbss-cvut/record-manager-ui#202] Fix tests
palagdan Sep 20, 2024
fc12427
[kbss-cvut/record-manager-ui#202] Replace types with roles
palagdan Sep 23, 2024
152c841
[kbss-cvut/record-manager-ui#202] Replace hasRole with hasAuthorities
palagdan Sep 25, 2024
2fc603b
[kbss-cvut/record-manager-ui#202] Remove Security Role
palagdan Sep 25, 2024
0e3bc34
[kbss-cvut/record-manager-ui#202] Remove types from the user
palagdan Sep 25, 2024
2752833
[kbss-cvut/record-manager-ui#202] Implement RoleGroup controller
palagdan Sep 25, 2024
fea4ef1
[kbss-cvut/record-manager-ui#202] Refactor roles names
palagdan Sep 26, 2024
4d53a14
[kbss-cvut/record-manager-ui#202] Rename security constants
palagdan Sep 30, 2024
d5b29ca
[kbss-cvut/record-manager-ui#202] Fix RoleGroupController path
palagdan Sep 30, 2024
d52b1de
[kbss-cvut/record-manager-ui#202] FromIri and FromName dont' throw an…
palagdan Oct 1, 2024
fb316d7
[kbss-cvut/record-manager-ui#202] Fix the bug while loading all users
palagdan Oct 1, 2024
4a87a5b
[kbss-cvut/record-manager-ui#202] Add method to get current user with…
blcham Nov 25, 2024
c4ee42a
[kbss-cvut/record-manager-ui#202] Fix user.getRoleGroup() null
palagdan Dec 6, 2024
8a3c607
[kbss-cvut/record-manager-ui#202] Fix RoleGroup persistence context
palagdan Dec 6, 2024
fad3222
[kbss-cvut/record-manager-ui#202] Fix role name in resoleRoles
palagdan Dec 11, 2024
9c56d49
[kbss-cvut/record-manager-ui#202] Fix the access control to allow ROL…
palagdan Dec 11, 2024
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
3 changes: 2 additions & 1 deletion src/main/java/cz/cvut/kbss/study/config/SecurityConfig.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package cz.cvut.kbss.study.config;

import cz.cvut.kbss.study.exception.RecordManagerException;
import cz.cvut.kbss.study.model.Role;
import cz.cvut.kbss.study.security.CsrfHeaderFilter;
import cz.cvut.kbss.study.security.CustomSwitchUserFilter;
import cz.cvut.kbss.study.security.SecurityConstants;
Expand Down Expand Up @@ -79,7 +80,7 @@ public SecurityFilterChain filterChain(HttpSecurity http, ConfigReader config,
LOG.debug("Using internal security mechanisms.");
final AuthenticationManager authManager = buildAuthenticationManager(http);
http.authorizeHttpRequests(
(auth) -> auth.requestMatchers("/rest/users/impersonate").hasAuthority(SecurityConstants.ROLE_ADMIN)
(auth) -> auth.requestMatchers("/rest/users/impersonate").hasAuthority(Role.administrator.getRoleName())
.anyRequest().permitAll())
.cors((auth) -> auth.configurationSource(corsConfigurationSource(config)))
.csrf(AbstractHttpConfigurer::disable)
Expand Down
128 changes: 128 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,128 @@
package cz.cvut.kbss.study.model;

import com.fasterxml.jackson.annotation.JsonValue;
import cz.cvut.kbss.jopa.model.annotations.Individual;
import cz.cvut.kbss.study.security.SecurityConstants;
import java.util.Optional;

public enum Role {

// TODO deprecated -- should be removed.
@Individual(iri = Vocabulary.s_i_RM_ADMIN)
administrator(SecurityConstants.ROLE_ADMIN, Vocabulary.s_i_RM_ADMIN),
// TODO deprecated -- should be removed.
@Individual(iri = Vocabulary.s_i_RM_USER)
user(SecurityConstants.ROLE_USER, Vocabulary.s_i_RM_USER),

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

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

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

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

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

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

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

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

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

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

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

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

private final String iri;

public final String roleName;

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

@JsonValue
public String getRoleName() {
return roleName;
}

public String getIri() {
return iri;
}

/**
* Retrieves a role based on its IRI.
*
* <p>This method iterates over all available roles and checks if any role's IRI
* matches the provided IRI string. If a match is found, the corresponding role
* is returned as an Optional. If no match is found, an empty Optional is returned.</p>
*
* @param iri the IRI of the role to retrieve
* @return an Optional containing the corresponding Role if found,
* or an empty Optional if no matching role exists
*/
public static Optional<Role> fromIri(String iri) {
for (Role r : values()) {
if (r.getIri().equals(iri)) {
return Optional.of(r);
}
}
return Optional.empty();
}

/**
* Retrieves a role based on its role name.
*
* <p>This method iterates over all available roles and checks if any role's
* name matches the provided name string (case-insensitive). If a match is found,
* the corresponding role is returned as an Optional. If no match is found,
* an empty Optional is returned.</p>
*
* @param name the name of the role to retrieve
* @return an Optional containing the corresponding Role if found,
* or an empty Optional if no matching role exists
*/
public static Optional<Role> fromName(String name) {
for (Role r : values()) {
if (r.roleName.equalsIgnoreCase(name)) {
return Optional.of(r);
}
}
return Optional.empty();
}

/**
* Retrieves a role based on either its IRI or role name.
*
* <p>This method first attempts to find a role using the provided identification string
* as an IRI. If no role is found, it then attempts to find a role using the
* identification string as a role name. The first successful match will be returned
* as an Optional. If no match is found, an empty Optional is returned.</p>
*
* @param identification the IRI or role name of the role to retrieve
* @return an Optional containing the corresponding Role if found,
* or an empty Optional if no matching role exists
*/
public static Optional<Role> fromIriOrName(String identification) {
Optional<Role> role = fromIri(identification);
return role.isPresent() ? role : fromName(identification);
}
}
71 changes: 71 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,71 @@
package cz.cvut.kbss.study.model;
import cz.cvut.kbss.jopa.model.annotations.*;
import cz.cvut.kbss.study.model.util.HasOwlKey;
import cz.cvut.kbss.study.model.util.HasUri;
import cz.cvut.kbss.study.util.Constants;

import java.io.Serializable;
import java.net.URI;
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;

@OWLClass(iri = Vocabulary.s_c_role_group)
public class RoleGroup implements Serializable, HasUri {

@Id
private URI uri;

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

@Enumerated(EnumType.OBJECT_ONE_OF)
@OWLObjectProperty(iri = Vocabulary.s_p_has_role)
private Set<Role> roles = new HashSet<>();

public void addRole(Role role){
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;
}

public void generateUri() {
this.uri = URI.create(Constants.BASE_URI + name);
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
RoleGroup roleGroup = (RoleGroup) o;
return Objects.equals(name, roleGroup.name);
}

@Override
public int hashCode() {
return Objects.hash(name);
}
}
29 changes: 9 additions & 20 deletions src/main/java/cz/cvut/kbss/study/model/User.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,16 @@
import cz.cvut.kbss.jopa.model.annotations.OWLDataProperty;
import cz.cvut.kbss.jopa.model.annotations.OWLObjectProperty;
import cz.cvut.kbss.jopa.model.annotations.ParticipationConstraints;
import cz.cvut.kbss.jopa.model.annotations.Types;
import cz.cvut.kbss.study.model.util.HasDerivableUri;
import cz.cvut.kbss.study.util.Constants;
import cz.cvut.kbss.study.util.IdentificationUtils;
import org.springframework.security.crypto.password.PasswordEncoder;

import java.io.Serializable;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;

@OWLClass(iri = Vocabulary.s_c_Person)
public class User implements HasDerivableUri, Serializable {
Expand Down Expand Up @@ -61,12 +57,11 @@ public class User implements HasDerivableUri, Serializable {
@OWLObjectProperty(iri = Vocabulary.s_p_is_member_of, fetch = FetchType.EAGER)
private Institution institution;

@Types
private Set<String> types;
@OWLObjectProperty(iri = Vocabulary.s_p_has_role_group, fetch = FetchType.EAGER)
private RoleGroup roleGroup;

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

}

@Override
Expand Down Expand Up @@ -137,17 +132,12 @@ public void setInstitution(Institution institution) {
this.institution = institution;
}

public Set<String> getTypes() {
return types;
}

public void setTypes(Set<String> types) {
this.types = types;
public RoleGroup getRoleGroup() {
return roleGroup;
}

public void addType(String type) {
assert types != null;
getTypes().add(type);
public void setRoleGroup(RoleGroup roleGroup) {
this.roleGroup = roleGroup;
}

/**
Expand All @@ -158,8 +148,7 @@ public void addType(String type) {
* @return {@code true} if this is admin, {@code false} otherwise
*/
public boolean isAdmin() {
assert types != null;
return getTypes().contains(Vocabulary.s_c_administrator);
return roleGroup != null && roleGroup.getRoles().contains(Role.administrator);
}

public String getToken() {
Expand Down Expand Up @@ -216,7 +205,7 @@ public User copy() {
copy.setInstitution(institution);
copy.setIsInvited(isInvited);
copy.setToken(token);
types.forEach(copy::addType);
copy.setRoleGroup(roleGroup);
return copy;
}

Expand Down
44 changes: 44 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,44 @@
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.exception.PersistenceException;
import cz.cvut.kbss.study.model.RoleGroup;
import cz.cvut.kbss.study.util.Constants;
import java.util.Objects;
import org.springframework.stereotype.Repository;
import java.net.URI;
import cz.cvut.kbss.study.model.Vocabulary;

@Repository
public class RoleGroupDao extends BaseDao<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;
}
}

@Override
public void persist(RoleGroup entity) {
Objects.requireNonNull(entity);
try {
em.persist(entity);
} catch (RuntimeException e) {
LOG.error("Error when persisting entity.", e);
throw new PersistenceException(e);
}
}

}
8 changes: 5 additions & 3 deletions src/main/java/cz/cvut/kbss/study/persistence/dao/UserDao.java
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,11 @@ public List<User> findByInstitution(Institution institution) {

public int getNumberOfInvestigators() {
return ((BigInteger) em.createNativeQuery(
"SELECT (count(?p) as ?investigatorCount) WHERE { ?p a ?typeDoctor . MINUS {?p a ?typeAdmin}}")
.setParameter("typeDoctor", URI.create(Vocabulary.s_c_doctor))
.setParameter("typeAdmin", URI.create(Vocabulary.s_c_administrator)).getSingleResult()
"SELECT (count(?p) as ?investigatorCount) WHERE { ?p a ?typeUser . MINUS {?p ?hasRoleGroup ?roleGroup . ?roleGroup ?hasRole ?typeAdmin}}")
blcham marked this conversation as resolved.
Show resolved Hide resolved
.setParameter("typeUser", URI.create(Vocabulary.s_c_Person))
.setParameter("hasRoleGroup", URI.create(Vocabulary.s_p_has_role_group))
.setParameter("hasRole", URI.create(Vocabulary.s_p_has_role))
.setParameter("typeAdmin", URI.create(Vocabulary.s_i_RM_ADMIN)).getSingleResult()
).intValue();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public void create(@RequestBody ActionHistory actionHistory) {
}
}

@PreAuthorize("hasRole('" + SecurityConstants.ROLE_ADMIN + "')")
@PreAuthorize("hasAuthority('" + SecurityConstants.ROLE_ADMIN + "')")
@GetMapping(produces = MediaType.APPLICATION_JSON_VALUE)
public List<ActionHistory> getActions(@RequestParam(value = "author", required = false) String authorUsername,
@RequestParam(value = "type", required = false) String type,
Expand All @@ -73,7 +73,7 @@ public List<ActionHistory> getActions(@RequestParam(value = "author", required =
return result.getContent();
}

@PreAuthorize("hasRole('" + SecurityConstants.ROLE_ADMIN + "')")
@PreAuthorize("hasAuthority('" + SecurityConstants.ROLE_ADMIN + "')")
@GetMapping(value = "/{key}", produces = MediaType.APPLICATION_JSON_VALUE)
public ActionHistory getByKey(@PathVariable("key") String key) {
final ActionHistory action = actionHistoryService.findByKey(key);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import org.springframework.web.bind.annotation.*;

@RestController
@PreAuthorize("hasRole('" + SecurityConstants.ROLE_USER + "')")
@PreAuthorize("hasAuthority('" + SecurityConstants.ROLE_USER + "')")
@RequestMapping("/formGen")
public class FormGenController extends BaseController {

Expand Down
Loading
Loading