From c2c408605bd1c89c1306e6b8e64e048406a92c22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ant=C3=B3nio=20Lindo?= Date: Tue, 3 Dec 2024 10:50:38 +0000 Subject: [PATCH] users and groups synchronization (#438) --- .../common/server/ViewerConfiguration.java | 55 ++++++++++++++++--- .../server/controller/UserLoginHelper.java | 44 +++++++++------ .../resources/config/dbvtk-viewer.properties | 8 ++- .../resources/config/users-groups.properties | 20 +++++++ 4 files changed, 101 insertions(+), 26 deletions(-) create mode 100644 src/main/resources/config/users-groups.properties diff --git a/src/main/java/com/databasepreservation/common/server/ViewerConfiguration.java b/src/main/java/com/databasepreservation/common/server/ViewerConfiguration.java index 786efdd4b..463b32d6c 100644 --- a/src/main/java/com/databasepreservation/common/server/ViewerConfiguration.java +++ b/src/main/java/com/databasepreservation/common/server/ViewerConfiguration.java @@ -31,9 +31,13 @@ import java.util.Properties; import java.util.Set; import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.TimeUnit; import java.util.regex.Pattern; import java.util.stream.Collectors; +import org.apache.commons.configuration.CombinedConfiguration; import org.apache.commons.configuration.CompositeConfiguration; import org.apache.commons.configuration.Configuration; import org.apache.commons.configuration.ConfigurationException; @@ -60,7 +64,7 @@ /** * Singleton configuration instance used by the Database Visualization Toolkit - * + * * @author Bruno Ferreira */ public class ViewerConfiguration extends ViewerAbstractConfiguration { @@ -100,7 +104,7 @@ public class ViewerConfiguration extends ViewerAbstractConfiguration { public static final String PROPERTY_AUTHORIZATION_FULLNAME_ATTRIBUTE = "user.attribute.fullname"; public static final String PROPERTY_AUTHORIZATION_EMAIL_ATTRIBUTE = "user.attribute.email"; - public static final String PROPERTY_AUTHORIZATION_ROLES_ATTRIBUTE = "user.attribute.roles"; + public static final String PROPERTY_AUTHORIZATION_ROLES_ATTRIBUTE = "user.attribute.roles[]"; public static final String PROPERTY_AUTHORIZATION_ADMINISTRATORS = "user.attribute.roles.administrators"; public static final String PROPERTY_AUTHENTICATED_USER_ENABLE_DEFAULT_ATTRIBUTES = "authenticated.user.enable.default.attributes"; @@ -132,6 +136,8 @@ public class ViewerConfiguration extends ViewerAbstractConfiguration { public static final String PROPERTY_BLOB_PREFIX_NAME = "ui.blob.prefix.name"; public static final String SIARD_AVAILABLE_TO_SEARCH_ALL = "ui.siard.available.search.all"; + public static final String RELOAD_DBPTK_VIEWER_PROPERTIES = "ui.reload.viewer.properties"; + public static final String RELOAD_DBPTK_VIEWER_PROPERTIES_PERIOD = "ui.reload.viewer.properties.period"; private static boolean instantiatedWithoutErrors = true; private static String applicationEnvironment = ViewerConstants.APPLICATION_ENV_SERVER; @@ -153,7 +159,10 @@ public class ViewerConfiguration extends ViewerAbstractConfiguration { // Configuration related objects private static CompositeConfiguration viewerConfiguration = null; + private static final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); + private static CombinedConfiguration combinedConfiguration = new CombinedConfiguration(); private static List configurationFiles = null; + private static List commonConfigurationFiles = null; private List cachedWhitelistedIPs = null; private List cachedWhiteListedUsername = null; @@ -281,8 +290,12 @@ private ViewerConfiguration() { // load core configurations configurationFiles = new ArrayList<>(); - addConfiguration("dbvtk-viewer.properties"); - LOGGER.debug("Finished loading dbvtk-viewer.properties"); + commonConfigurationFiles = new ArrayList<>(); + + commonConfigurationFiles.add("dbvtk-viewer.properties"); + commonConfigurationFiles.add("users-groups.properties"); + addCommonConfiguration(); + LOGGER.debug("Finished loading " + String.join(" , ", commonConfigurationFiles)); addConfiguration("dbvtk-roles.properties"); LOGGER.debug("Finished loading dbvtk-roles.properties"); @@ -290,6 +303,19 @@ private ViewerConfiguration() { applicationEnvironment = System.getProperty(ViewerConstants.APPLICATION_ENV_KEY, ViewerConstants.APPLICATION_ENV_SERVER); + boolean reloadViewerConfiguration = getViewerConfigurationAsBoolean(false, RELOAD_DBPTK_VIEWER_PROPERTIES); + + if (reloadViewerConfiguration) { + long reloadPeriod = getViewerConfigurationAsInt(86400000, RELOAD_DBPTK_VIEWER_PROPERTIES_PERIOD); + scheduler.scheduleAtFixedRate(() -> { + try { + reloadCombinedConfiguration(); + } catch (ConfigurationException e) { + LOGGER.error("Error reloading combined configuration", e); + } + }, 0, reloadPeriod, TimeUnit.MILLISECONDS); + } + } catch (ConfigurationException e) { LOGGER.error("Error loading dbvtk properties", e); instantiatedWithoutErrors = false; @@ -534,14 +560,14 @@ public AuthorizationGroupsList getCollectionsAuthorizationGroupsWithAdminAndUser AuthorizationGroupsList authorizationGroupsList = getCollectionsAuthorizationGroups(); final List adminRoles = ViewerConfiguration.getInstance() - .getViewerConfigurationAsList(ViewerConfiguration.PROPERTY_AUTHORIZATION_ADMINISTRATORS); + .getViewerConfigurationAsList(ViewerConfiguration.PROPERTY_AUTHORIZATION_ADMINISTRATORS); for (String adminRole : adminRoles) { AuthorizationGroup authorizationGroup = new AuthorizationGroup(); authorizationGroup.setId("roles.administrators." + adminRole); authorizationGroup.setLabel("Administrators"); authorizationGroup.setAttributeName(getViewerConfigurationAsString(ViewerConstants.DEFAULT_ATTRIBUTE_ROLES, - ViewerConfiguration.PROPERTY_AUTHORIZATION_ROLES_ATTRIBUTE)); + ViewerConfiguration.PROPERTY_AUTHORIZATION_ROLES_ATTRIBUTE)); authorizationGroup.setAttributeOperator(PROPERTY_COLLECTIONS_AUTHORIZATION_GROUP_OPERATOR_EQUAL); authorizationGroup.setAttributeValue(adminRole); authorizationGroup.setType(AuthorizationGroup.Type.DEFAULT); @@ -553,7 +579,7 @@ public AuthorizationGroupsList getCollectionsAuthorizationGroupsWithAdminAndUser authorizationGroup.setId("roles.users"); authorizationGroup.setLabel("Users"); authorizationGroup.setAttributeName(getViewerConfigurationAsString(ViewerConstants.DEFAULT_ATTRIBUTE_ROLES, - ViewerConfiguration.PROPERTY_AUTHORIZATION_ROLES_ATTRIBUTE)); + ViewerConfiguration.PROPERTY_AUTHORIZATION_ROLES_ATTRIBUTE)); authorizationGroup.setAttributeOperator(PROPERTY_COLLECTIONS_AUTHORIZATION_GROUP_OPERATOR_EQUAL); authorizationGroup.setAttributeValue(getViewerConfigurationAsString("users", "user.attribute.roles.users")); authorizationGroup.setType(AuthorizationGroup.Type.DEFAULT); @@ -682,6 +708,21 @@ private static void addConfiguration(String configurationFile) throws Configurat configurationFiles.add(configurationFile); } + private static void addCommonConfiguration() throws ConfigurationException { + for (String configurationFile : commonConfigurationFiles) { + Configuration configuration = getConfiguration(configurationFile); + combinedConfiguration.addConfiguration((PropertiesConfiguration) configuration); + } + viewerConfiguration.addConfiguration(combinedConfiguration); + } + + private void reloadCombinedConfiguration() throws ConfigurationException { + viewerConfiguration.removeConfiguration(combinedConfiguration); + combinedConfiguration.clear(); + addCommonConfiguration(); + clearViewerCachableObjectsAfterConfigurationChange(); + } + private static Configuration getConfiguration(String configurationFile) throws ConfigurationException { Path config = configPath.resolve(configurationFile); PropertiesConfiguration propertiesConfiguration = new PropertiesConfiguration(); diff --git a/src/main/java/com/databasepreservation/common/server/controller/UserLoginHelper.java b/src/main/java/com/databasepreservation/common/server/controller/UserLoginHelper.java index 2d44d74ab..ddbcc397b 100644 --- a/src/main/java/com/databasepreservation/common/server/controller/UserLoginHelper.java +++ b/src/main/java/com/databasepreservation/common/server/controller/UserLoginHelper.java @@ -2,7 +2,7 @@ * The contents of this file are subject to the license and copyright * detailed in the LICENSE file at the root of the source * tree and available online at - * + *

* https://github.com/keeps/dbptk-ui */ package com.databasepreservation.common.server.controller; @@ -37,25 +37,31 @@ public static User casLogin(final String username, final HttpServletRequest requ AttributePrincipal attributePrincipal = (AttributePrincipal) request.getUserPrincipal(); Map attributes = attributePrincipal.getAttributes(); - final String rolesConfigurationValue = ViewerConfiguration.getInstance().getViewerConfigurationAsString( - ViewerConstants.DEFAULT_ATTRIBUTE_ROLES, ViewerConfiguration.PROPERTY_AUTHORIZATION_ROLES_ATTRIBUTE); + final List rolesConfigurationValue = ViewerConfiguration.getInstance() + .getViewerConfigurationAsList(ViewerConfiguration.PROPERTY_AUTHORIZATION_ROLES_ATTRIBUTE); final String fullNameConfigurationValue = ViewerConfiguration.getInstance().getViewerConfigurationAsString( ViewerConstants.DEFAULT_ATTRIBUTE_FULLNAME, ViewerConfiguration.PROPERTY_AUTHORIZATION_FULLNAME_ATTRIBUTE); final String emailConfigurationValue = ViewerConfiguration.getInstance().getViewerConfigurationAsString( ViewerConstants.DEFAULT_ATTRIBUTE_EMAIL, ViewerConfiguration.PROPERTY_AUTHORIZATION_EMAIL_ATTRIBUTE); - if (attributes.get(rolesConfigurationValue) instanceof String) { - Set roles = new HashSet<>(); - mapCasAttributeString(attributes, rolesConfigurationValue, roles::addAll); - user.setAllRoles(roles); - user.setDirectRoles(roles); - } else if (attributes.get(rolesConfigurationValue) instanceof List) { - mapCasAttributeList(user, attributes, rolesConfigurationValue, RodaPrincipal::setAllRoles); - mapCasAttributeList(user, attributes, rolesConfigurationValue, RodaPrincipal::setDirectRoles); + user.setAllRoles(new HashSet<>()); + user.setDirectRoles(new HashSet<>()); + for (String rolesAttribute : rolesConfigurationValue) { + if (attributes.get(rolesAttribute) instanceof String) { + Set roles = new HashSet<>(); + mapCasAttributeString(attributes, rolesAttribute, roles::addAll); + user.getAllRoles().addAll(roles); + user.getDirectRoles().addAll(roles); + } else if (attributes.get(rolesAttribute) instanceof List) { + mapCasAttributeList(user, attributes, rolesAttribute, + (user1, allRoles) -> user1.getAllRoles().addAll(allRoles)); + mapCasAttributeList(user, attributes, rolesAttribute, + (user1, directRoles) -> user1.getDirectRoles().addAll(directRoles)); + } } - mapAuthorizedGroups(user, - ViewerConfiguration.getInstance().getCollectionsAuthorizationGroupsWithAdminAndUserRoles().getAuthorizationGroupsList()); + mapAuthorizedGroups(user, ViewerConfiguration.getInstance() + .getCollectionsAuthorizationGroupsWithAdminAndUserRoles().getAuthorizationGroupsList()); // Add default roles to authenticated user boolean addDefaultRoles = ViewerConfiguration.getInstance().getViewerConfigurationAsBoolean(false, @@ -84,14 +90,16 @@ public static User casLogin(final String username, final HttpServletRequest requ private static void mapAuthorizedGroups(User user, Set authorizationGroups) { Set authorizedRoles = new HashSet<>(); for (AuthorizationGroup group : authorizationGroups) { - String rolesAttribute = ViewerConfiguration.getInstance().getViewerConfigurationAsString( - ViewerConstants.DEFAULT_ATTRIBUTE_ROLES, ViewerConfiguration.PROPERTY_AUTHORIZATION_ROLES_ATTRIBUTE); + List rolesAttribute = ViewerConfiguration.getInstance() + .getViewerConfigurationAsList(ViewerConfiguration.PROPERTY_AUTHORIZATION_ROLES_ATTRIBUTE); if (ViewerConfiguration.PROPERTY_COLLECTIONS_AUTHORIZATION_GROUP_OPERATOR_EQUAL .equals(group.getAttributeOperator())) { - if (rolesAttribute.equalsIgnoreCase(group.getAttributeName())) { - if (user.getAllRoles().stream().anyMatch(p -> p.equals(group.getAttributeValue()))) { - authorizedRoles.add(group.getAttributeValue()); + for (String roleAttribute : rolesAttribute) { + if (roleAttribute.equalsIgnoreCase(group.getAttributeName())) { + if (user.getAllRoles().stream().anyMatch(p -> p.equals(group.getAttributeValue()))) { + authorizedRoles.add(group.getAttributeValue()); + } } } } diff --git a/src/main/resources/config/dbvtk-viewer.properties b/src/main/resources/config/dbvtk-viewer.properties index b6d8ae3a5..0c9f8aa95 100644 --- a/src/main/resources/config/dbvtk-viewer.properties +++ b/src/main/resources/config/dbvtk-viewer.properties @@ -8,6 +8,7 @@ ############################################## ui.sharedProperties.whitelist.configuration.prefix=lists ui.sharedProperties.whitelist.configuration.prefix=ui.lists +ui.sharedProperties.whitelist.configuration.prefix=ui.reload ui.sharedProperties.whitelist.configuration.prefix=ui.header ui.sharedProperties.whitelist.configuration.prefix=ui.interface ui.sharedProperties.whitelist.configuration.prefix=ui.disable @@ -70,7 +71,7 @@ ui.disable.whitelist.cache=false ############################################## user.attribute.fullname=fullname user.attribute.email=email -user.attribute.roles=memberOf +user.attribute.roles[]=memberOf user.attribute.roles.administrators=administrators user.attribute.roles.users=users ############################################## @@ -168,3 +169,8 @@ ui.siard.available.search.all=true # Show schema name in reference table ############################################## ui.reference.table.show.schema.name=true +############################################## +# Reload dbptk viewer properties +############################################## +ui.reload.viewer.properties=false +ui.reload.viewer.properties.period=86400000 diff --git a/src/main/resources/config/users-groups.properties b/src/main/resources/config/users-groups.properties new file mode 100644 index 000000000..53187078e --- /dev/null +++ b/src/main/resources/config/users-groups.properties @@ -0,0 +1,20 @@ +############################################## +# This file is used to configure the users and groups if automatic reload of them is desired. +# If not keep this file empty. +############################################## +############################################## +# Authorization Groups +#-------------------------------------------- +# A list of authorization groups for databases, +# DBPTKE will compare user roles with permissions saved in databases status using these groups. +# Label: Replaces attributeValue in interface. +# attributeName: Session attribute that will be added in user roles +# attributeOperator: Operator used to check database permissions +# attributeValue: Value used to check database permissions +# For now only the EQUAL operator is implemented +############################################## +#lists.collections.authorization.groups[]={list value} +#lists.collections.authorization.groups[].{list value}.label=Group1 +#lists.collections.authorization.groups[].{list value}.attributeName=memberOf +#lists.collections.authorization.groups[].{list value}.attributeOperator=EQUAL +#lists.collections.authorization.groups[].{list value}.attributeValue=roda \ No newline at end of file