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

Migration to role group #71

wants to merge 17 commits into from

Conversation

palagdan
Copy link
Collaborator

Resolves partially kbss-cvut/record-manager-ui#202

@palagdan
Copy link
Collaborator Author

2024-09-25_14-40

It should be working as expected. Now I just need to fix the frontend based on the new implementation of the backend.

@palagdan palagdan force-pushed the migration-to-role-group branch from 748fb42 to 65fd7fb Compare September 25, 2024 14:01
Copy link

@blcham blcham left a comment

Choose a reason for hiding this comment

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

Some of the comments i need to explain, som lets have call about it.

src/main/java/cz/cvut/kbss/study/model/RoleGroup.java Outdated Show resolved Hide resolved
if (!currentUser.getTypes().contains(Vocabulary.s_c_administrator)
&& (!instance.getTypes().equals(currentUser.getTypes()) || (instance.getInstitution() != null
if (!currentUser.isAdmin()
&& (!instance.getRoleGroup().getRoles().equals(currentUser.getRoleGroup().getRoles()) || (instance.getInstitution() != null
Copy link

@blcham blcham Sep 26, 2024

Choose a reason for hiding this comment

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

Wow, not sure if I understand role of this condition now or before, but I am little worried that we need compare roleGroups here instead. Otherwise we would persist different roleGroups in internal-auth profile.

Copy link

Choose a reason for hiding this comment

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

? (it seems to me that this code is used for both keycloak-auth and internal-auth)

Copy link

Choose a reason for hiding this comment

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

Copy link

Choose a reason for hiding this comment

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

@palagdan please answer this

Copy link
Collaborator Author

@palagdan palagdan Dec 11, 2024

Choose a reason for hiding this comment

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

The condition is true when:

  • The user is not an admin, and
  • Either the roles of the instance and the currentUser are different, or:
  • The instance has an institution, and its key is different from the currentUser's institution key.

Why can't we just compare by URI, in case the user has the ROLE_USER role and tries to update another user? This condition seems too complicated.

Yes, it is used for both profiles.

src/main/resources/model.ttl Outdated Show resolved Hide resolved
src/main/resources/model.ttl Outdated Show resolved Hide resolved
@palagdan palagdan force-pushed the migration-to-role-group branch 4 times, most recently from 6beabca to 3d722f7 Compare September 26, 2024 16:08
@palagdan
Copy link
Collaborator Author

@blcham
2024-09-26_18-07
If it is okay for current iteration, I can continue with frontend.

@palagdan palagdan force-pushed the migration-to-role-group branch from 3d722f7 to f736a00 Compare September 26, 2024 16:20
@palagdan palagdan force-pushed the migration-to-role-group branch 3 times, most recently from 0f248d0 to 5d9f9aa Compare September 30, 2024 14:41
@palagdan
Copy link
Collaborator Author

palagdan commented Oct 2, 2024

@blcham

While implementing the logic to support the Keycloak profile, I encountered a problem. When the application extracts roles from the Keycloak claims, it creates a new instance of RoleGroup and adds these roles to this empty group. The issue is that this group does not exist in the database, and a cascade strategy for this role group is not provided because we don’t need it. As a result, when the current user updates or creates something, this empty RoleGroup with the roles is in the transactional context, but the database does not know how to manage it since this RoleGroup does not exist and no cascade strategy is provided, which leads to an error.

I have one idea on how to solve this: retrieve the RoleGroup from the token claim(not roles), attempt to find it in the database, and then add it to the user.

What do you think? Do you have any other ideas?

@blcham
Copy link

blcham commented Oct 2, 2024

Not sure if I understand. Isn't it possible just to ignore persistence of RoleGroup in keycloak profile? E.g. in preUpdate method?

@palagdan
Copy link
Collaborator Author

palagdan commented Oct 3, 2024

@blcham
I am not sure how to ignore the persistence.
I thought that preUpdate method is only for validation.

@blcham blcham force-pushed the migration-to-role-group branch 2 times, most recently from 631a0b3 to 349001c Compare October 9, 2024 10:54
@blcham blcham force-pushed the migration-to-role-group branch 3 times, most recently from 467b0f7 to d23dead Compare November 13, 2024 17:01
@blcham
Copy link

blcham commented Nov 14, 2024

@palagdan so i guess to simulate the error i can change the institution of the user and then save right?

@blcham
Copy link

blcham commented Nov 25, 2024

@palagdan I added method to get user without security context

@palagdan palagdan force-pushed the migration-to-role-group branch from e599913 to 8e21f6d Compare December 6, 2024 10:56
@palagdan palagdan requested a review from blcham December 6, 2024 16:20
@kbss-cvut kbss-cvut deleted a comment from palagdan Dec 6, 2024
@blcham blcham force-pushed the migration-to-role-group branch from 8e7d3ec to cd47a66 Compare December 10, 2024 20:50
Copy link

@blcham blcham left a comment

Choose a reason for hiding this comment

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

@palagdan see my comments.

Please carefully go over each line containing Generator.generateUser(institutionAnother, this.roleGroupAdmin). I believe we do not want to have them there as they just might introduce condition in test that will not fail due to this restriction. Or did you do that because in those tests you do not want to test security related issues and you will replace this.roleGroupAdmin with something that will have all permissions?

Comment on lines 59 to 60
"hasAuthority('" + SecurityConstants.ROLE_ADMIN + "') " +
"or hasAuthority('" + SecurityConstants.ROLE_ADMIN + "') and @securityUtils.isMemberOfInstitution(#institutionKey)")
Copy link

Choose a reason for hiding this comment

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

Suggested change
"hasAuthority('" + SecurityConstants.ROLE_ADMIN + "') " +
"or hasAuthority('" + SecurityConstants.ROLE_ADMIN + "') and @securityUtils.isMemberOfInstitution(#institutionKey)")
"hasAuthority('" + SecurityConstants.ROLE_ADMIN + "') " +
"or hasAuthority('" + SecurityConstants.ROLE_USER + "') and @securityUtils.isMemberOfInstitution(#institutionKey)")

Copy link

Choose a reason for hiding this comment

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

I believed you changed it accidentally

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I changed it back.

this.securityUtils = securityUtils;
this.passwordEncoder = passwordEncoder;
this.userDao = userDao;
this.patientRecordDao = patientRecordDao;
this.email = email;
this.config = config;
this.em = em;
Copy link

Choose a reason for hiding this comment

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

This is bad practice to include em in service layer ... you need to call userDao instead !

Comment on lines 86 to 87
User currentUser = securityUtils.getCurrentUser();
em.detach(currentUser);
Copy link

Choose a reason for hiding this comment

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

I was explaining that we could implement it like that but it was rather to explain how it work ...

I was hoping you would not implement it this way. But rather include method securityUtils.getCurrentUserName(). What is the reason to not implement it like that?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@blcham
I tried implementing this method in SecurityUtils as you suggested, but the issue still occurred because, as you mentioned, we are retrieving the user within the same transaction. Only this solution resolved the problem

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done, I removed em from the service layer and used securityUtils, but I still have some questions about why it works now..

@Test
public void findByKeyReturnsActionWithPayload() {
Institution institution = Generator.generateInstitution();
User user = Generator.generateUser(institution);

User user = Generator.generateUser(institution, this.roleGroupAdmin);
Copy link

Choose a reason for hiding this comment

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

This is confusing. Why we now need roleGroupAdmin and it is not enough to have roleGroupUser? I would not elevate required permissions as we might get lost later when we will try to remove ADMINISTRATOR role. We would not understand why ADMIN role was needed here ....

Note that you are using it in many places within this file.

@Test
public void persistedInstanceHasGeneratedUri(){
final Institution institution = Generator.generateInstitution();
final User user = Generator.generateUser(institution);
final RoleGroup roleGroupAdmin = Generator.generateRoleGroupWithRoles(Role.administrator);
final User user = Generator.generateUser(institution, roleGroupAdmin);
Copy link

Choose a reason for hiding this comment

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

as explained in other comment we are elevating user's permission to ADMIN and i do not understand why. I suggest to use something like roleGroupUser instead.

@BeforeEach
public void setUp() {
super.setUp(controller);
Institution institution = Generator.generateInstitution();
User user = Generator.generateUser(institution);
this.roleGroupAdmin = Generator.generateRoleGroupWithRoles(Role.administrator);
User user = Generator.generateUser(institution, this.roleGroupAdmin);
Copy link

Choose a reason for hiding this comment

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

as explained in other comment we are elevating user's permission to ADMIN and i do not understand why. I suggest to use something like roleGroupUser instead.

public static final String USERNAME = "halsey";
public static final String PASSWORD = "john117";
public static final String EMAIL = "[email protected]";

@BeforeEach
public void setUp() throws Exception {
Institution institution = Generator.generateInstitution();
user = Generator.getUser(USERNAME, PASSWORD, "John", "Grant", EMAIL, institution);
this.roleGroupAdmin = Generator.generateRoleGroupWithRoles(Role.administrator);
Copy link

Choose a reason for hiding this comment

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

as explained in other comment we are elevating user's permission to ADMIN and i do not understand why. I suggest to use something like roleGroupUser instead.

@Test
public void loadUserByUsername() {
Institution institution = Generator.generateInstitution();
institutionService.persist(institution);

User user = Generator.generateUser(institution);
User user = Generator.generateUser(institution, this.roleGroupAdmin);
Copy link

Choose a reason for hiding this comment

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

as explained in other comment we are elevating user's permission to ADMIN and i do not understand why. I suggest to use something like roleGroupUser instead.

@@ -95,10 +97,10 @@ private List<PatientRecord> generateRecordsToImport(User originalAuthor) {

@Test
void importRecordsRetainsRecordProvenanceDataWhenCurrentUserIsAdmin() {
final User originalAuthor = Generator.generateUser(Generator.generateInstitution());
final User originalAuthor = Generator.generateUser(Generator.generateInstitution(), this.roleGroupAdmin);
Copy link

Choose a reason for hiding this comment

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

as explained in other comment we are elevating user's permission to ADMIN and i do not understand why. I suggest to use something like roleGroupUser instead.

@@ -58,14 +59,17 @@ public class SecurityUtilsTest {

private User user;

private RoleGroup roleGroupAdmin;
Copy link

Choose a reason for hiding this comment

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

as explained in other comment we are elevating user's permission to ADMIN and i do not understand why. I suggest to use something like roleGroupUser instead.

@palagdan palagdan force-pushed the migration-to-role-group branch from 62e97c3 to 9c56d49 Compare December 11, 2024 11:36
@palagdan
Copy link
Collaborator Author

palagdan commented Dec 11, 2024

@blcham

I investigated the potential problem you described. I reviewed several classes across different layers (controllers, services, and DAOs) and tested using a user with only the "user" role. All tests produced the same results.

The issue is that we don't test the @PreAuthorize methods in all cases, so the roles assigned to the user in these tests do not affect the outcome.

For reference, see: How do I unit test Spring Security PreAuthorize/HasRole.

I’ll work on understanding how to implement tests that also validate roles.

In the example below, I show that the method getAllInstitutions has the following restriction: @PreAuthorize("hasAuthority('" + SecurityConstants.ROLE_ADMIN + "')")
However, it works in tests even if the user has the role "User." and authentication has authorities "User"

test_roles

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants