All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.opencastproject.userdirectory.InMemoryUserAndRoleProvider Maven / Gradle / Ivy

/**
 * Licensed to The Apereo Foundation under one or more contributor license
 * agreements. See the NOTICE file distributed with this work for additional
 * information regarding copyright ownership.
 *
 *
 * The Apereo Foundation licenses this file to you under the Educational
 * Community License, Version 2.0 (the "License"); you may not use this file
 * except in compliance with the License. You may obtain a copy of the License
 * at:
 *
 *   http://opensource.org/licenses/ecl2.txt
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
 * License for the specific language governing permissions and limitations under
 * the License.
 *
 */

package org.opencastproject.userdirectory;

import static org.opencastproject.security.api.SecurityConstants.GLOBAL_SUDO_ROLE;

import org.opencastproject.security.api.JaxbOrganization;
import org.opencastproject.security.api.JaxbRole;
import org.opencastproject.security.api.JaxbUser;
import org.opencastproject.security.api.Organization;
import org.opencastproject.security.api.Role;
import org.opencastproject.security.api.RoleProvider;
import org.opencastproject.security.api.SecurityConstants;
import org.opencastproject.security.api.SecurityService;
import org.opencastproject.security.api.User;
import org.opencastproject.security.api.UserProvider;

import org.apache.commons.lang3.StringUtils;
import org.osgi.service.cm.ConfigurationException;
import org.osgi.service.cm.ManagedService;
import org.osgi.service.component.ComponentContext;
import org.osgi.service.component.annotations.Component;
import org.osgi.service.component.annotations.Reference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Dictionary;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
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 java.util.stream.Collectors;

/**
 * An in-memory user directory containing the users and roles used by the system.
 */
@Component(
    property = {
        "service.description=A user and role provider"
    },
    immediate = true,
    service = { UserProvider.class, RoleProvider.class, ManagedService.class }
)
public class InMemoryUserAndRoleProvider implements UserProvider, RoleProvider, ManagedService {

  /** The logging facility */
  private static final Logger logger = LoggerFactory.getLogger(InMemoryUserAndRoleProvider.class);

  public static final String PROVIDER_NAME = "system";

  /** The digest users */
  public static final String DIGEST_USER_NAME = "System User";
  public static final String CAPTURE_AGENT_USER_NAME = "Capture Agent";

  /** Configuration key for the digest users */
  public static final String DIGEST_USER_KEY = "org.opencastproject.security.digest.user";
  public static final String CAPTURE_AGENT_USER_PREFIX = "capture_agent.user.";

  /** Configuration key for the digest password */
  public static final String DIGEST_PASSWORD_KEY = "org.opencastproject.security.digest.pass";

  /**
   * System password set by default in the configuration file.
   * Note that this is not set if it is not defined in the configuration file.
   * */
  private static final String DIGEST_PASSWORD_DEFAULT_CONFIGURATION = "CHANGE_ME";

  /** Configuration key for optional additional roles for the capture agent user */
  public static final String CAPTURE_AGENT_ROLES_PREFIX = "capture_agent.roles.";

  /** The list of in-memory users */
  private final Map> inMemoryUsers = new ConcurrentHashMap<>();

  /**
   * List of capture agent users.
   * 
    *
  • the organization id is used as key.
  • *
  • the value is a list of capture agent user for that organization with each user being represented by a list * of the username and password, followed by additional roles
  • *
**/ private Map>> captureAgentUsers = new ConcurrentHashMap<>(); /** The security service */ protected SecurityService securityService; private String digestUsername; private String digestUserPass; /** * Callback to activate the component. * * @param cc * the declarative services component context */ protected void activate(ComponentContext cc) { digestUsername = StringUtils.trimToNull(cc.getBundleContext().getProperty(DIGEST_USER_KEY)); if (digestUsername == null) { logger.warn("Digest username has not been configured ({})", DIGEST_USER_KEY); } digestUserPass = StringUtils.trimToNull(cc.getBundleContext().getProperty(DIGEST_PASSWORD_KEY)); if (digestUserPass == null) { logger.warn("Digest password has not been configured ({})", DIGEST_PASSWORD_KEY); } else if (DIGEST_PASSWORD_DEFAULT_CONFIGURATION.equals(digestUserPass)) { logger.warn("\n" + "######################################################\n" + "# #\n" + "# WARNING: Opencast still uses the default system #\n" + "# credentials. Never do this in production. #\n" + "# #\n" + "# To change the password, edit the key #\n" + "# org.opencastproject.security.digest.pass #\n" + "# in custom.properties. #\n" + "# #\n" + "######################################################"); } } @Override public void updated(Dictionary properties) throws ConfigurationException { if (properties == null) { captureAgentUsers.clear(); // enforce reload of users if the configuration has changed inMemoryUsers.clear(); return; } Map>> newCAUsers = new ConcurrentHashMap<>(); Enumeration keys = properties.keys(); while (keys.hasMoreElements()) { final String key = keys.nextElement(); // skip non user definition keys if (!key.startsWith(CAPTURE_AGENT_USER_PREFIX)) { continue; } final String[] orgUser = key.substring(CAPTURE_AGENT_USER_PREFIX.length()).split("\\."); if (orgUser.length != 2) { logger.warn("Ignoring invalid capture agent user definition. Should be {}.., was {}", CAPTURE_AGENT_USER_PREFIX, key); } final String orgId = orgUser[0]; final String username = orgUser[1]; final String password = Objects.toString(properties.get(key), null); if (password == null) { continue; } // check for extra roles final String rolesStr = Objects.toString(properties.get(CAPTURE_AGENT_ROLES_PREFIX + orgId + '.' + username), ""); final String[] roles = StringUtils.split(rolesStr, ", "); final List userData = new ArrayList<>(); userData.add(username); userData.add(password); userData.addAll(Arrays.asList(roles)); if (!newCAUsers.containsKey(orgId)) { newCAUsers.put(orgId, new ArrayList<>()); } newCAUsers.get(orgId).add(userData); } // update list of CA users captureAgentUsers = newCAUsers; // enforce reload of users if the configuration has changed inMemoryUsers.clear(); } @Override public String getName() { return PROVIDER_NAME; } /** * Get a list of all users for the current organization, loading the users from configuration if they had not been * loaded before. * * @return List of users. */ private List getOrganizationUsers() { final Organization organization = securityService.getOrganization(); final List users = inMemoryUsers.get(organization.getId()); if (users == null) { return createSystemUsers(organization); } return users; } /** * Creates the digest users. * * @param organization * Organization to set users for * @return List of all users */ private synchronized List createSystemUsers(final Organization organization) { List users = inMemoryUsers.get(organization.getId()); if (users != null) { logger.trace("Organization users have already been initialized. Aborting."); return users; } users = new ArrayList<>(); JaxbOrganization jaxbOrganization = JaxbOrganization.fromOrganization(organization); // Create the system user if (digestUsername != null && digestUserPass != null) { // Role set for the system user Set roleList = new HashSet<>(); for (String roleName : SecurityConstants.GLOBAL_SYSTEM_ROLES) { roleList.add(new JaxbRole(roleName, jaxbOrganization)); } User digestUser = new JaxbUser(digestUsername, digestUserPass, DIGEST_USER_NAME, null, getName(), jaxbOrganization, roleList); users.add(digestUser); logger.info("Added system digest user '{}' for organization '{}'", digestUsername, organization.getId()); } for (List userData: captureAgentUsers.getOrDefault(organization.getId(), new ArrayList<>())) { final String username = userData.get(0); final String password = userData.get(1); // Role set for the capture agent user Set caRoleList = new HashSet<>(); // Add the organization anonymous role to the capture agent user caRoleList.add(new JaxbRole(organization.getAnonymousRole(), jaxbOrganization)); // Add global CA user roles Arrays.stream(SecurityConstants.GLOBAL_CAPTURE_AGENT_ROLES) .forEach((role) -> caRoleList.add(new JaxbRole(role, jaxbOrganization))); // Add additional custom rules userData.stream().skip(2).forEach((role) -> caRoleList.add(new JaxbRole(role, jaxbOrganization))); // Create the capture agent user logger.info("Creating the capture agent digest user '{}'", username); User caUser = new JaxbUser(username, password, CAPTURE_AGENT_USER_NAME, null, getName(), jaxbOrganization, caRoleList); users.add(caUser); } inMemoryUsers.put(organization.getId(), users); return users; } @Override public Iterator getUsers() { return getOrganizationUsers().iterator(); } /** * {@inheritDoc} * * @see org.opencastproject.security.api.UserProvider#loadUser(java.lang.String) */ @Override public User loadUser(String userName) { return getOrganizationUsers().stream() .filter(user -> user.getUsername().equals(userName)) .findFirst() .orElse(null); } /** * {@inheritDoc} * * @see java.lang.Object#toString() */ @Override public String toString() { return getClass().getName(); } /** * {@inheritDoc} * * @see org.opencastproject.security.api.UserProvider#getOrganization() */ @Override public String getOrganization() { return ALL_ORGANIZATIONS; } /** * {@inheritDoc} * * @see org.opencastproject.security.api.RoleProvider#getRolesForUser(String) */ @Override public List getRolesForUser(String userName) { return getOrganizationUsers().stream() .filter(user -> user.getUsername().equals(userName)) .flatMap(user -> user.getRoles().stream()) .collect(Collectors.toList()); } @Override public Iterator findUsers(String query, int offset, int limit) { if (query == null) { throw new IllegalArgumentException("Query must be set"); } // Find all users from the user providers return getOrganizationUsers().stream() .filter(user -> like(user.getUsername(), query)) .sorted(Comparator.comparing(User::getUsername)) .skip(offset).limit(limit <= 0 ? Long.MAX_VALUE : limit) .iterator(); } @Override public Iterator findRoles(String query, Role.Target target, int offset, int limit) { if (query == null) { throw new IllegalArgumentException("Query must be set"); } // Find all roles from the role providers return getOrganizationUsers().stream() .flatMap(user -> user.getRoles().stream()) .filter(role -> (like(role.getName(), query) || like(role.getDescription(), query)) && !(target == Role.Target.ACL && GLOBAL_SUDO_ROLE.equals(role.getName()))) .sorted(Comparator.comparing(Role::getName)) .skip(offset).limit(limit <= 0 ? Long.MAX_VALUE : limit) .iterator(); } private boolean like(final String string, final String query) { if (string == null) { return false; } final String regex = query.replace("_", ".").replace("%", ".*?"); final Pattern pattern = Pattern.compile(regex, Pattern.CASE_INSENSITIVE | Pattern.DOTALL); return pattern.matcher(string).matches(); } @Override public long countUsers() { return getOrganizationUsers().size(); } @Override public void invalidate(String userName) { // nothing to do } /** * Sets a reference to the security service. * * @param securityService * the security service */ @Reference void setSecurityService(SecurityService securityService) { this.securityService = securityService; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy