package org.carlspring.strongbox.config; import org.carlspring.strongbox.resource.ConfigurationResourceResolver; import org.carlspring.strongbox.security.Credentials; import org.carlspring.strongbox.security.UserAccessModel; import org.carlspring.strongbox.security.UserPathPermissions; import org.carlspring.strongbox.security.UserRepository; import org.carlspring.strongbox.security.Users; import org.carlspring.strongbox.security.encryption.EncryptionAlgorithms; import org.carlspring.strongbox.users.domain.AccessModel; import org.carlspring.strongbox.users.domain.Privileges; import org.carlspring.strongbox.users.domain.User; import org.carlspring.strongbox.users.service.UserService; import org.carlspring.strongbox.xml.parsers.GenericParser; import javax.annotation.PostConstruct; import javax.inject.Inject; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import java.io.IOException; import java.util.HashSet; import java.util.Set; import com.orientechnologies.orient.core.entity.OEntityManager; import com.orientechnologies.orient.core.metadata.schema.OClass; import com.orientechnologies.orient.core.metadata.schema.OType; import com.orientechnologies.orient.object.db.OObjectDatabaseTx; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.BeanInstantiationException; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Import; import org.springframework.core.io.Resource; import org.springframework.transaction.annotation.EnableTransactionManagement; import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.support.TransactionTemplate; /** * Spring configuration for all user-related code. * * @author Alex Oreshkevich */ @Configuration @ComponentScan({ "org.carlspring.strongbox.users" }) @EnableTransactionManagement(proxyTargetClass = true, order = DataServiceConfig.TRANSACTIONAL_INTERCEPTOR_ORDER) @Import({ DataServiceConfig.class, CommonConfig.class }) public class UsersConfig { private static final Logger logger = LoggerFactory.getLogger(UsersConfig.class); private final static GenericParser<Users> parser = new GenericParser<>(Users.class); @Inject private OEntityManager oEntityManager; @Inject private UserService userService; @PersistenceContext private EntityManager entityManager; @Inject private TransactionTemplate transactionTemplate; private final Class<User> userClass = User.class; @PostConstruct public void init() { logger.debug("Loading users..."); transactionTemplate.execute((s) -> { doInit(); return null; }); } private void doInit() { // register all domain entities oEntityManager.registerEntityClasses(User.class.getPackage() .getName()); // set unique constraints and index field 'username' if it isn't present yet OClass oUserClass = ((OObjectDatabaseTx) entityManager.getDelegate()).getMetadata() .getSchema() .getOrCreateClass( userClass.getSimpleName()); if (oUserClass.getIndexes() .stream() .noneMatch(oIndex -> oIndex.getName().equals("idx_username"))) { oUserClass.createProperty("username", OType.STRING); oUserClass.createIndex("idx_username", OClass.INDEX_TYPE.UNIQUE, "username"); } // remove all possible existing users (due to test executions with @Rollback(false) or another causes) // just to make sure userService.deleteAll(); loadUsersFromConfigFile(); } @Transactional private void loadUsersFromConfigFile() { try { // save loaded users to the database if schema do not exists boolean needToSaveInDb = userService.count() == 0; parser.parse(getUsersConfigurationResource().getURL()) .getUsers() .forEach(user -> obtainUser(user, needToSaveInDb)); } catch (Exception e) { logger.error("Unable to load users from configuration file.", e); throw new BeanInstantiationException(getClass(), "Unable to load users from configuration file.", e); } } @Transactional private void obtainUser(org.carlspring.strongbox.security.User user, boolean needToSaveInDb) { User internalUser = toInternalUser(user); if (needToSaveInDb) { logger.debug("Saving new user from config file:\n\t" + internalUser); try { userService.save(internalUser); } catch (Exception e) { logger.error("Unable to save user " + internalUser.getUsername(), e); } } } @Transactional private User toInternalUser(org.carlspring.strongbox.security.User user) { User internalUser = new User(); internalUser.setUsername(user.getUsername()); Credentials credentials = user.getCredentials(); EncryptionAlgorithms algorithms = EncryptionAlgorithms.valueOf(credentials.getEncryptionAlgorithm() .toUpperCase()); switch (algorithms) { case PLAIN: internalUser.setPassword(credentials.getPassword()); break; // TODO process other cases default: throw new UnsupportedOperationException(algorithms.toString()); } internalUser.setEnabled(true); internalUser.setRoles(user.getRoles()); internalUser.setSalt(user.getSeed() + ""); // load userAccessModel UserAccessModel userAccessModel = user.getUserAccessModel(); if (userAccessModel != null) { AccessModel internalAccessModel = new AccessModel(); userAccessModel.getStorages() .getStorages() .forEach(storage -> storage.getRepositories() .getRepositories() .forEach(repository -> processRepository(internalAccessModel, storage.getStorageId(), repository))); internalUser.setAccessModel(internalAccessModel); } return internalUser; } private void processRepository(AccessModel internalAccessModel, String storageId, UserRepository repository) { // assign default repository-level privileges set Set<String> defaultPrivileges = new HashSet<>(); String key = "/storages/" + storageId + "/" + repository.getRepositoryId(); repository.getPrivileges() .getPrivileges() .forEach(privilege -> defaultPrivileges.add(privilege.getName().toUpperCase())); internalAccessModel.getRepositoryPrivileges().put(key, defaultPrivileges); // assign path-specific privileges UserPathPermissions userPathPermissions = repository.getPathPermissions(); if (userPathPermissions != null) { userPathPermissions .getPathPermissions() .forEach(pathPermission -> { Set<String> privileges = translateToPrivileges(pathPermission.getPermission()); internalAccessModel.getUrlToPrivilegesMap() .put(key + "/" + pathPermission.getPath(), privileges); }); internalAccessModel.obtainPrivileges(); } } private Set<String> translateToPrivileges(String permission) { if (permission == null || permission.equalsIgnoreCase(Privileges.DEFAULT)) { return Privileges.rw(); } else { return Privileges.r(); } } private Resource getUsersConfigurationResource() throws IOException { return ConfigurationResourceResolver.getConfigurationResource("users.config.xml", "etc/conf/strongbox-security-users.xml"); } }