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

#4941 - Mapping OIDC groups to INCEpTION's internal roles #4982

Open
wants to merge 15 commits into
base: main
Choose a base branch
from

Conversation

kzgrzendek
Copy link

What's in the PR
This PR contains the feature allowing to map OAuth2 groups with INCEpTION roles, as described in the corresopnding feaure request

How to test manually

  1. Run a local Keycloak, with a dedicated user and 4 groups associated to that user :
  • INCEPTION_ADMIN
  • INCEPTION_USER
  • INCEPTION_PROJECT_CREATOR
  • INCEPTION_REMOTE
  1. Add the following lines to your settings.properties file, inside your INCEpTION home directory :
    # OAuth2 groups mapping settings
    oauth2-groups.enabled=true
    oauth2-groups.admin=/INCEPTION_ADMIN
    oauth2-groups.user=/INCEPTION_USER
    oauth2-groups.project-creator=/INCEPTION_PROJECT_CREATOR
    oauth2-groups.remote=/INCEPTION_REMOTE
    
  2. Build and run an INCEpTION instance from this branch
  3. Try to login via OAuth2, and observe that the newly created user is bind to all the INCEpTION roles, as mapped in the settings.properties file. You can play with the mapping/groups to test the behavior.

Note : :

  • The roles are mapped at the user's creation and at every connections
  • If the feature is enabled and the user isn't in any group mapped to INCEpTION roles, and AccessDeniedException will be throw.
  • The OAuth2AdaptaterImpl class was vefore calling to the PreAuthUtil class, whereas it's specified in the documentation that the External PreAuth feature isn't compatible with OAuth2 authentication. I made a dedicated OAuth2Utils class that contains a method that maps the OAuth2 groups if the mapping feature is enabled, or just adds the USER INCEpTION role to the user if the mapping feature is disabled.
  • Corresponding properties have been added with a native SpringBoot @Configuration annotation.

Automatic testing

  • The test class OAuth2UtilsTest contains a set of unit tests
  • PR includes unit tests

Documentation

  • I can do the documentation as well if the feature is validated.
  • PR updates documentation

I'm of course open to feedback and willing to modify the implementation if needed ;)

Copy link
Member

@reckart reckart left a comment

Choose a reason for hiding this comment

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

Thanks for the suggestion! I hope you find my feedback useful.


@Configuration
@ConfigurationProperties(prefix = "oauth2-groups")
public class OAuth2Utils {
Copy link
Member

Choose a reason for hiding this comment

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

Utils classes in INCEpTION are typically final classes containing only static methods. Configuration classes are called XxxPropertiesImpl, e.g. KnowledgeBasePropertiesImpl and come with a respective interface, e.g. KnowledgeBaseProperties. This class/interface separation is necessary in cases where the properties are injected into Wicket code.

Copy link
Author

Choose a reason for hiding this comment

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

@reckart how would you recommend to proceed? Creating the interface and implementation class for the properties, injecting the PropertiesImpl class in OAuth2AdapterImpl's constructor and moving the mapping logic in a private method of that class?

@ConfigurationProperties(prefix = "oauth2-groups")
public class OAuth2Utils {

private static boolean OAUTH2_GROUPS_ENABLED;
Copy link
Member

Choose a reason for hiding this comment

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

Fields should be non-static and use camelCase.

}


public static Set<Role> getOAuth2UserRoles(User aUser, ArrayList<String> oauth2groups)
Copy link
Member

Choose a reason for hiding this comment

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

Arguments should use interfaces for collections. e.g. List instead of ArrayList.

public static Set<Role> getOAuth2UserRoles(User aUser, ArrayList<String> oauth2groups)
throws AccessDeniedException
{
Set<Role> roles = new HashSet<>();
Copy link
Member

Choose a reason for hiding this comment

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

Use var when possible.

+ aUser.getUsername() + "] doesn't have any groups, or the corresponding claim is empty");
}

oauth2groups.forEach(group -> matchOauth2groupToRole(group, roles));
Copy link
Member

Choose a reason for hiding this comment

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

Functional programming (without side-effects) would be easier to understand. Can we use map instead of forEach here?


Set<Role> userRoles = OAuth2Utils.getOAuth2UserRoles(testUser, userOAuth2Groups);

assertTrue(userRoles.contains(Role.ROLE_USER));
Copy link
Member

Choose a reason for hiding this comment

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

Please use AssertJ asserts instead of Unit asserts.


ArrayList<String> userOAuth2Groups = new ArrayList<>();

try {
Copy link
Member

Choose a reason for hiding this comment

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

assertThatExceptionOfType(...).isThrownBy(() -> { ... })


@ExtendWith(SpringExtension.class)
@ContextConfiguration(classes = OAuth2Utils.class)
@DirtiesContext(classMode = ClassMode.AFTER_CLASS)
Copy link
Member

Choose a reason for hiding this comment

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

Instead of making this a spring integration test, I'd tend towards it being a plain junit test that sets up the necessary beans as POJOs. It seems a bit of overkill to start up an entire spring context for this.

import de.tudarmstadt.ukp.clarin.webanno.security.model.User;

@Configuration
@ConfigurationProperties(prefix = "oauth2-groups")
Copy link
Member

Choose a reason for hiding this comment

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

security.oauth2.roles...? Other INCEpTION security properties live under security.... Mind, INCEpTION has no groups, only roles. It is incidental that you call these labels groups on your side. So from the INCEpTION perspective, we are mapping roles, not groups.

security.oauth.roles.enabled=true
security.oauth.roles.claim=groups
security.oauth.roles.mapping.ROLE_USER=ad-user
security.oauth.roles.mapping.ROLE_ADMIN=ad-admin
security.oauth.roles.mapping.ROLE_PROJECT_CREATOR=ad-project-creator
security.oauth.roles.mapping....=...

@@ -148,6 +147,10 @@ private User fetchOrMaterializeUser(OAuth2UserRequest userRequest, OAuth2User us
if (u != null) {
denyAccessToDeactivatedUsers(u);
denyAccessOfRealmsDoNotMatch(realm, u);

u.setRoles(OAuth2Utils.getOAuth2UserRoles(u, user.getAttribute("groups")));
Copy link
Member

Choose a reason for hiding this comment

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

The claim should be configurable via the properties as well since this is not a well-defined name. Others might e.g. use a roles claim instead.

@reckart reckart changed the title Feat/map oauth2 groups to roles #4941 - Mapping OIDC groups to INCEpTION's internal roles Aug 7, 2024
@reckart
Copy link
Member

reckart commented Aug 7, 2024

Btw. the commit message convention for INCEpTION goes like this:

#ISSUE-NUMBER - Title of the issue

- change 1
- change 2
- change 3

@reckart reckart added the ⭐️ Enhancement New feature or request label Aug 7, 2024
@reckart reckart added this to the 34.0 milestone Aug 7, 2024
@reckart reckart modified the milestones: 34.0, 35.0 Sep 7, 2024
@reckart
Copy link
Member

reckart commented Sep 7, 2024

The PR build fails but I have no idea why. I have opened a support discussion: https://github.com/orgs/community/discussions/138018

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
⭐️ Enhancement New feature or request
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants