Skip to content

Commit

Permalink
Make LTI "Custom role" configuration more configurable (opencast#5312)
Browse files Browse the repository at this point in the history
Hi, this is my first contribution to this repository.

* The problem:
Current implementation of custom roles allows only one custom role also
it only accepts string that are exactly equal to role name which can
limit configurability.

This pull request implements ability to define multiple custom roles and
custom roles that need to be added for example:
```
lti.custom_role_name.1=Instructor
lti.custom_roles.1=ROLE_MOODLE_INSTRUCTOR

lti.custom_role_name.2=Learner
lti.custom_roles.2=ROLE_MOODLE_LEARNER

-- Additionally theres regex support for matching roles --
lti.custom_role_name.3=ims\/lis\/Administrator
lti.custom_roles.3=ROLE_MOODLE_ADMINISTRATOR
```

### Your pull request should…

* [x] have a concise title
* [x] [close an accompanying
issue](https://help.github.com/en/articles/closing-issues-using-keywords)
if one exists
* [x] [be against the correct
branch](https://docs.opencast.org/develop/developer/development-process#acceptance-criteria-for-patches-in-different-versions)
* [x] include migration scripts and documentation, if appropriate
* [x] pass automated tests
* [x] have a clean commit history
* [x] [have proper commit messages (title and body) for all
commits](https://medium.com/@steveamaza/e028865e5791)
  • Loading branch information
Arnei authored Jan 26, 2024
2 parents 9afc484 + 26659e1 commit cdbebed
Show file tree
Hide file tree
Showing 3 changed files with 53 additions and 15 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
With this release you can configure multiple LTI Custom roles, identifying each of them with a key index for example lti.custom_role_name.1=... lti.custom_roles.1=..., lti.custom_role_name.2=... lti.custom_roles.2=... etc.
Additionally, there's custom_role_name regex support for more flexible roles matching.
Legacy lti.custom_role_name=... and lti.custom_roles=... configuration keys are still supported.
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,16 @@
# lti.create_jpa_user_reference.roles = *

# Add Custom Roles to users who has the role with custom_role_name
# This configuration key is a list, to add additional custom roles increment the lti.custom_role_name.# number,
# the role will only be added if it has matching lti.custom_roles.# roles configuration
# It also has support for regex patterns for example 'ims\/lis\/.*' will match all roles that start with ims/list/
# Default: empty no custom roles

#lti.custom_role_name=Instructor
#This Role set is an example for a user which can open the editor for an event and upload videos via opencast studio.
#lti.custom_roles=ROLE_ADMIN_UI,ROLE_API_EVENTS_METADATA_DELETE,ROLE_API_EVENTS_METADATA_EDIT,ROLE_API_EVENTS_METADATA_VIEW,ROLE_UI_EVENTS_DETAILS_COMMENTS_CREATE,ROLE_UI_EVENTS_DETAILS_COMMENTS_DELETE,ROLE_UI_EVENTS_DETAILS_COMMENTS_EDIT,ROLE_UI_EVENTS_DETAILS_COMMENTS_REPLY,ROLE_UI_EVENTS_DETAILS_COMMENTS_RESOLVE,ROLE_UI_EVENTS_DETAILS_COMMENTS_VIEW,ROLE_UI_EVENTS_EDITOR_EDIT,ROLE_UI_EVENTS_EDITOR_VIEW,ROLE_STUDIO
# lti.custom_role_name.1=Instructor

# The lti.custom_roles.# configuration key must have matching lti.custom_role_name.# key.
# This Role set is an example for a user which can open the editor for an event and upload videos via opencast studio.
# lti.custom_roles.1=ROLE_ADMIN_UI,ROLE_API_EVENTS_METADATA_DELETE,ROLE_API_EVENTS_METADATA_EDIT,ROLE_API_EVENTS_METADATA_VIEW,ROLE_UI_EVENTS_DETAILS_COMMENTS_CREATE,ROLE_UI_EVENTS_DETAILS_COMMENTS_DELETE,ROLE_UI_EVENTS_DETAILS_COMMENTS_EDIT,ROLE_UI_EVENTS_DETAILS_COMMENTS_REPLY,ROLE_UI_EVENTS_DETAILS_COMMENTS_RESOLVE,ROLE_UI_EVENTS_DETAILS_COMMENTS_VIEW,ROLE_UI_EVENTS_EDITOR_EDIT,ROLE_UI_EVENTS_EDITOR_VIEW,ROLE_STUDIO

# Prefix for LTI context based roles based on OAuth consumer keys.
# The LTI context (e.g. the course identifier) is used to generate context roles like “12345_Learner”.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,12 +56,14 @@
import java.util.Collections;
import java.util.Date;
import java.util.Dictionary;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;

import javax.persistence.RollbackException;
import javax.servlet.http.HttpServletRequest;
Expand Down Expand Up @@ -134,21 +136,26 @@ public class LtiLaunchAuthenticationHandler implements OAuthAuthenticationHandle
/** The user reference provider */
private UserReferenceProvider userReferenceProvider = null;

/** The role name of the user to add custom Roles to **/
/** The role name of the user to add custom Roles to (Legacy) **/
private static final String CUSTOM_ROLE_NAME = "lti.custom_role_name";

/** A List of Roles to add to the user if he has the custom role name **/
/** A List of Roles to add to the user if he has the custom role name (Legacy) **/
private static final String CUSTOM_ROLES = "lti.custom_roles";

/** Key prefix for names of the custom roles of the user **/
private static final String CUSTOM_ROLE_NAME_PREFIX = "lti.custom_role_name.";

/** Key prefix for list of roles to add to user if the user has the custom role **/
private static final String CUSTOM_ROLES_LIST_PREFIX = "lti.custom_roles.";

/** Key prefix for configuring consumer role prefixes */
private static final String ROLE_PREFIX_KEY = "lti.consumer_role_prefix.";

/** Consumer role prefix store */
private final ConcurrentHashMap<String, String> rolePrefixes = new ConcurrentHashMap<>();

private String customRoleName = "";

private String[] customRoles;
/** Custom role regex pattern */
private HashMap<Pattern, String[]> customRolePatterns = new HashMap<>();

/** The user details service */
private UserDetailsService userDetailsService;
Expand Down Expand Up @@ -253,10 +260,25 @@ protected void activate(ComponentContext cc) {
ltiRolesForUserCreation = extractLtiRolesForUserCreation(
Objects.toString(properties.get(LTI_ROLES_TO_CREATE_JPA_USER_REFERENCES_FROM), "*"));

customRoleName = StringUtils.trimToNull((String) properties.get(CUSTOM_ROLE_NAME));
for (int i = 1; true; i++) {
logger.debug("Looking for custom role configuration of {}", CUSTOM_ROLE_NAME_PREFIX + i);
String customRoleName = StringUtils.trimToNull((String) properties.get(CUSTOM_ROLE_NAME_PREFIX + i));
if (customRoleName == null) {
break;
}
String customRolesString = StringUtils.trimToNull((String) properties.get(CUSTOM_ROLES_LIST_PREFIX + i));
if (customRolesString == null) {
continue;
}
enrichLtiCustomRoles(customRoleName, customRolesString);
}

// Add support for legacy custom_role_name
String customRoleName = StringUtils.trimToNull((String) properties.get(CUSTOM_ROLE_NAME));

if (customRoleName != null) {
String custumRolesString = StringUtils.trimToNull((String) properties.get(CUSTOM_ROLES));
customRoles = custumRolesString.split(",");
String customRolesString = StringUtils.trimToNull((String) properties.get(CUSTOM_ROLES));
enrichLtiCustomRoles(customRoleName, customRolesString);
}

// Allow configuring prefixes for certain consumer
Expand Down Expand Up @@ -449,9 +471,12 @@ private void enrichRoleGrants(String roles, String context, final String rolePre

for (final String ltiRole : roleList) {
// Build the role
if (ltiRole.equals(customRoleName)) {
for (String roleName : customRoles) {
userAuthorities.add(new SimpleGrantedAuthority(roleName));
for (Pattern rolePattern : customRolePatterns.keySet()) {
logger.debug("Matching role pattern '{}' against '{}'", rolePattern.pattern(), ltiRole);
if (rolePattern.matcher(ltiRole).matches()) {
for (String roleName : customRolePatterns.get(rolePattern)) {
userAuthorities.add(new SimpleGrantedAuthority(roleName));
}
}
}

Expand All @@ -471,6 +496,12 @@ private void enrichRoleGrants(String roles, String context, final String rolePre
}
}

private void enrichLtiCustomRoles(String roleName, String customRolesString) {
String[] customRoles = customRolesString.split(",");
Pattern customRolePattern = Pattern.compile(roleName);
customRolePatterns.put(customRolePattern, customRoles);
}

/**
* Creates a JpaOrganization from an organization
*
Expand Down Expand Up @@ -519,5 +550,4 @@ private boolean requestContainsMatchingRoles(HttpServletRequest request) {
}
return false;
}

}

0 comments on commit cdbebed

Please sign in to comment.