From 88621cd36125f41cb7dd06f952b56f54863c2603 Mon Sep 17 00:00:00 2001 From: GDLMadushanka Date: Wed, 25 Oct 2023 15:00:31 +0530 Subject: [PATCH] Improve the file-based user store Improve file-based user store to work as a primary user store. Fixes wso2/micro-integrator/issues/3006 --- .../http/api/ConfigurationLoader.java | 60 --- .../http/api/ConfigurationLoaderTestCase.java | 89 ++-- .../management/apis/ManagementApiParser.java | 16 - .../handler/AuthenticationHandlerAdapter.java | 1 + .../handler/AuthorizationHandler.java | 1 + .../handler/FileBasedUserStoreManager.java | 109 ---- .../handler/SecurityHandlerAdapter.java | 7 + .../apis/security/handler/SecurityUtils.java | 1 + .../apis/ManagementApiParserTest.java | 3 +- .../security/user/core/UserCoreConstants.java | 1 + .../core/common/AbstractUserStoreManager.java | 11 +- .../user/core/common/DefaultRealm.java | 30 +- .../user/core/common/DefaultRealmService.java | 18 +- .../security/user/core/dto/UserInfoDTO.java} | 11 +- .../core/file/FileBasedUserStoreManager.java | 499 ++++++++++++++++++ .../hybrid/FileBasedHybridRoleManager.java | 56 ++ .../user/core/jdbc/JDBCUserStoreManager.java | 52 +- .../ldap/ReadOnlyLDAPUserStoreManager.java | 33 +- .../ldap/ReadWriteLDAPUserStoreManager.java | 40 +- .../templates/conf/internal-apis.xml.j2 | 2 +- 20 files changed, 739 insertions(+), 301 deletions(-) delete mode 100644 components/org.wso2.micro.integrator.extensions/org.wso2.micro.integrator.management.apis/src/main/java/org/wso2/micro/integrator/management/apis/security/handler/FileBasedUserStoreManager.java rename components/{mediation/inbound-endpoints/org.wso2.micro.integrator.inbound.endpoint/src/main/java/org/wso2/carbon/inbound/endpoint/internal/http/api/UserInfo.java => org.wso2.micro.integrator.security/src/main/java/org/wso2/micro/integrator/security/user/core/dto/UserInfoDTO.java} (79%) create mode 100644 components/org.wso2.micro.integrator.security/src/main/java/org/wso2/micro/integrator/security/user/core/file/FileBasedUserStoreManager.java create mode 100644 components/org.wso2.micro.integrator.security/src/main/java/org/wso2/micro/integrator/security/user/core/hybrid/FileBasedHybridRoleManager.java diff --git a/components/mediation/inbound-endpoints/org.wso2.micro.integrator.inbound.endpoint/src/main/java/org/wso2/carbon/inbound/endpoint/internal/http/api/ConfigurationLoader.java b/components/mediation/inbound-endpoints/org.wso2.micro.integrator.inbound.endpoint/src/main/java/org/wso2/carbon/inbound/endpoint/internal/http/api/ConfigurationLoader.java index a73f4b5bd2..c42601da4d 100644 --- a/components/mediation/inbound-endpoints/org.wso2.micro.integrator.inbound.endpoint/src/main/java/org/wso2/carbon/inbound/endpoint/internal/http/api/ConfigurationLoader.java +++ b/components/mediation/inbound-endpoints/org.wso2.micro.integrator.inbound.endpoint/src/main/java/org/wso2/carbon/inbound/endpoint/internal/http/api/ConfigurationLoader.java @@ -63,12 +63,6 @@ public class ConfigurationLoader { private static final QName PROTOCOL_Q = new QName("protocol"); private static final QName HANDLERS_Q = new QName("handlers"); private static final QName RESOURCES_Q = new QName("resources"); - private static final QName USER_STORE_Q = new QName("userStore"); - private static final QName USERS_Q = new QName("users"); - private static final QName USER_Q = new QName("user"); - private static final QName USERNAME_Q = new QName("username"); - private static final QName PASSWORD_Q = new QName("password"); - private static final QName IS_ADMIN_Q = new QName("isAdmin"); private static final QName STORE_PASSWORD_Q = new QName("Password"); private static final QName KEY_PASSWORD_Q = new QName("KeyPassword"); @@ -84,7 +78,6 @@ public class ConfigurationLoader { private static SSLConfiguration sslConfiguration; private static boolean sslConfiguredSuccessfully; - private static Map userMap; private static List internalHttpApiList = new ArrayList<>(); private static List internalHttpsApiList = new ArrayList<>(); @@ -107,7 +100,6 @@ public static void loadInternalApis(String apiFilePath) { } setSecretResolver(apiConfig); - populateUserStore(apiConfig); Iterator apiIterator = apiConfig.getChildrenWithLocalName(APIS); @@ -187,54 +179,6 @@ public static void loadInternalApis(String apiFilePath) { } } - /** - * Populates the userList hashMap by userStore OM element - */ - private static void populateUserStore(OMElement apiConfig) { - OMElement userStoreOM = apiConfig.getFirstChildWithName(USER_STORE_Q); - if (Objects.nonNull(userStoreOM)) { - userMap = populateUsers(userStoreOM.getFirstChildWithName(USERS_Q)); - } else { - userMap = null; - } - } - - /** - * Populates individual users. - * - * @param users the parent element of users - * @return map of users against UserInfo config - */ - private static Map populateUsers(OMElement users) { - HashMap userMap = new HashMap<>(); - if (users != null) { - @SuppressWarnings("unchecked") - Iterator usersIterator = users.getChildrenWithName(USER_Q); - if (usersIterator != null) { - while (usersIterator.hasNext()) { - OMElement userElement = usersIterator.next(); - OMElement userNameElement = userElement.getFirstChildWithName(USERNAME_Q); - OMElement passwordElement = userElement.getFirstChildWithName(PASSWORD_Q); - OMElement isAdminElement = userElement.getFirstChildWithName(IS_ADMIN_Q); - if (userNameElement != null && passwordElement != null) { - String userName = userNameElement.getText(); - if (userMap.containsKey(userName)) { - handleException("Error parsing the file based user store. User: " + userName + " defined " - + "more than once. "); - } - boolean isAdmin = false; - if (isAdminElement != null) { - isAdmin = Boolean.parseBoolean(isAdminElement.getText().trim()); - } - userMap.put(userName, new UserInfo(userName, - resolveSecret(passwordElement.getText()).toCharArray(), isAdmin)); - } - } - } - } - return userMap; - } - /** * Checks if the text is protected and returns decrypted text if protected, else returns the plain text * @param text @@ -337,10 +281,6 @@ private static InternalAPI createApi(String classFQName) { } } - public static Map getUserMap() { - return userMap; - } - public static int getInternalInboundHttpPort() { return getPort(Constants.INTERNAL_HTTP_API_PORT, internalInboundHttpPortProperty, diff --git a/components/mediation/inbound-endpoints/org.wso2.micro.integrator.inbound.endpoint/src/test/java/internal/http/api/ConfigurationLoaderTestCase.java b/components/mediation/inbound-endpoints/org.wso2.micro.integrator.inbound.endpoint/src/test/java/internal/http/api/ConfigurationLoaderTestCase.java index 76d22b8ffe..a9a00aada7 100644 --- a/components/mediation/inbound-endpoints/org.wso2.micro.integrator.inbound.endpoint/src/test/java/internal/http/api/ConfigurationLoaderTestCase.java +++ b/components/mediation/inbound-endpoints/org.wso2.micro.integrator.inbound.endpoint/src/test/java/internal/http/api/ConfigurationLoaderTestCase.java @@ -24,7 +24,6 @@ import org.wso2.carbon.inbound.endpoint.internal.http.api.Constants; import org.wso2.carbon.inbound.endpoint.internal.http.api.InternalAPI; import org.wso2.carbon.inbound.endpoint.internal.http.api.InternalAPIHandler; -import org.wso2.carbon.inbound.endpoint.internal.http.api.UserInfo; import java.net.URL; import java.util.List; @@ -78,50 +77,50 @@ public void testLoadHandlers() { } - /** - * Test loading of internal apis from the internal-apis.xml file. - */ - @Test - public void testLoadUsers() { - - // test configuration with users - URL url = getClass().getResource("internal-apis.xml"); - Assert.assertNotNull("Configuration file not found", url); - - ConfigurationLoader.loadInternalApis("internal/http/api/internal-apis.xml"); - Map userMap = ConfigurationLoader.getUserMap(); - - org.junit.Assert.assertEquals(3, userMap.size()); - //Assert admin:admin - org.junit.Assert.assertNotNull(userMap.get("admin")); - org.junit.Assert.assertEquals("admin", String.valueOf(userMap.get("admin").getPassword())); - org.junit.Assert.assertTrue("isAdmin", userMap.get("admin").isAdmin()); - - //Assert user1:pwd1 - org.junit.Assert.assertNotNull(userMap.get("user1")); - org.junit.Assert.assertEquals("pwd1", String.valueOf(userMap.get("user1").getPassword())); - org.junit.Assert.assertFalse("isAdmin", userMap.get("user1").isAdmin()); - - //Assert user2:pwd2 - org.junit.Assert.assertNotNull(userMap.get("user2")); - org.junit.Assert.assertEquals("pwd2", String.valueOf(userMap.get("user2").getPassword())); - org.junit.Assert.assertFalse("isAdmin", userMap.get("user2").isAdmin()); - } - - /** - * Test loading of internal apis from the internal-apis.xml file. - */ - @Test - public void testLoadInternalApisWithNoUserStore() { - - // test configuration with users - URL url = getClass().getResource("internal-apis-without-user-store.xml"); - Assert.assertNotNull("Configuration file not found", url); - - ConfigurationLoader.loadInternalApis("internal/http/api/internal-apis-without-user-store.xml"); - Assert.assertNull("User store is not defined in the file but it is not null", - ConfigurationLoader.getUserMap()); - } +// /** +// * Test loading of internal apis from the internal-apis.xml file. +// */ +// @Test +// public void testLoadUsers() { +// +// // test configuration with users +// URL url = getClass().getResource("internal-apis.xml"); +// Assert.assertNotNull("Configuration file not found", url); +// +// ConfigurationLoader.loadInternalApis("internal/http/api/internal-apis.xml"); +// Map userMap = ConfigurationLoader.getUserMap(); +// +// org.junit.Assert.assertEquals(3, userMap.size()); +// //Assert admin:admin +// org.junit.Assert.assertNotNull(userMap.get("admin")); +// org.junit.Assert.assertEquals("admin", String.valueOf(userMap.get("admin").getPassword())); +// org.junit.Assert.assertTrue("isAdmin", userMap.get("admin").isAdmin()); +// +// //Assert user1:pwd1 +// org.junit.Assert.assertNotNull(userMap.get("user1")); +// org.junit.Assert.assertEquals("pwd1", String.valueOf(userMap.get("user1").getPassword())); +// org.junit.Assert.assertFalse("isAdmin", userMap.get("user1").isAdmin()); +// +// //Assert user2:pwd2 +// org.junit.Assert.assertNotNull(userMap.get("user2")); +// org.junit.Assert.assertEquals("pwd2", String.valueOf(userMap.get("user2").getPassword())); +// org.junit.Assert.assertFalse("isAdmin", userMap.get("user2").isAdmin()); +// } +// +// /** +// * Test loading of internal apis from the internal-apis.xml file. +// */ +// @Test +// public void testLoadInternalApisWithNoUserStore() { +// +// // test configuration with users +// URL url = getClass().getResource("internal-apis-without-user-store.xml"); +// Assert.assertNotNull("Configuration file not found", url); +// +// ConfigurationLoader.loadInternalApis("internal/http/api/internal-apis-without-user-store.xml"); +// Assert.assertNull("User store is not defined in the file but it is not null", +// ConfigurationLoader.getUserMap()); +// } private List getApis() { System.setProperty(Constants.PREFIX_TO_ENABLE_INTERNAL_APIS + "SampleAPI", "true"); diff --git a/components/org.wso2.micro.integrator.extensions/org.wso2.micro.integrator.management.apis/src/main/java/org/wso2/micro/integrator/management/apis/ManagementApiParser.java b/components/org.wso2.micro.integrator.extensions/org.wso2.micro.integrator.management.apis/src/main/java/org/wso2/micro/integrator/management/apis/ManagementApiParser.java index f3f75eaaf4..5e51091a4a 100644 --- a/components/org.wso2.micro.integrator.extensions/org.wso2.micro.integrator.management.apis/src/main/java/org/wso2/micro/integrator/management/apis/ManagementApiParser.java +++ b/components/org.wso2.micro.integrator.extensions/org.wso2.micro.integrator.management.apis/src/main/java/org/wso2/micro/integrator/management/apis/ManagementApiParser.java @@ -22,7 +22,6 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.wso2.carbon.inbound.endpoint.internal.http.api.ConfigurationLoader; -import org.wso2.carbon.inbound.endpoint.internal.http.api.UserInfo; import org.wso2.micro.core.util.CarbonException; import org.wso2.micro.integrator.core.util.MicroIntegratorBaseUtils; import org.wso2.securevault.SecretResolverFactory; @@ -89,21 +88,6 @@ public static OMElement getManagementApiElement() throws IOException, CarbonExce throw new ManagementApiUndefinedException("Management API not defined in " + getConfigurationFilePath()); } - /** - * Method to get the user store define in internal-apis.xml - * - * @return a non null map if the user store is defined. - * @throws UserStoreUndefinedException if the user store is not defined in internal-apis.xml - */ - public Map getUserMap() throws UserStoreUndefinedException { - Map usersMap = ConfigurationLoader.getUserMap(); - if (Objects.nonNull(usersMap)) { - return usersMap; - } else { - throw new UserStoreUndefinedException("UserStore tag not defined inside the Management API"); - } - } - private static OMElement getInternalApisElement() throws IOException, CarbonException, XMLStreamException { File mgtApiUserConfig = getConfigurationFile(); try (InputStream fileInputStream = new FileInputStream(mgtApiUserConfig)) { diff --git a/components/org.wso2.micro.integrator.extensions/org.wso2.micro.integrator.management.apis/src/main/java/org/wso2/micro/integrator/management/apis/security/handler/AuthenticationHandlerAdapter.java b/components/org.wso2.micro.integrator.extensions/org.wso2.micro.integrator.management.apis/src/main/java/org/wso2/micro/integrator/management/apis/security/handler/AuthenticationHandlerAdapter.java index cbdd9d6125..5fd6de6ad5 100644 --- a/components/org.wso2.micro.integrator.extensions/org.wso2.micro.integrator.management.apis/src/main/java/org/wso2/micro/integrator/management/apis/security/handler/AuthenticationHandlerAdapter.java +++ b/components/org.wso2.micro.integrator.extensions/org.wso2.micro.integrator.management.apis/src/main/java/org/wso2/micro/integrator/management/apis/security/handler/AuthenticationHandlerAdapter.java @@ -28,6 +28,7 @@ import org.wso2.micro.integrator.management.apis.ManagementApiUndefinedException; import org.wso2.micro.integrator.security.MicroIntegratorSecurityUtils; import org.wso2.micro.integrator.security.user.api.UserStoreException; +import org.wso2.micro.integrator.security.user.core.file.FileBasedUserStoreManager; import java.io.IOException; import java.util.Map; diff --git a/components/org.wso2.micro.integrator.extensions/org.wso2.micro.integrator.management.apis/src/main/java/org/wso2/micro/integrator/management/apis/security/handler/AuthorizationHandler.java b/components/org.wso2.micro.integrator.extensions/org.wso2.micro.integrator.management.apis/src/main/java/org/wso2/micro/integrator/management/apis/security/handler/AuthorizationHandler.java index a1556858a2..b91ab4d579 100644 --- a/components/org.wso2.micro.integrator.extensions/org.wso2.micro.integrator.management.apis/src/main/java/org/wso2/micro/integrator/management/apis/security/handler/AuthorizationHandler.java +++ b/components/org.wso2.micro.integrator.extensions/org.wso2.micro.integrator.management.apis/src/main/java/org/wso2/micro/integrator/management/apis/security/handler/AuthorizationHandler.java @@ -25,6 +25,7 @@ import org.wso2.micro.integrator.management.apis.ManagementApiUndefinedException; import org.wso2.micro.integrator.security.MicroIntegratorSecurityUtils; import org.wso2.micro.integrator.security.user.api.UserStoreException; +import org.wso2.micro.integrator.security.user.core.file.FileBasedUserStoreManager; import javax.xml.stream.XMLStreamException; import java.io.IOException; diff --git a/components/org.wso2.micro.integrator.extensions/org.wso2.micro.integrator.management.apis/src/main/java/org/wso2/micro/integrator/management/apis/security/handler/FileBasedUserStoreManager.java b/components/org.wso2.micro.integrator.extensions/org.wso2.micro.integrator.management.apis/src/main/java/org/wso2/micro/integrator/management/apis/security/handler/FileBasedUserStoreManager.java deleted file mode 100644 index 2612b5e7b0..0000000000 --- a/components/org.wso2.micro.integrator.extensions/org.wso2.micro.integrator.management.apis/src/main/java/org/wso2/micro/integrator/management/apis/security/handler/FileBasedUserStoreManager.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. - * - * WSO2 Inc. licenses this file to you under the Apache 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://www.apache.org/licenses/LICENSE-2.0 - * - * 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.wso2.micro.integrator.management.apis.security.handler; - -import org.apache.commons.lang3.StringUtils; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; -import org.wso2.carbon.inbound.endpoint.internal.http.api.UserInfo; -import org.wso2.micro.integrator.management.apis.ManagementApiParser; -import org.wso2.micro.integrator.management.apis.UserStoreUndefinedException; - -import java.util.Map; - -/** - * This class is used to authenticate, authorize users against the File based user store defined in internal-apis.xml - */ -public class FileBasedUserStoreManager { - - private static final Log LOG = LogFactory.getLog(FileBasedUserStoreManager.class); - private static final FileBasedUserStoreManager userStoreManager = new FileBasedUserStoreManager(); - private static Map usersList; - private static boolean isInitialized = false; - - private FileBasedUserStoreManager() { - - initializeUserStore(); - } - - /** - * Method to retrieve FileBasedUserStoreManager - * - * @return FileBasedUserStoreManager - */ - public static FileBasedUserStoreManager getUserStoreManager() { - - return userStoreManager; - } - - /** - * Authenticate the user against the file based user store. - * - * @param username the user to be authenticated - * @param password the password used for authentication - * @return true if authenticated - */ - public boolean authenticate(String username, String password) { - - if (usersList.containsKey(username)) { - String passwordFromStore = String.valueOf(usersList.get(username).getPassword()); - if (StringUtils.isNotBlank(passwordFromStore) && passwordFromStore.equals(password)) { - return true; - } - } - return false; - } - - /** - * Method to assert if a user is an admin - * - * @param username the user to be validated as an admin - * @return true if the admin role is assigned to the user - */ - public boolean isAdmin(String username) { - - if (usersList.containsKey(username)) { - UserInfo userInfo = usersList.get(username); - return userInfo.isAdmin(); - } - return false; - } - - /** - * Method to check whether FileBasedUserStoreManager is initialized - * - * @return true if successfully initialized - */ - public boolean isInitialized() { - - return isInitialized; - } - - private static void initializeUserStore() { - - ManagementApiParser mgtParser = new ManagementApiParser(); - try { - usersList = mgtParser.getUserMap(); - isInitialized = true; - } catch (UserStoreUndefinedException e) { - LOG.error("User store config has not been defined in file " - + ManagementApiParser.getConfigurationFilePath(), e); - } - } -} diff --git a/components/org.wso2.micro.integrator.extensions/org.wso2.micro.integrator.management.apis/src/main/java/org/wso2/micro/integrator/management/apis/security/handler/SecurityHandlerAdapter.java b/components/org.wso2.micro.integrator.extensions/org.wso2.micro.integrator.management.apis/src/main/java/org/wso2/micro/integrator/management/apis/security/handler/SecurityHandlerAdapter.java index 6b4292ed5e..b878c09b3a 100644 --- a/components/org.wso2.micro.integrator.extensions/org.wso2.micro.integrator.management.apis/src/main/java/org/wso2/micro/integrator/management/apis/security/handler/SecurityHandlerAdapter.java +++ b/components/org.wso2.micro.integrator.extensions/org.wso2.micro.integrator.management.apis/src/main/java/org/wso2/micro/integrator/management/apis/security/handler/SecurityHandlerAdapter.java @@ -23,9 +23,11 @@ import org.apache.synapse.MessageContext; import org.apache.synapse.rest.RESTConstants; import org.wso2.carbon.inbound.endpoint.internal.http.api.InternalAPIHandler; +import org.wso2.config.mapper.ConfigParser; import org.wso2.micro.core.util.CarbonException; import org.wso2.micro.integrator.management.apis.Constants; import org.wso2.micro.integrator.management.apis.ManagementApiUndefinedException; +import org.wso2.micro.integrator.security.user.core.file.FileBasedUserStoreManager; import java.io.IOException; import java.util.ArrayList; @@ -40,6 +42,7 @@ public abstract class SecurityHandlerAdapter implements InternalAPIHandler { protected static boolean useCarbonUserStore = false; private static boolean isInitialized = false; + private static String FILE_BASED_USER_STORE_AS_PRIMARY = "internal_apis.file_user_store.primary"; /** * Resources defined in internal-apis.xml to be handled */ @@ -67,6 +70,10 @@ private static void initializeUserStore() throws CarbonException, IOException, M XMLStreamException { if (!isInitialized) { if (SecurityUtils.isFileBasedUserStoreEnabled()) { + Object fileUserStore = ConfigParser.getParsedConfigs().get(FILE_BASED_USER_STORE_AS_PRIMARY); + if (fileUserStore != null && Boolean.parseBoolean(fileUserStore.toString())) { + useCarbonUserStore = true; + } isInitialized = FileBasedUserStoreManager.getUserStoreManager().isInitialized(); } else { LOG.info("File based user store has been disabled. Carbon user store settings will be used."); diff --git a/components/org.wso2.micro.integrator.extensions/org.wso2.micro.integrator.management.apis/src/main/java/org/wso2/micro/integrator/management/apis/security/handler/SecurityUtils.java b/components/org.wso2.micro.integrator.extensions/org.wso2.micro.integrator.management.apis/src/main/java/org/wso2/micro/integrator/management/apis/security/handler/SecurityUtils.java index 9c8cc10b8f..ef00e46a5c 100644 --- a/components/org.wso2.micro.integrator.extensions/org.wso2.micro.integrator.management.apis/src/main/java/org/wso2/micro/integrator/management/apis/security/handler/SecurityUtils.java +++ b/components/org.wso2.micro.integrator.extensions/org.wso2.micro.integrator.management.apis/src/main/java/org/wso2/micro/integrator/management/apis/security/handler/SecurityUtils.java @@ -27,6 +27,7 @@ import org.wso2.micro.integrator.management.apis.Constants; import org.wso2.micro.integrator.security.MicroIntegratorSecurityUtils; import org.wso2.micro.integrator.security.user.api.UserStoreException; +import org.wso2.micro.integrator.security.user.core.file.FileBasedUserStoreManager; import org.wso2.securevault.SecretResolver; import org.wso2.securevault.SecureVaultException; diff --git a/components/org.wso2.micro.integrator.extensions/org.wso2.micro.integrator.management.apis/src/test/java/org/wso2/micro/integrator/management/apis/ManagementApiParserTest.java b/components/org.wso2.micro.integrator.extensions/org.wso2.micro.integrator.management.apis/src/test/java/org/wso2/micro/integrator/management/apis/ManagementApiParserTest.java index 629b64e9ff..17b3bd930a 100644 --- a/components/org.wso2.micro.integrator.extensions/org.wso2.micro.integrator.management.apis/src/test/java/org/wso2/micro/integrator/management/apis/ManagementApiParserTest.java +++ b/components/org.wso2.micro.integrator.extensions/org.wso2.micro.integrator.management.apis/src/test/java/org/wso2/micro/integrator/management/apis/ManagementApiParserTest.java @@ -28,7 +28,6 @@ import org.powermock.core.classloader.annotations.PrepareForTest; import org.powermock.modules.junit4.PowerMockRunner; import org.wso2.carbon.inbound.endpoint.internal.http.api.ConfigurationLoader; -import org.wso2.carbon.inbound.endpoint.internal.http.api.UserInfo; import org.wso2.micro.core.util.CarbonException; import org.wso2.micro.integrator.core.internal.MicroIntegratorBaseConstants; @@ -60,6 +59,7 @@ public void getManagementApiElement() throws CarbonException, XMLStreamException Assert.assertEquals(MGT_API_NAME, managementApiElement.getAttributeValue(NAME_ATTR)); } + /* @Test public void testGetUserList() throws UserStoreUndefinedException { Map userMap = new HashMap<>(); @@ -79,4 +79,5 @@ public void testGetNullUserStore() throws UserStoreUndefinedException { ManagementApiParser managementApiParser = new ManagementApiParser(); managementApiParser.getUserMap(); } + */ } diff --git a/components/org.wso2.micro.integrator.security/src/main/java/org/wso2/micro/integrator/security/user/core/UserCoreConstants.java b/components/org.wso2.micro.integrator.security/src/main/java/org/wso2/micro/integrator/security/user/core/UserCoreConstants.java index cfc558ec6d..696f28e681 100644 --- a/components/org.wso2.micro.integrator.security/src/main/java/org/wso2/micro/integrator/security/user/core/UserCoreConstants.java +++ b/components/org.wso2.micro.integrator.security/src/main/java/org/wso2/micro/integrator/security/user/core/UserCoreConstants.java @@ -67,6 +67,7 @@ public class UserCoreConstants { public static final String IS_USER_IN_ROLE_CACHE_IDENTIFIER = "@__isUserHasTheRole__@"; public static final String DOMAIN_SEPARATOR; + public static final String FILE_BASED_USER_STORE_AS_PRIMARY = "internal_apis.file_user_store.primary"; static { String userDomainSeparator = CarbonServerConfigurationService.getInstance().getFirstProperty("UserDomainSeparator"); diff --git a/components/org.wso2.micro.integrator.security/src/main/java/org/wso2/micro/integrator/security/user/core/common/AbstractUserStoreManager.java b/components/org.wso2.micro.integrator.security/src/main/java/org/wso2/micro/integrator/security/user/core/common/AbstractUserStoreManager.java index 6e56da6d13..cf3413dcc0 100644 --- a/components/org.wso2.micro.integrator.security/src/main/java/org/wso2/micro/integrator/security/user/core/common/AbstractUserStoreManager.java +++ b/components/org.wso2.micro.integrator.security/src/main/java/org/wso2/micro/integrator/security/user/core/common/AbstractUserStoreManager.java @@ -40,6 +40,7 @@ import org.wso2.micro.integrator.security.user.core.constants.UserCoreErrorConstants; import org.wso2.micro.integrator.security.user.core.constants.UserCoreErrorConstants.ErrorMessages; import org.wso2.micro.integrator.security.user.core.dto.RoleDTO; +import org.wso2.micro.integrator.security.user.core.hybrid.FileBasedHybridRoleManager; import org.wso2.micro.integrator.security.user.core.hybrid.HybridRoleManager; import org.wso2.micro.integrator.security.user.core.hybrid.JdbcHybridRoleManager; import org.wso2.micro.integrator.security.user.core.internal.UMListenerServiceComponent; @@ -5790,9 +5791,13 @@ protected List getMappingAttributeList(List claimList) return attributeList; } - protected void doInitialSetup() throws UserStoreException { - systemUserRoleManager = new SystemUserRoleManager(dataSource, tenantId); - hybridRoleManager = new JdbcHybridRoleManager(dataSource, tenantId, realmConfig, userRealm); + protected void doInitialSetup(boolean isFileBased) throws UserStoreException { + if (!isFileBased) { + systemUserRoleManager = new SystemUserRoleManager(dataSource, tenantId); + hybridRoleManager = new JdbcHybridRoleManager(dataSource, tenantId, realmConfig, userRealm); + } else { + hybridRoleManager = new FileBasedHybridRoleManager(dataSource, tenantId, realmConfig, userRealm); + } } /** diff --git a/components/org.wso2.micro.integrator.security/src/main/java/org/wso2/micro/integrator/security/user/core/common/DefaultRealm.java b/components/org.wso2.micro.integrator.security/src/main/java/org/wso2/micro/integrator/security/user/core/common/DefaultRealm.java index 3ed8b7ae91..85b2b694c6 100644 --- a/components/org.wso2.micro.integrator.security/src/main/java/org/wso2/micro/integrator/security/user/core/common/DefaultRealm.java +++ b/components/org.wso2.micro.integrator.security/src/main/java/org/wso2/micro/integrator/security/user/core/common/DefaultRealm.java @@ -19,6 +19,8 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.snmp4j.User; +import org.wso2.config.mapper.ConfigParser; import org.wso2.micro.integrator.security.user.api.RealmConfiguration; import org.wso2.micro.integrator.security.user.core.AuthorizationManager; import org.wso2.micro.integrator.security.user.core.UserCoreConstants; @@ -62,6 +64,8 @@ public class DefaultRealm implements UserRealm { private AuthorizationManager authzManager = null; private Map properties = null; + private boolean fileBasedUserStoreMode = false; + /** * Usage of this method is found on tests. * @@ -115,16 +119,21 @@ public void init(RealmConfiguration configBean, Map propertiesMa Map claimMappings = new HashMap(); Map profileConfigs = new HashMap(); - - if (Boolean.parseBoolean(realmConfig.getRealmProperty(UserCoreClaimConstants.INITIALIZE_NEW_CLAIM_MANAGER))) { - if (UserStoreMgtDSComponent.getClaimManagerFactory() != null) { - claimMan = UserStoreMgtDSComponent.getClaimManagerFactory().createClaimManager(tenantId); + Object fileUserStore = ConfigParser.getParsedConfigs().get(UserCoreConstants.FILE_BASED_USER_STORE_AS_PRIMARY); + if (fileUserStore != null && Boolean.parseBoolean(fileUserStore.toString())) { + fileBasedUserStoreMode = true; + } + if (!fileBasedUserStoreMode) { + if (Boolean.parseBoolean(realmConfig.getRealmProperty(UserCoreClaimConstants.INITIALIZE_NEW_CLAIM_MANAGER))) { + if (UserStoreMgtDSComponent.getClaimManagerFactory() != null) { + claimMan = UserStoreMgtDSComponent.getClaimManagerFactory().createClaimManager(tenantId); + } else { + claimMan = new InMemoryClaimManager(); + } } else { - claimMan = new InMemoryClaimManager(); + populateProfileAndClaimMaps(claimMappings, profileConfigs); + claimMan = new DefaultClaimManager(claimMappings, dataSource, tenantId); } - } else { - populateProfileAndClaimMaps(claimMappings, profileConfigs); - claimMan = new DefaultClaimManager(claimMappings, dataSource, tenantId); } initializeObjects(); } @@ -310,8 +319,9 @@ private void initializeObjects() throws UserStoreException { log.error(message); throw new UserStoreException(message); } - this.authzManager = (AuthorizationManager) createObjectWithOptions(value, realmConfig, - properties); + if (!fileBasedUserStoreMode) { + this.authzManager = (AuthorizationManager) createObjectWithOptions(value, realmConfig, properties); + } } catch (Exception e) { log.error(e.getMessage(), e); diff --git a/components/org.wso2.micro.integrator.security/src/main/java/org/wso2/micro/integrator/security/user/core/common/DefaultRealmService.java b/components/org.wso2.micro.integrator.security/src/main/java/org/wso2/micro/integrator/security/user/core/common/DefaultRealmService.java index b485757f20..46d6432973 100644 --- a/components/org.wso2.micro.integrator.security/src/main/java/org/wso2/micro/integrator/security/user/core/common/DefaultRealmService.java +++ b/components/org.wso2.micro.integrator.security/src/main/java/org/wso2/micro/integrator/security/user/core/common/DefaultRealmService.java @@ -22,6 +22,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.osgi.framework.BundleContext; +import org.wso2.config.mapper.ConfigParser; import org.wso2.micro.core.Constants; import org.wso2.micro.core.util.CarbonException; import org.wso2.micro.integrator.core.internal.MicroIntegratorBaseConstants; @@ -83,13 +84,16 @@ public DefaultRealmService(BundleContext bc, RealmConfiguration realmConfig) thr } else { this.bootstrapRealmConfig = buildBootStrapRealmConfig(); } -// this.tenantMgtConfiguration = buildTenantMgtConfig(bc, -// this.bootstrapRealmConfig.getUserStoreProperty(UserCoreConstants.TenantMgtConfig.LOCAL_NAME_TENANT_MANAGER)); - this.dataSource = DatabaseUtil.getRealmDataSource(bootstrapRealmConfig); - // TODO We do not need to init DB for now - //initializeDatabase(dataSource); - properties.put(UserCoreConstants.DATA_SOURCE, dataSource); - properties.put(UserCoreConstants.FIRST_STARTUP_CHECK, isFirstInitialization); + Object fileUserStore = ConfigParser.getParsedConfigs().get(UserCoreConstants.FILE_BASED_USER_STORE_AS_PRIMARY); + if (!(fileUserStore != null && Boolean.parseBoolean(fileUserStore.toString()))) { + // this.tenantMgtConfiguration = buildTenantMgtConfig(bc, + // this.bootstrapRealmConfig.getUserStoreProperty(UserCoreConstants.TenantMgtConfig.LOCAL_NAME_TENANT_MANAGER)); + this.dataSource = DatabaseUtil.getRealmDataSource(bootstrapRealmConfig); + // TODO We do not need to init DB for now + //initializeDatabase(dataSource); + properties.put(UserCoreConstants.DATA_SOURCE, dataSource); + properties.put(UserCoreConstants.FIRST_STARTUP_CHECK, isFirstInitialization); + } //this.tenantManager = this.initializeTenantManger(this.getTenantConfigurationElement(bc)); // this.tenantManager = this.initializeTenantManger(this.tenantMgtConfiguration); diff --git a/components/mediation/inbound-endpoints/org.wso2.micro.integrator.inbound.endpoint/src/main/java/org/wso2/carbon/inbound/endpoint/internal/http/api/UserInfo.java b/components/org.wso2.micro.integrator.security/src/main/java/org/wso2/micro/integrator/security/user/core/dto/UserInfoDTO.java similarity index 79% rename from components/mediation/inbound-endpoints/org.wso2.micro.integrator.inbound.endpoint/src/main/java/org/wso2/carbon/inbound/endpoint/internal/http/api/UserInfo.java rename to components/org.wso2.micro.integrator.security/src/main/java/org/wso2/micro/integrator/security/user/core/dto/UserInfoDTO.java index cd07453c58..9d34b47dd5 100644 --- a/components/mediation/inbound-endpoints/org.wso2.micro.integrator.inbound.endpoint/src/main/java/org/wso2/carbon/inbound/endpoint/internal/http/api/UserInfo.java +++ b/components/org.wso2.micro.integrator.security/src/main/java/org/wso2/micro/integrator/security/user/core/dto/UserInfoDTO.java @@ -1,7 +1,7 @@ /* - * Copyright (c) 2021, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. * - * WSO2 Inc. licenses this file to you under the Apache License, + * WSO2 LLC. licenses this file to you under the Apache 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 @@ -16,18 +16,17 @@ * under the License. */ -package org.wso2.carbon.inbound.endpoint.internal.http.api; +package org.wso2.micro.integrator.security.user.core.dto; /** * This class is the DTO for User configs in internal-apis.xml */ -public class UserInfo { - +public class UserInfoDTO { private String username; private char[] password; private boolean admin; - public UserInfo(String username, char[] password, boolean admin) { + public UserInfoDTO(String username, char[] password, boolean admin) { this.username = username; this.password = password; diff --git a/components/org.wso2.micro.integrator.security/src/main/java/org/wso2/micro/integrator/security/user/core/file/FileBasedUserStoreManager.java b/components/org.wso2.micro.integrator.security/src/main/java/org/wso2/micro/integrator/security/user/core/file/FileBasedUserStoreManager.java new file mode 100644 index 0000000000..7c386f82dd --- /dev/null +++ b/components/org.wso2.micro.integrator.security/src/main/java/org/wso2/micro/integrator/security/user/core/file/FileBasedUserStoreManager.java @@ -0,0 +1,499 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 LLC. licenses this file to you under the Apache 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://www.apache.org/licenses/LICENSE-2.0 + * + * 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.wso2.micro.integrator.security.user.core.file; + +import org.apache.axiom.om.OMElement; +import org.apache.axiom.om.impl.builder.StAXOMBuilder; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; + +import org.wso2.micro.core.util.CarbonException; +import org.wso2.micro.integrator.core.util.MicroIntegratorBaseUtils; +import org.wso2.micro.integrator.security.UnsupportedSecretTypeException; +import org.wso2.micro.integrator.security.user.api.Properties; +import org.wso2.micro.integrator.security.user.api.RealmConfiguration; +import org.wso2.micro.integrator.security.user.api.Tenant; +import org.wso2.micro.integrator.security.user.api.UserStoreException; +import org.wso2.micro.integrator.security.user.core.UserCoreConstants; +import org.wso2.micro.integrator.security.user.core.UserRealm; +import org.wso2.micro.integrator.security.user.core.claim.ClaimManager; +import org.wso2.micro.integrator.security.user.core.common.AbstractUserStoreManager; +import org.wso2.micro.integrator.security.user.core.common.RoleContext; +import org.wso2.micro.integrator.security.user.core.dto.UserInfoDTO; +import org.wso2.micro.integrator.security.user.core.profile.ProfileConfigurationManager; +import org.wso2.micro.integrator.security.user.core.util.JDBCRealmUtil; +import org.wso2.micro.integrator.security.util.Secret; +import org.wso2.securevault.SecretResolver; +import org.wso2.securevault.SecretResolverFactory; +import org.wso2.securevault.commons.MiscellaneousUtil; + +import javax.xml.namespace.QName; +import javax.xml.stream.XMLStreamException; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Objects; + +/** + * This class is used to authenticate, authorize users against the File based user store defined in internal-apis.xml + */ +public class FileBasedUserStoreManager extends AbstractUserStoreManager { + + private static final Log log = LogFactory.getLog(FileBasedUserStoreManager.class); + private static final FileBasedUserStoreManager userStoreManager = new FileBasedUserStoreManager(); + private static Map usersList; + private static boolean isInitialized = false; + private static final String USER_STORE = "userStore"; + private static final String USERS = "users"; + private static final String USER = "user"; + private static final String USERNAME = "username"; + private static final String PASSWORD = "password"; + private static final String IS_ADMIN = "isAdmin"; + private static SecretResolver secretResolver; + + private FileBasedUserStoreManager() { + initializeUserStore(); + } + + /** + * Method to retrieve FileBasedUserStoreManager + * + * @return FileBasedUserStoreManager + */ + public static FileBasedUserStoreManager getUserStoreManager() { + return userStoreManager; + } + + /** + * Authenticate the user against the file based user store. + * + * @param username the user to be authenticated + * @param password the password used for authentication + * @return true if authenticated + */ + public boolean authenticate(String username, String password) { + if (usersList.containsKey(username)) { + String passwordFromStore = String.valueOf(usersList.get(username).getPassword()); + return StringUtils.isNotBlank(passwordFromStore) && passwordFromStore.equals(password); + } + return false; + } + + /** + * Method to assert if a user is an admin + * + * @param username the user to be validated as an admin + * @return true if the admin role is assigned to the user + */ + public boolean isAdmin(String username) { + if (usersList.containsKey(username)) { + UserInfoDTO userInfo = usersList.get(username); + return userInfo.isAdmin(); + } + return false; + } + + /** + * Method to check whether a user exists in the FileBasedUserStoreManager. + * + * @param username the user to be checked. + * @return true if the user exists. + */ + public boolean isUserExists(String username) { + return usersList.containsKey(username); + } + + /** + * Method to check whether FileBasedUserStoreManager is initialized + * + * @return true if successfully initialized + */ + public boolean isInitialized() { + return isInitialized; + } + + /** + * Method to initialize FileBasedUserStoreManager using the internal-apis.xml + */ + private static void initializeUserStore() { + OMElement documentElement = null; + // Fetch users from internal-apis.xml + File mgtApiUserConfig = new File(MicroIntegratorBaseUtils.getCarbonConfigDirPath(), "internal-apis.xml"); + try (InputStream fileInputStream = Files.newInputStream(mgtApiUserConfig.toPath())) { + InputStream inputStream = MicroIntegratorBaseUtils.replaceSystemVariablesInXml(fileInputStream); + StAXOMBuilder builder = new StAXOMBuilder(inputStream); + documentElement = builder.getDocumentElement(); + } catch (IOException | CarbonException | XMLStreamException e) { + log.error("Error parsing the file based user store. Error reading internal-apis.xml", e); + } + if (documentElement == null) { + log.error("Error parsing the file based user store. Error reading internal-apis.xml"); + return; + } + secretResolver = SecretResolverFactory.create(documentElement, true); + OMElement userStoreOM = (OMElement) documentElement.getChildrenWithName(new QName(USER_STORE)).next(); + if (Objects.nonNull(userStoreOM)) { + usersList = populateUsers(userStoreOM.getFirstChildWithName(new QName(USERS))); + isInitialized = true; + } else { + log.error("Error parsing the file based user store. User store element not found in internal-apis.xml"); + } + } + + @Override + public Map getProperties(Tenant tenant) throws UserStoreException { + return null; + } + + @Override + public boolean isMultipleProfilesAllowed() { + return false; + } + + @Override + public void addRememberMe(String userName, String token) throws UserStoreException { + + } + + @Override + public boolean isValidRememberMeToken(String userName, String token) throws UserStoreException { + return false; + } + + @Override + public Properties getDefaultUserStoreProperties() { + return null; + } + + @Override + public String[] getProfileNames(String userName) { + return new String[0]; + } + + @Override + public String[] getAllProfileNames() { + return new String[0]; + } + + @Override + public boolean isReadOnly() throws org.wso2.micro.integrator.security.user.core.UserStoreException { + return false; + } + + @Override + public int getUserId(String username) { + return 0; + } + + @Override + public int getTenantId(String username) throws org.wso2.micro.integrator.security.user.core.UserStoreException { + return 0; + } + + @Override + public int getTenantId() throws org.wso2.micro.integrator.security.user.core.UserStoreException { + return 0; + } + + @Override + public Map getProperties(org.wso2.micro.integrator.security.user.core.tenant.Tenant tenant) + throws org.wso2.micro.integrator.security.user.core.UserStoreException { + return null; + } + + @Override + public boolean isBulkImportSupported() { + return false; + } + + @Override + public RealmConfiguration getRealmConfiguration() { + return this.realmConfig; + } + + @Override + protected Map getUserPropertyValues(String userName, String[] propertyNames, String profileName) throws org.wso2.micro.integrator.security.user.core.UserStoreException { + return null; + } + + @Override + protected boolean doCheckExistingRole(String roleName) throws org.wso2.micro.integrator.security.user.core.UserStoreException { + return false; + } + + @Override + protected RoleContext createRoleContext(String roleName) throws org.wso2.micro.integrator.security.user.core.UserStoreException { + return null; + } + + @Override + protected boolean doCheckExistingUser(String userName) throws org.wso2.micro.integrator.security.user.core.UserStoreException { + return false; + } + + @Override + protected String[] getUserListFromProperties(String property, String value, String profileName) throws org.wso2.micro.integrator.security.user.core.UserStoreException { + return new String[0]; + } + + @Override + protected boolean doAuthenticate(String userName, Object credential) throws org.wso2.micro.integrator.security.user.core.UserStoreException { + if (credential == null || ((Secret) credential).getChars().length == 0) { + return false; + } else { + try { + Secret password = Secret.getSecret(credential); + return authenticate(userName, new String(password.getChars())); + } catch (UnsupportedSecretTypeException e) { + return false; + } + } + } + + @Override + protected void doAddUser(String userName, Object credential, String[] roleList, Map claims, String profileName, boolean requirePasswordChange) throws org.wso2.micro.integrator.security.user.core.UserStoreException { + + } + + @Override + protected void doUpdateCredential(String userName, Object newCredential, Object oldCredential) + throws org.wso2.micro.integrator.security.user.core.UserStoreException { + } + + @Override + protected void doUpdateCredentialByAdmin(String userName, Object newCredential) + throws org.wso2.micro.integrator.security.user.core.UserStoreException { + } + + @Override + protected void doDeleteUser(String userName) + throws org.wso2.micro.integrator.security.user.core.UserStoreException { + } + + @Override + protected void doSetUserClaimValue(String userName, String claimURI, String claimValue, String profileName) + throws org.wso2.micro.integrator.security.user.core.UserStoreException { + + } + + @Override + protected void doSetUserClaimValues(String userName, Map claims, String profileName) + throws org.wso2.micro.integrator.security.user.core.UserStoreException { + } + + @Override + protected void doDeleteUserClaimValue(String userName, String claimURI, String profileName) + throws org.wso2.micro.integrator.security.user.core.UserStoreException { + } + + @Override + protected void doDeleteUserClaimValues(String userName, String[] claims, String profileName) + throws org.wso2.micro.integrator.security.user.core.UserStoreException { + } + + @Override + protected void doUpdateUserListOfRole(String roleName, String[] deletedUsers, String[] newUsers) + throws org.wso2.micro.integrator.security.user.core.UserStoreException { + + } + + @Override + protected void doUpdateRoleListOfUser(String userName, String[] deletedRoles, String[] newRoles) + throws org.wso2.micro.integrator.security.user.core.UserStoreException { + + } + + @Override + protected String[] doGetExternalRoleListOfUser(String userName, String filter) + throws org.wso2.micro.integrator.security.user.core.UserStoreException { + return new String[0]; + } + + @Override + protected String[] doGetSharedRoleListOfUser(String userName, String tenantDomain, String filter) + throws org.wso2.micro.integrator.security.user.core.UserStoreException { + return new String[0]; + } + + @Override + protected void doAddRole(String roleName, String[] userList, boolean shared) + throws org.wso2.micro.integrator.security.user.core.UserStoreException { + + } + + @Override + protected void doDeleteRole(String roleName) + throws org.wso2.micro.integrator.security.user.core.UserStoreException { + + } + + @Override + protected void doUpdateRoleName(String roleName, String newRoleName) + throws org.wso2.micro.integrator.security.user.core.UserStoreException { + + } + + @Override + protected String[] doGetRoleNames(String filter, int maxItemLimit) + throws org.wso2.micro.integrator.security.user.core.UserStoreException { + // admin is the only role supported in file based user store + return new String[]{"admin"}; + } + + @Override + protected String[] doListUsers(String filter, int maxItemLimit) + throws org.wso2.micro.integrator.security.user.core.UserStoreException { + return usersList.keySet().toArray(new String[0]); + } + + @Override + protected String[] doGetDisplayNamesForInternalRole(String[] userNames) + throws org.wso2.micro.integrator.security.user.core.UserStoreException { + return new String[0]; + } + + @Override + public boolean doCheckIsUserInRole(String userName, String roleName) + throws org.wso2.micro.integrator.security.user.core.UserStoreException { + return false; + } + + @Override + protected String[] doGetSharedRoleNames(String tenantDomain, String filter, int maxItemLimit) + throws org.wso2.micro.integrator.security.user.core.UserStoreException { + return new String[0]; + } + + @Override + protected String[] doGetUserListOfRole(String roleName, String filter) + throws org.wso2.micro.integrator.security.user.core.UserStoreException { + return new String[0]; + } + + + public FileBasedUserStoreManager(RealmConfiguration realmConfig, Map properties, + ClaimManager claimManager, ProfileConfigurationManager profileManager, + UserRealm realm, Integer tenantId) + throws org.wso2.micro.integrator.security.user.core.UserStoreException { + this(realmConfig, tenantId); + this.claimManager = claimManager; + this.userRealm = realm; + doInitialSetup(true); + } + + public FileBasedUserStoreManager(RealmConfiguration realmConfig, int tenantId) + throws org.wso2.micro.integrator.security.user.core.UserStoreException { + this.realmConfig = realmConfig; + this.tenantId = tenantId; + realmConfig.setUserStoreProperties(JDBCRealmUtil.getSQL(realmConfig + .getUserStoreProperties())); + + // new properties after carbon core 4.0.7 release. + if (realmConfig.getUserStoreProperty(UserCoreConstants.RealmConfig.READ_GROUPS_ENABLED) != null) { + readGroupsEnabled = Boolean.parseBoolean(realmConfig + .getUserStoreProperty(UserCoreConstants.RealmConfig.READ_GROUPS_ENABLED)); + } + + if (realmConfig.getUserStoreProperty(UserCoreConstants.RealmConfig.WRITE_GROUPS_ENABLED) != null) { + writeGroupsEnabled = Boolean.parseBoolean(realmConfig + .getUserStoreProperty(UserCoreConstants.RealmConfig.WRITE_GROUPS_ENABLED)); + } else { + if (!isReadOnly()) { + writeGroupsEnabled = true; + } + } + + // This property is now deprecated + if (realmConfig + .getUserStoreProperty(UserCoreConstants.RealmConfig.PROPERTY_INTERNAL_ROLES_ONLY) != null) { + boolean internalRolesOnly = Boolean + .parseBoolean(realmConfig + .getUserStoreProperty(UserCoreConstants.RealmConfig.PROPERTY_INTERNAL_ROLES_ONLY)); + if (internalRolesOnly) { + readGroupsEnabled = false; + writeGroupsEnabled = false; + } else { + readGroupsEnabled = true; + writeGroupsEnabled = true; + } + } + + if (writeGroupsEnabled) { + readGroupsEnabled = true; + } + + } + + /** + * Populates individual users. + * + * @param users the parent element of users + * @return map of users against UserInfo config + */ + private static Map populateUsers(OMElement users) { + HashMap userMap = new HashMap<>(); + if (users != null) { + @SuppressWarnings("unchecked") + Iterator usersIterator = users.getChildrenWithName(new QName(USER)); + if (usersIterator != null) { + while (usersIterator.hasNext()) { + OMElement userElement = usersIterator.next(); + OMElement userNameElement = userElement.getFirstChildWithName(new QName(USERNAME)); + OMElement passwordElement = userElement.getFirstChildWithName(new QName(PASSWORD)); + OMElement isAdminElement = userElement.getFirstChildWithName(new QName(IS_ADMIN)); + if (userNameElement != null && passwordElement != null) { + String userName = userNameElement.getText(); + if (userMap.containsKey(userName)) { + System.out.println("Error parsing the file based user store. User: " + userName + " defined " + + "more than once."); + } + boolean isAdmin = false; + if (isAdminElement != null) { + isAdmin = Boolean.parseBoolean(isAdminElement.getText().trim()); + } + userMap.put(userName, new UserInfoDTO(userName, + resolveSecret(passwordElement.getText()).toCharArray(), isAdmin)); + } + } + } + } + return userMap; + } + + /** + * Checks if the text is protected and returns decrypted text if protected, else returns the plain text + * + * @param text text to be resolved + * @return Decrypted text if protected else plain text + */ + private static String resolveSecret(String text) { + String alias = MiscellaneousUtil.getProtectedToken(text); + if (!StringUtils.isEmpty(alias)) { + if (secretResolver.isInitialized()) { + return MiscellaneousUtil.resolve(alias, secretResolver); + } + } + return text; + } +} diff --git a/components/org.wso2.micro.integrator.security/src/main/java/org/wso2/micro/integrator/security/user/core/hybrid/FileBasedHybridRoleManager.java b/components/org.wso2.micro.integrator.security/src/main/java/org/wso2/micro/integrator/security/user/core/hybrid/FileBasedHybridRoleManager.java new file mode 100644 index 0000000000..9e223ba539 --- /dev/null +++ b/components/org.wso2.micro.integrator.security/src/main/java/org/wso2/micro/integrator/security/user/core/hybrid/FileBasedHybridRoleManager.java @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2023, WSO2 LLC. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 LLC. licenses this file to you under the Apache 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://www.apache.org/licenses/LICENSE-2.0 + * + * 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.wso2.micro.integrator.security.user.core.hybrid; + +import org.wso2.micro.integrator.security.user.api.RealmConfiguration; +import org.wso2.micro.integrator.security.user.core.UserRealm; +import org.wso2.micro.integrator.security.user.core.UserStoreException; +import org.wso2.micro.integrator.security.user.core.file.FileBasedUserStoreManager; + +import javax.sql.DataSource; + +/** + * RoleManager implementation for the file based user store. + */ +public class FileBasedHybridRoleManager extends HybridRoleManager { + public FileBasedHybridRoleManager(DataSource dataSource, int tenantId, RealmConfiguration realmConfig, + UserRealm realm) { + super(dataSource, tenantId, realmConfig, realm); + } + + /** + * Get the list of roles of a user + * + * @param userName user name + * @param filter filter + * @return list of roles + * @throws UserStoreException if an error occurs + */ + public String[] getHybridRoleListOfUser(String userName, String filter) throws UserStoreException { + FileBasedUserStoreManager userStoreManager = FileBasedUserStoreManager.getUserStoreManager(); + if (userStoreManager.isUserExists(userName)) { + if (userStoreManager.isAdmin(userName)) { + return new String[]{"admin", "Internal/everyone"}; + } else { + return new String[]{"Internal/everyone"}; + } + } else { + return new String[0]; + } + } +} diff --git a/components/org.wso2.micro.integrator.security/src/main/java/org/wso2/micro/integrator/security/user/core/jdbc/JDBCUserStoreManager.java b/components/org.wso2.micro.integrator.security/src/main/java/org/wso2/micro/integrator/security/user/core/jdbc/JDBCUserStoreManager.java index eed623fbac..15612cf905 100644 --- a/components/org.wso2.micro.integrator.security/src/main/java/org/wso2/micro/integrator/security/user/core/jdbc/JDBCUserStoreManager.java +++ b/components/org.wso2.micro.integrator.security/src/main/java/org/wso2/micro/integrator/security/user/core/jdbc/JDBCUserStoreManager.java @@ -23,6 +23,7 @@ import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.snmp4j.User; +import org.wso2.config.mapper.ConfigParser; import org.wso2.micro.core.Constants; import org.wso2.micro.core.util.DatabaseCreator; import org.wso2.micro.integrator.security.UnsupportedSecretTypeException; @@ -37,6 +38,7 @@ import org.wso2.micro.integrator.security.user.core.common.PaginatedSearchResult; import org.wso2.micro.integrator.security.user.core.common.RoleContext; import org.wso2.micro.integrator.security.user.core.dto.RoleDTO; +import org.wso2.micro.integrator.security.user.core.hybrid.FileBasedHybridRoleManager; import org.wso2.micro.integrator.security.user.core.hybrid.HybridJDBCConstants; import org.wso2.micro.integrator.security.user.core.jdbc.caseinsensitive.JDBCCaseInsensitiveConstants; import org.wso2.micro.integrator.security.user.core.model.Condition; @@ -99,11 +101,17 @@ public class JDBCUserStoreManager extends AbstractUserStoreManager { private static final String MSSQL = "mssql"; private static final String ORACLE = "oracle"; private static final String MYSQL = "mysql"; - + private boolean fileBasedUserStoreMode = false; public JDBCUserStoreManager() { } + private void checkFileBasedUserStoreEnabled() throws UserStoreException { + Object fileUserStore = ConfigParser.getParsedConfigs().get(UserCoreConstants.FILE_BASED_USER_STORE_AS_PRIMARY); + if (fileUserStore != null && Boolean.parseBoolean(fileUserStore.toString())) { + fileBasedUserStoreMode = true; + } + } /** * @param realmConfig * @param tenantId @@ -194,14 +202,17 @@ public JDBCUserStoreManager(DataSource ds, RealmConfiguration realmConfig, int t this.jdbcds = ds; this.dataSource = ds; - if (dataSource == null) { - dataSource = DatabaseUtil.getRealmDataSource(realmConfig); - } - if (dataSource == null) { - throw new UserStoreException("User Management Data Source is null"); + checkFileBasedUserStoreEnabled(); + if (!fileBasedUserStoreMode) { + if (dataSource == null) { + dataSource = DatabaseUtil.getRealmDataSource(realmConfig); + } + if (dataSource == null) { + throw new UserStoreException("User Management Data Source is null"); + } + this.persistDomain(); } - doInitialSetup(); - this.persistDomain(); + doInitialSetup(fileBasedUserStoreMode); if (addInitData && realmConfig.isPrimary()) { addInitialAdminData(Boolean.parseBoolean(realmConfig.getAddAdmin()), !isInitSetupDone()); @@ -284,22 +295,25 @@ public JDBCUserStoreManager(RealmConfiguration realmConfig, Map } } - dataSource = (DataSource) properties.get(UserCoreConstants.DATA_SOURCE); - if (dataSource == null) { - dataSource = DatabaseUtil.getRealmDataSource(realmConfig); - } - if (dataSource == null) { - throw new UserStoreException("User Management Data Source is null"); + checkFileBasedUserStoreEnabled(); + if (!fileBasedUserStoreMode) { + dataSource = (DataSource) properties.get(UserCoreConstants.DATA_SOURCE); + if (dataSource == null) { + dataSource = DatabaseUtil.getRealmDataSource(realmConfig); + } + if (dataSource == null) { + throw new UserStoreException("User Management Data Source is null"); + } + properties.put(UserCoreConstants.DATA_SOURCE, dataSource); } - properties.put(UserCoreConstants.DATA_SOURCE, dataSource); - - realmConfig.setUserStoreProperties(JDBCRealmUtil.getSQL(realmConfig .getUserStoreProperties())); - this.persistDomain(); - doInitialSetup(); + if (!fileBasedUserStoreMode) { + this.persistDomain(); + } + doInitialSetup(fileBasedUserStoreMode); if (!skipInitData && realmConfig.isPrimary()) { addInitialAdminData(Boolean.parseBoolean(realmConfig.getAddAdmin()), !isInitSetupDone()); diff --git a/components/org.wso2.micro.integrator.security/src/main/java/org/wso2/micro/integrator/security/user/core/ldap/ReadOnlyLDAPUserStoreManager.java b/components/org.wso2.micro.integrator.security/src/main/java/org/wso2/micro/integrator/security/user/core/ldap/ReadOnlyLDAPUserStoreManager.java index 234f19fcff..0706e5ceb3 100644 --- a/components/org.wso2.micro.integrator.security/src/main/java/org/wso2/micro/integrator/security/user/core/ldap/ReadOnlyLDAPUserStoreManager.java +++ b/components/org.wso2.micro.integrator.security/src/main/java/org/wso2/micro/integrator/security/user/core/ldap/ReadOnlyLDAPUserStoreManager.java @@ -22,6 +22,7 @@ import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.wso2.config.mapper.ConfigParser; import org.wso2.micro.core.Constants; import org.wso2.micro.integrator.security.UnsupportedSecretTypeException; import org.wso2.micro.integrator.security.user.core.UserCoreConstants; @@ -154,6 +155,8 @@ public class ReadOnlyLDAPUserStoreManager extends AbstractUserStoreManager { */ protected boolean emptyRolesAllowed = false; + private boolean fileBasedUserStoreMode = false; + static { setAdvancedProperties(); } @@ -202,17 +205,22 @@ public ReadOnlyLDAPUserStoreManager(RealmConfiguration realmConfig, // check if required configurations are in the user-mgt.xml checkRequiredUserStoreConfigurations(); - - dataSource = (DataSource) properties.get(UserCoreConstants.DATA_SOURCE); - if (dataSource == null) { - // avoid returning null - dataSource = DatabaseUtil.getRealmDataSource(realmConfig); + Object fileUserStore = ConfigParser.getParsedConfigs().get(UserCoreConstants.FILE_BASED_USER_STORE_AS_PRIMARY); + if (fileUserStore != null && Boolean.parseBoolean(fileUserStore.toString())) { + fileBasedUserStoreMode = true; } - if (dataSource == null) { - throw new UserStoreException("Data Source is null"); - } - properties.put(UserCoreConstants.DATA_SOURCE, dataSource); + if(!fileBasedUserStoreMode) { + dataSource = (DataSource) properties.get(UserCoreConstants.DATA_SOURCE); + if (dataSource == null) { + // avoid returning null + dataSource = DatabaseUtil.getRealmDataSource(realmConfig); + } + if (dataSource == null) { + throw new UserStoreException("Data Source is null"); + } + properties.put(UserCoreConstants.DATA_SOURCE, dataSource); + } /* * obtain the ldap connection source that was created in * DefaultRealmService. @@ -234,8 +242,11 @@ public ReadOnlyLDAPUserStoreManager(RealmConfiguration realmConfig, JNDIUtil.closeContext(dirContext); } this.userRealm = realm; - this.persistDomain(); - doInitialSetup(); + + if (!fileBasedUserStoreMode) { + this.persistDomain(); + } + doInitialSetup(fileBasedUserStoreMode); if (realmConfig.isPrimary()) { addInitialAdminData(Boolean.parseBoolean(realmConfig.getAddAdmin()), !isInitSetupDone()); diff --git a/components/org.wso2.micro.integrator.security/src/main/java/org/wso2/micro/integrator/security/user/core/ldap/ReadWriteLDAPUserStoreManager.java b/components/org.wso2.micro.integrator.security/src/main/java/org/wso2/micro/integrator/security/user/core/ldap/ReadWriteLDAPUserStoreManager.java index d28a938e06..f2880248ba 100644 --- a/components/org.wso2.micro.integrator.security/src/main/java/org/wso2/micro/integrator/security/user/core/ldap/ReadWriteLDAPUserStoreManager.java +++ b/components/org.wso2.micro.integrator.security/src/main/java/org/wso2/micro/integrator/security/user/core/ldap/ReadWriteLDAPUserStoreManager.java @@ -20,6 +20,7 @@ import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; +import org.wso2.config.mapper.ConfigParser; import org.wso2.micro.core.Constants; import org.wso2.micro.integrator.security.user.core.UserCoreConstants; import org.wso2.micro.integrator.security.user.api.Properties; @@ -30,6 +31,7 @@ import org.wso2.micro.integrator.security.user.core.UserStoreException; import org.wso2.micro.integrator.security.user.core.claim.ClaimManager; import org.wso2.micro.integrator.security.user.core.common.RoleContext; +import org.wso2.micro.integrator.security.user.core.hybrid.FileBasedHybridRoleManager; import org.wso2.micro.integrator.security.user.core.hybrid.HybridRoleManager; import org.wso2.micro.integrator.security.user.core.hybrid.JdbcHybridRoleManager; import org.wso2.micro.integrator.security.user.core.profile.ProfileConfigurationManager; @@ -102,6 +104,7 @@ public class ReadWriteLDAPUserStoreManager extends ReadOnlyLDAPUserStoreManager protected Random random = new Random(); protected boolean kdcEnabled = false; + private boolean fileBasedUserStoreMode = false; static { setAdvancedProperties(); @@ -130,22 +133,31 @@ public ReadWriteLDAPUserStoreManager(RealmConfiguration realmConfig, this.kdcEnabled = UserCoreUtil.isKdcEnabled(realmConfig); checkRequiredUserStoreConfigurations(); - - dataSource = (DataSource) properties.get(UserCoreConstants.DATA_SOURCE); - if (dataSource == null) { - // avoid returning null - dataSource = DatabaseUtil.getRealmDataSource(realmConfig); + Object fileUserStore = ConfigParser.getParsedConfigs().get(UserCoreConstants.FILE_BASED_USER_STORE_AS_PRIMARY); + if (fileUserStore != null && Boolean.parseBoolean(fileUserStore.toString())) { + fileBasedUserStoreMode = true; + hybridRoleManager = new FileBasedHybridRoleManager(dataSource, tenantId, realmConfig, userRealm); } - if (dataSource == null) { - throw new UserStoreException("Data Source is null"); + if (!fileBasedUserStoreMode) { + dataSource = (DataSource) properties.get(UserCoreConstants.DATA_SOURCE); + if (dataSource == null) { + // avoid returning null + dataSource = DatabaseUtil.getRealmDataSource(realmConfig); + } + if (dataSource == null) { + throw new UserStoreException("Data Source is null"); + } + properties.put(UserCoreConstants.DATA_SOURCE, dataSource); + + // hybrid role manager used if only users needs to be read-written. + hybridRoleManager = new JdbcHybridRoleManager(dataSource, tenantId, realmConfig, userRealm); + } else { + hybridRoleManager = new FileBasedHybridRoleManager(dataSource, tenantId, realmConfig, userRealm); } - properties.put(UserCoreConstants.DATA_SOURCE, dataSource); ReadWriteLDAPUserStoreManager.isFirstStartup = (Boolean) properties .get(UserCoreConstants.FIRST_STARTUP_CHECK); - // hybrid role manager used if only users needs to be read-written. - hybridRoleManager = new JdbcHybridRoleManager(dataSource, tenantId, realmConfig, userRealm); // obtain the ldap connection source that was created in // DefaultRealmService. @@ -169,9 +181,11 @@ public ReadWriteLDAPUserStoreManager(RealmConfiguration realmConfig, JNDIUtil.closeContext(dirContext); } this.userRealm = realm; - //persist domain - this.persistDomain(); - doInitialSetup(); + if (!fileBasedUserStoreMode) { + //persist domain + this.persistDomain(); + } + doInitialSetup(fileBasedUserStoreMode); if (realmConfig.isPrimary()) { addInitialAdminData(Boolean.parseBoolean(realmConfig.getAddAdmin()), !isInitSetupDone()); diff --git a/distribution/src/resources/config-tool/templates/conf/internal-apis.xml.j2 b/distribution/src/resources/config-tool/templates/conf/internal-apis.xml.j2 index b0428ebca0..5d1112abfb 100644 --- a/distribution/src/resources/config-tool/templates/conf/internal-apis.xml.j2 +++ b/distribution/src/resources/config-tool/templates/conf/internal-apis.xml.j2 @@ -18,7 +18,7 @@ - + {% if prometheus_api.basic_security_handler.enable == true