/* * JBoss, Home of Professional Open Source. * Copyright 2011, Red Hat, Inc., and individual contributors * as indicated by the @author tags. See the copyright.txt file in the * distribution for a full listing of individual contributors. * * This is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This software is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this software; if not, write to the Free * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA, or see the FSF site: http://www.fsf.org. */ package org.jboss.as.domain.management.security; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.ADVANCED_FILTER; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.AUTHENTICATION; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.AUTHORIZATION; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.GROUP_SEARCH; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.GROUP_TO_PRINCIPAL; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.LDAP; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.LOCAL; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.PATH; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.PRINCIPAL_TO_GROUP; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.PROPERTIES; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SECRET; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SERVER; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SERVER_IDENTITY; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SSL; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.TRUSTSTORE; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.USER; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.USERNAME_FILTER; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.USERNAME_IS_DN; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.USERNAME_TO_DN; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.USERS; import static org.jboss.as.domain.management.ModelDescriptionConstants.BY_ACCESS_TIME; import static org.jboss.as.domain.management.ModelDescriptionConstants.BY_SEARCH_TIME; import static org.jboss.as.domain.management.ModelDescriptionConstants.CACHE; import static org.jboss.as.domain.management.ModelDescriptionConstants.JAAS; import static org.jboss.as.domain.management.ModelDescriptionConstants.JKS; import static org.jboss.as.domain.management.ModelDescriptionConstants.KERBEROS; import static org.jboss.as.domain.management.ModelDescriptionConstants.KEYSTORE_PATH; import static org.jboss.as.domain.management.ModelDescriptionConstants.KEYTAB; import static org.jboss.as.domain.management.ModelDescriptionConstants.PASSWORD; import static org.jboss.as.domain.management.ModelDescriptionConstants.PLUG_IN; import static org.jboss.as.domain.management.ModelDescriptionConstants.PROPERTY; import static org.jboss.msc.service.ServiceController.Mode.ON_DEMAND; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import javax.net.ssl.SSLContext; import javax.net.ssl.TrustManager; import org.jboss.as.controller.OperationContext; import org.jboss.as.controller.OperationFailedException; import org.jboss.as.controller.OperationStepHandler; import org.jboss.as.controller.PathAddress; import org.jboss.as.controller.registry.Resource; import org.jboss.as.controller.services.path.PathManager; import org.jboss.as.controller.services.path.PathManagerService; import org.jboss.as.core.security.ServerSecurityManager; import org.jboss.as.domain.management.AuthMechanism; import org.jboss.as.domain.management.CallbackHandlerFactory; import org.jboss.as.domain.management.SecurityRealm; import org.jboss.as.domain.management.connections.ldap.LdapConnectionManagerService; import org.jboss.as.domain.management.security.BaseLdapGroupSearchResource.GroupName; import org.jboss.dmr.ModelNode; import org.jboss.dmr.ModelType; import org.jboss.dmr.Property; import org.jboss.msc.inject.Injector; import org.jboss.msc.service.ServiceBuilder; import org.jboss.msc.service.ServiceController; import org.jboss.msc.service.ServiceController.Mode; import org.jboss.msc.service.ServiceName; import org.jboss.msc.service.ServiceTarget; import org.jboss.msc.value.InjectedSetValue; import org.jboss.msc.value.InjectedValue; /** * Handler to add security realm definitions and register the service. * * @author <a href="mailto:darran.lofthouse@jboss.com">Darran Lofthouse</a> * @author Brian Stansberry (c) 2011 Red Hat Inc. */ public class SecurityRealmAddHandler implements OperationStepHandler { public static final SecurityRealmAddHandler INSTANCE = new SecurityRealmAddHandler(); @Override public void execute(OperationContext context, ModelNode operation) throws OperationFailedException { ModelNode model = context.createResource(PathAddress.EMPTY_ADDRESS).getModel(); SecurityRealmResourceDefinition.MAP_GROUPS_TO_ROLES.validateAndSet(operation, model); // Add a step validating that we have the correct authentication and authorization child resources ModelNode validationOp = AuthenticationValidatingHandler.createOperation(operation); context.addStep(validationOp, AuthenticationValidatingHandler.INSTANCE, OperationContext.Stage.MODEL); validationOp = AuthorizationValidatingHandler.createOperation(operation); context.addStep(validationOp, AuthorizationValidatingHandler.INSTANCE, OperationContext.Stage.MODEL); context.addStep(new OperationStepHandler() { @Override public void execute(OperationContext context, ModelNode operation) throws OperationFailedException { // Install another RUNTIME handler to actually install the services. This will run after the // RUNTIME handler for any child resources. Doing this will ensure that child resource handlers don't // see the installed services and can just ignore doing any RUNTIME stage work context.addStep(ServiceInstallStepHandler.INSTANCE, OperationContext.Stage.RUNTIME); context.completeStep(OperationContext.RollbackHandler.NOOP_ROLLBACK_HANDLER); } }, OperationContext.Stage.RUNTIME); context.completeStep(OperationContext.RollbackHandler.NOOP_ROLLBACK_HANDLER); } protected void installServices(final OperationContext context, final String realmName, final ModelNode model) throws OperationFailedException { final ModelNode plugIns = model.hasDefined(PLUG_IN) ? model.get(PLUG_IN) : null; final ModelNode authentication = model.hasDefined(AUTHENTICATION) ? model.get(AUTHENTICATION) : null; final ModelNode authorization = model.hasDefined(AUTHORIZATION) ? model.get(AUTHORIZATION) : null; final ModelNode serverIdentities = model.hasDefined(SERVER_IDENTITY) ? model.get(SERVER_IDENTITY) : null; final ServiceTarget serviceTarget = context.getServiceTarget(); final boolean mapGroupsToRoles = SecurityRealmResourceDefinition.MAP_GROUPS_TO_ROLES.resolveModelAttribute(context, model).asBoolean(); final SecurityRealmService securityRealmService = new SecurityRealmService(realmName, mapGroupsToRoles); final ServiceName realmServiceName = SecurityRealm.ServiceUtil.createServiceName(realmName); ServiceBuilder<?> realmBuilder = serviceTarget.addService(realmServiceName, securityRealmService); final ServiceName tmpDirPath = ServiceName.JBOSS.append("server", "path", "jboss.controller.temp.dir"); final boolean shareLdapConnections = shareLdapConnection(context, authentication, authorization); ModelNode authTruststore = null; if (plugIns != null) { addPlugInLoaderService(realmName, plugIns, serviceTarget); } InjectedSetValue<CallbackHandlerService> injectorSet = securityRealmService.getCallbackHandlerService(); if (authentication != null) { // Authentication can have a truststore defined at the same time as a username/password based mechanism. // // In this case it is expected certificate based authentication will first occur with a fallback to username/password // based authentication. if (authentication.hasDefined(TRUSTSTORE)) { authTruststore = authentication.require(TRUSTSTORE); addClientCertService(realmName, serviceTarget, realmBuilder, injectorSet.injector()); } if (authentication.hasDefined(LOCAL)) { addLocalService(context, authentication.require(LOCAL), realmName, serviceTarget, realmBuilder, injectorSet.injector()); } if (authentication.hasDefined(KERBEROS)) { addKerberosService(context, authentication.require(KERBEROS), realmName, serviceTarget, realmBuilder, injectorSet.injector()); } if (authentication.hasDefined(JAAS)) { addJaasService(context, authentication.require(JAAS), realmName, serviceTarget, context.isNormalServer(), realmBuilder, injectorSet.injector()); } else if (authentication.hasDefined(LDAP)) { addLdapService(context, authentication.require(LDAP), realmName, serviceTarget, realmBuilder, injectorSet.injector(), shareLdapConnections); } else if (authentication.hasDefined(PLUG_IN)) { addPlugInAuthenticationService(context, authentication.require(PLUG_IN), realmName, securityRealmService, serviceTarget, realmBuilder, injectorSet.injector()); } else if (authentication.hasDefined(PROPERTIES)) { addPropertiesAuthenticationService(context, authentication.require(PROPERTIES), realmName, serviceTarget, realmBuilder, injectorSet.injector()); } else if (authentication.hasDefined(USERS)) { addUsersService(context, authentication.require(USERS), realmName, serviceTarget, realmBuilder, injectorSet.injector()); } } if (authorization != null) { if (authorization.hasDefined(PROPERTIES)) { addPropertiesAuthorizationService(context, authorization.require(PROPERTIES), realmName, serviceTarget, realmBuilder, securityRealmService.getSubjectSupplementalInjector()); } else if (authorization.hasDefined(PLUG_IN)) { addPlugInAuthorizationService(context, authorization.require(PLUG_IN), realmName, serviceTarget, realmBuilder, securityRealmService.getSubjectSupplementalInjector()); } else if (authorization.hasDefined(LDAP)) { addLdapAuthorizationService(context, authorization.require(LDAP), realmName, serviceTarget, realmBuilder, securityRealmService.getSubjectSupplementalInjector(), shareLdapConnections); } } ModelNode ssl = null; if (serverIdentities != null) { if (serverIdentities.hasDefined(SSL)) { ssl = serverIdentities.require(SSL); } if (serverIdentities.hasDefined(SECRET)) { addSecretService(context, serverIdentities.require(SECRET), realmName,serviceTarget, realmBuilder, securityRealmService.getSecretCallbackFactory()); } if (serverIdentities.hasDefined(KERBEROS)) { addKerberosIdentityServices(context, serverIdentities.require(KERBEROS), realmName, serviceTarget, realmBuilder, securityRealmService.getKeytabIdentityFactoryInjector()); } } if (ssl != null || authTruststore != null) { addSSLServices(context, ssl, authTruststore, realmName, serviceTarget, realmBuilder, securityRealmService.getSSLContextInjector()); } realmBuilder.addDependency(tmpDirPath, String.class, securityRealmService.getTmpDirPathInjector()); realmBuilder.setInitialMode(Mode.ACTIVE); realmBuilder.install(); } private boolean shareLdapConnection(final OperationContext context, final ModelNode authentication, final ModelNode authorization) throws OperationFailedException { if (authentication == null || authorization == null || authentication.hasDefined(LDAP) == false || authorization.hasDefined(LDAP) == false) { return false; } String authConnectionManager = LdapAuthenticationResourceDefinition.CONNECTION.resolveModelAttribute(context, authentication.require(LDAP)).asString(); String authzConnectionManager = LdapAuthorizationResourceDefinition.CONNECTION.resolveModelAttribute(context, authorization.require(LDAP)).asString(); return authConnectionManager.equals(authzConnectionManager); } private ServiceName addPlugInLoaderService(String realmName, ModelNode plugInModel, ServiceTarget serviceTarget) { ServiceName plugInLoaderName = PlugInLoaderService.ServiceUtil.createServiceName(realmName); List<Property> plugIns = plugInModel.asPropertyList(); ArrayList<String> knownNames = new ArrayList<String>(plugIns.size()); for (Property current : plugIns) { knownNames.add(current.getName()); } PlugInLoaderService loaderService = new PlugInLoaderService(Collections.unmodifiableList(knownNames)); serviceTarget.addService(plugInLoaderName, loaderService) .setInitialMode(Mode.ON_DEMAND) .install(); return plugInLoaderName; } private void addClientCertService(String realmName, ServiceTarget serviceTarget, ServiceBuilder<?> realmBuilder, Injector<CallbackHandlerService> injector) { ServiceName clientCertServiceName = ClientCertCallbackHandler.ServiceUtil.createServiceName(realmName); ClientCertCallbackHandler clientCertCallbackHandler = new ClientCertCallbackHandler(); serviceTarget.addService(clientCertServiceName, clientCertCallbackHandler) .setInitialMode(ON_DEMAND) .install(); CallbackHandlerService.ServiceUtil.addDependency(realmBuilder, injector, clientCertServiceName, false); } private void addKerberosService(OperationContext context, ModelNode kerberos, String realmName, ServiceTarget serviceTarget, ServiceBuilder<?> realmBuilder, Injector<CallbackHandlerService> injector) throws OperationFailedException { ServiceName kerberosServiceName = KerberosCallbackHandler.ServiceUtil.createServiceName(realmName); boolean removeRealm = KerberosAuthenticationResourceDefinition.REMOVE_REALM.resolveModelAttribute(context, kerberos).asBoolean(); KerberosCallbackHandler kerberosCallbackHandler = new KerberosCallbackHandler(removeRealm); ServiceBuilder<?> ccBuilder = serviceTarget.addService(kerberosServiceName, kerberosCallbackHandler); ccBuilder.setInitialMode(ON_DEMAND).install(); CallbackHandlerService.ServiceUtil.addDependency(realmBuilder, injector, kerberosServiceName, false); } private void addJaasService(OperationContext context, ModelNode jaas, String realmName, ServiceTarget serviceTarget, boolean injectServerManager, ServiceBuilder<?> realmBuilder, Injector<CallbackHandlerService> injector) throws OperationFailedException { ServiceName jaasServiceName = JaasCallbackHandler.ServiceUtil.createServiceName(realmName); String name = JaasAuthenticationResourceDefinition.NAME.resolveModelAttribute(context, jaas).asString(); boolean assignGroups = JaasAuthenticationResourceDefinition.ASSIGN_GROUPS.resolveModelAttribute(context, jaas).asBoolean(); JaasCallbackHandler jaasCallbackHandler = new JaasCallbackHandler(realmName, name, assignGroups); ServiceBuilder<?> jaasBuilder = serviceTarget.addService(jaasServiceName, jaasCallbackHandler); if (injectServerManager) { jaasBuilder.addDependency(ServiceName.JBOSS.append("security", "simple-security-manager"), ServerSecurityManager.class, jaasCallbackHandler.getSecurityManagerValue()); } jaasBuilder.setInitialMode(ON_DEMAND).install(); CallbackHandlerService.ServiceUtil.addDependency(realmBuilder, injector, jaasServiceName, false); } private <R, K> LdapCacheService<R, K> createCacheService(OperationContext context, LdapSearcher<R, K> searcher, ModelNode cache) throws OperationFailedException { if (cache != null && cache.isDefined()) { ModelNode cacheDefinition = null; boolean byAccessTime = false; if (cache.hasDefined(BY_ACCESS_TIME)) { cacheDefinition = cache.require(BY_ACCESS_TIME); byAccessTime = true; } else if (cache.hasDefined(BY_SEARCH_TIME)) { cacheDefinition = cache.require(BY_SEARCH_TIME); } if (cacheDefinition != null) { int evictionTime = LdapCacheResourceDefinition.EVICTION_TIME.resolveModelAttribute(context, cacheDefinition).asInt(); boolean cacheFailures = LdapCacheResourceDefinition.CACHE_FAILURES.resolveModelAttribute(context, cacheDefinition) .asBoolean(); int maxSize = LdapCacheResourceDefinition.MAX_CACHE_SIZE.resolveModelAttribute(context, cacheDefinition).asInt(); return byAccessTime ? LdapCacheService.createByAccessCacheService(searcher, evictionTime, cacheFailures, maxSize) : LdapCacheService.createBySearchCacheService(searcher, evictionTime, cacheFailures, maxSize); } } return LdapCacheService.createNoCacheService(searcher); } private void addLdapService(OperationContext context, ModelNode ldap, String realmName, ServiceTarget serviceTarget, ServiceBuilder<?> realmBuilder, Injector<CallbackHandlerService> injector, boolean shareConnection) throws OperationFailedException { ServiceName ldapServiceName = UserLdapCallbackHandler.ServiceUtil.createServiceName(realmName); final String baseDn = LdapAuthenticationResourceDefinition.BASE_DN.resolveModelAttribute(context, ldap).asString(); ModelNode node = LdapAuthenticationResourceDefinition.USERNAME_FILTER.resolveModelAttribute(context, ldap); final String usernameAttribute = node.isDefined() ? node.asString() : null; node = LdapAuthenticationResourceDefinition.ADVANCED_FILTER.resolveModelAttribute(context, ldap); final String advancedFilter = node.isDefined() ? node.asString() : null; node = LdapAuthenticationResourceDefinition.USERNAME_LOAD.resolveModelAttribute(context, ldap); final String usernameLoad = node.isDefined() ? node.asString() : null; final boolean recursive = LdapAuthenticationResourceDefinition.RECURSIVE.resolveModelAttribute(context, ldap).asBoolean(); final boolean allowEmptyPasswords = LdapAuthenticationResourceDefinition.ALLOW_EMPTY_PASSWORDS.resolveModelAttribute(context, ldap).asBoolean(); final String userDn = LdapAuthenticationResourceDefinition.USER_DN.resolveModelAttribute(context, ldap).asString(); UserLdapCallbackHandler ldapCallbackHandler = new UserLdapCallbackHandler(allowEmptyPasswords, shareConnection); final LdapSearcher<LdapEntry, String> userSearcher; if (usernameAttribute != null) { userSearcher = LdapUserSearcherFactory.createForUsernameFilter(baseDn, recursive, userDn, usernameAttribute, usernameLoad); } else { userSearcher = LdapUserSearcherFactory.createForAdvancedFilter(baseDn, recursive, userDn, advancedFilter, usernameLoad); } final LdapCacheService<LdapEntry, String> cacheService = createCacheService(context, userSearcher, ldap.get(CACHE)); ServiceName userSearcherCacheName = LdapSearcherCache.ServiceUtil.createServiceName(true, true, realmName); serviceTarget.addService(userSearcherCacheName, cacheService).setInitialMode(ON_DEMAND).install(); ServiceBuilder<?> ldapBuilder = serviceTarget.addService(ldapServiceName, ldapCallbackHandler); String connectionManager = LdapAuthenticationResourceDefinition.CONNECTION.resolveModelAttribute(context, ldap).asString(); LdapConnectionManagerService.ServiceUtil.addDependency(ldapBuilder, ldapCallbackHandler.getConnectionManagerInjector(), connectionManager, false); LdapSearcherCache.ServiceUtil.addDependency(ldapBuilder, LdapSearcherCache.class, ldapCallbackHandler.getLdapUserSearcherInjector(), true, true, realmName); ldapBuilder.setInitialMode(ON_DEMAND).install(); CallbackHandlerService.ServiceUtil.addDependency(realmBuilder, injector, ldapServiceName, false); } private void addLocalService(OperationContext context, ModelNode local, String realmName, ServiceTarget serviceTarget, ServiceBuilder<?> realmBuilder, Injector<CallbackHandlerService> injector) throws OperationFailedException { ServiceName localServiceName = LocalCallbackHandlerService.ServiceUtil.createServiceName(realmName); ModelNode node = LocalAuthenticationResourceDefinition.DEFAULT_USER.resolveModelAttribute(context, local); String defaultUser = node.isDefined() ? node.asString() : null; node = LocalAuthenticationResourceDefinition.ALLOWED_USERS.resolveModelAttribute(context, local); String allowedUsers = node.isDefined() ? node.asString() : null; node = LocalAuthenticationResourceDefinition.SKIP_GROUP_LOADING.resolveModelAttribute(context, local); boolean skipGroupLoading = node.asBoolean(); LocalCallbackHandlerService localCallbackHandler = new LocalCallbackHandlerService(defaultUser, allowedUsers, skipGroupLoading); serviceTarget.addService(localServiceName, localCallbackHandler) .setInitialMode(ON_DEMAND) .install(); CallbackHandlerService.ServiceUtil.addDependency(realmBuilder, injector, localServiceName, false); } private void addPlugInAuthenticationService(OperationContext context, ModelNode model, String realmName, SecurityRealmService registry, ServiceTarget serviceTarget, ServiceBuilder<?> realmBuilder, Injector<CallbackHandlerService> injector) throws OperationFailedException { ServiceName plugInServiceName = PlugInAuthenticationCallbackHandler.ServiceUtil.createServiceName(realmName); final String pluginName = PlugInAuthorizationResourceDefinition.NAME.resolveModelAttribute(context, model).asString(); final Map<String, String> properties = resolveProperties(context, model); String mechanismName = PlugInAuthenticationResourceDefinition.MECHANISM.resolveModelAttribute(context, model).asString(); AuthMechanism mechanism = AuthMechanism.valueOf(mechanismName); PlugInAuthenticationCallbackHandler plugInService = new PlugInAuthenticationCallbackHandler(registry.getName(), pluginName, properties, mechanism); ServiceBuilder<CallbackHandlerService> plugInBuilder = serviceTarget.addService(plugInServiceName, plugInService); PlugInLoaderService.ServiceUtil.addDependency(plugInBuilder, plugInService.getPlugInLoaderServiceValue(), realmName, false); plugInBuilder.setInitialMode(ON_DEMAND).install(); CallbackHandlerService.ServiceUtil.addDependency(realmBuilder, injector, plugInServiceName, false); } private void addPropertiesAuthenticationService(OperationContext context, ModelNode properties, String realmName, ServiceTarget serviceTarget, ServiceBuilder<?> realmBuilder, Injector<CallbackHandlerService> injector) throws OperationFailedException { ServiceName propsServiceName = PropertiesCallbackHandler.ServiceUtil.createServiceName(realmName); final String path = PropertiesAuthenticationResourceDefinition.PATH.resolveModelAttribute(context, properties).asString(); final ModelNode relativeToNode = PropertiesAuthenticationResourceDefinition.RELATIVE_TO.resolveModelAttribute(context, properties); final boolean plainText = PropertiesAuthenticationResourceDefinition.PLAIN_TEXT.resolveModelAttribute(context, properties).asBoolean(); String relativeTo = relativeToNode.isDefined() ? relativeToNode.asString() : null; PropertiesCallbackHandler propsCallbackHandler = new PropertiesCallbackHandler(realmName, path, relativeTo, plainText); ServiceBuilder<?> propsBuilder = serviceTarget.addService(propsServiceName, propsCallbackHandler); if (relativeTo != null) { propsBuilder.addDependency(PathManagerService.SERVICE_NAME, PathManager.class, propsCallbackHandler.getPathManagerInjectorInjector()); propsBuilder.addDependency(pathName(relativeTo)); } propsBuilder.setInitialMode(ON_DEMAND) .install(); CallbackHandlerService.ServiceUtil.addDependency(realmBuilder, injector, propsServiceName, false); } private void addPropertiesAuthorizationService(OperationContext context, ModelNode properties, String realmName, ServiceTarget serviceTarget, ServiceBuilder<?> realmBuilder, InjectedValue<SubjectSupplementalService> injector) throws OperationFailedException { ServiceName propsServiceName = PropertiesSubjectSupplemental.ServiceUtil.createServiceName(realmName); final String path = PropertiesAuthorizationResourceDefinition.PATH.resolveModelAttribute(context, properties).asString(); final ModelNode relativeToNode = PropertiesAuthorizationResourceDefinition.RELATIVE_TO.resolveModelAttribute(context, properties); String relativeTo = relativeToNode.isDefined() ? relativeToNode.asString() : null; PropertiesSubjectSupplemental propsSubjectSupplemental = new PropertiesSubjectSupplemental(realmName, path, relativeTo); ServiceBuilder<?> propsBuilder = serviceTarget.addService(propsServiceName, propsSubjectSupplemental); if (relativeTo != null) { propsBuilder.addDependency(PathManagerService.SERVICE_NAME, PathManager.class, propsSubjectSupplemental.getPathManagerInjectorInjector()); propsBuilder.addDependency(pathName(relativeTo)); } propsBuilder.setInitialMode(ON_DEMAND).install(); SubjectSupplementalService.ServiceUtil.addDependency(realmBuilder, injector, propsServiceName, false); } private void addPlugInAuthorizationService(OperationContext context, ModelNode model, String realmName, ServiceTarget serviceTarget, ServiceBuilder<?> realmBuilder, InjectedValue<SubjectSupplementalService> injector) throws OperationFailedException { ServiceName plugInServiceName = PlugInSubjectSupplemental.ServiceUtil.createServiceName(realmName); final String pluginName = PlugInAuthorizationResourceDefinition.NAME.resolveModelAttribute(context, model).asString(); final Map<String, String> properties = resolveProperties(context, model); PlugInSubjectSupplemental plugInSubjectSupplemental = new PlugInSubjectSupplemental(realmName, pluginName, properties); ServiceBuilder<?> plugInBuilder = serviceTarget.addService(plugInServiceName, plugInSubjectSupplemental); PlugInLoaderService.ServiceUtil.addDependency(plugInBuilder, plugInSubjectSupplemental.getPlugInLoaderServiceValue(), realmName, false); plugInBuilder.setInitialMode(ON_DEMAND).install(); SubjectSupplementalService.ServiceUtil.addDependency(realmBuilder, injector, plugInServiceName, false); } private void addLdapAuthorizationService(OperationContext context, ModelNode ldap, String realmName, ServiceTarget serviceTarget, ServiceBuilder<?> realmBuilder, InjectedValue<SubjectSupplementalService> injector, boolean shareConnection) throws OperationFailedException { ServiceName ldapName = LdapSubjectSupplementalService.ServiceUtil.createServiceName(realmName); LdapSearcher<LdapEntry, String> userSearcher = null; boolean forceUserDnSearch = false; ModelNode userCache = null; if (ldap.hasDefined(USERNAME_TO_DN)) { ModelNode usernameToDn = ldap.require(USERNAME_TO_DN); if (usernameToDn.hasDefined(USERNAME_IS_DN)) { ModelNode usernameIsDn = usernameToDn.require(USERNAME_IS_DN); userCache = usernameIsDn.get(CACHE); forceUserDnSearch = UserIsDnResourceDefintion.FORCE.resolveModelAttribute(context, usernameIsDn).asBoolean(); userSearcher = LdapUserSearcherFactory.createForUsernameIsDn(); } else if (usernameToDn.hasDefined(USERNAME_FILTER)) { ModelNode usernameFilter = usernameToDn.require(USERNAME_FILTER); userCache = usernameFilter.get(CACHE); forceUserDnSearch = UserSearchResourceDefintion.FORCE.resolveModelAttribute(context, usernameFilter).asBoolean(); String baseDn = UserSearchResourceDefintion.BASE_DN.resolveModelAttribute(context, usernameFilter).asString(); boolean recursive = UserSearchResourceDefintion.RECURSIVE.resolveModelAttribute(context, usernameFilter).asBoolean(); String userDnAttribute = UserSearchResourceDefintion.USER_DN_ATTRIBUTE.resolveModelAttribute(context, usernameFilter).asString(); String usernameAttribute = UserSearchResourceDefintion.ATTRIBUTE.resolveModelAttribute(context, usernameFilter).asString(); userSearcher = LdapUserSearcherFactory.createForUsernameFilter(baseDn, recursive, userDnAttribute, usernameAttribute, null); } else if (usernameToDn.hasDefined(ADVANCED_FILTER)) { ModelNode advancedFilter = usernameToDn.require(ADVANCED_FILTER); userCache = advancedFilter.get(CACHE); forceUserDnSearch = AdvancedUserSearchResourceDefintion.FORCE.resolveModelAttribute(context, advancedFilter).asBoolean(); String baseDn = AdvancedUserSearchResourceDefintion.BASE_DN.resolveModelAttribute(context, advancedFilter).asString(); boolean recursive = AdvancedUserSearchResourceDefintion.RECURSIVE.resolveModelAttribute(context, advancedFilter).asBoolean(); String userDnAttribute = AdvancedUserSearchResourceDefintion.USER_DN_ATTRIBUTE.resolveModelAttribute(context, advancedFilter).asString(); String filter = AdvancedUserSearchResourceDefintion.FILTER.resolveModelAttribute(context, advancedFilter).asString(); userSearcher = LdapUserSearcherFactory.createForAdvancedFilter(baseDn, recursive, userDnAttribute, filter, null); } } if (userSearcher != null) { LdapCacheService<LdapEntry, String> userSearcherCache = createCacheService(context, userSearcher, userCache); ServiceName userSearcherCacheName = LdapSearcherCache.ServiceUtil.createServiceName(false, true, realmName); serviceTarget.addService(userSearcherCacheName, userSearcherCache).setInitialMode(ON_DEMAND).install(); } ModelNode groupSearch = ldap.require(GROUP_SEARCH); LdapSearcher<LdapEntry[], LdapEntry> groupSearcher; boolean iterative = false; GroupName groupName = GroupName.DISTINGUISHED_NAME; ModelNode groupCache = null; if (groupSearch.hasDefined(GROUP_TO_PRINCIPAL)) { ModelNode groupToPrincipal = groupSearch.require(GROUP_TO_PRINCIPAL); groupCache = groupToPrincipal.get(CACHE); String baseDn = GroupToPrincipalResourceDefinition.BASE_DN.resolveModelAttribute(context, groupToPrincipal).asString(); String groupDnAttribute = GroupToPrincipalResourceDefinition.GROUP_DN_ATTRIBUTE.resolveModelAttribute(context, groupToPrincipal).asString(); groupName = GroupName.valueOf(GroupToPrincipalResourceDefinition.GROUP_NAME.resolveModelAttribute(context, groupToPrincipal).asString()); String groupNameAttribute = GroupToPrincipalResourceDefinition.GROUP_NAME_ATTRIBUTE.resolveModelAttribute(context, groupToPrincipal).asString(); iterative = GroupToPrincipalResourceDefinition.ITERATIVE.resolveModelAttribute(context, groupToPrincipal).asBoolean(); String principalAttribute = GroupToPrincipalResourceDefinition.PRINCIPAL_ATTRIBUTE.resolveModelAttribute(context, groupToPrincipal).asString(); boolean recursive = GroupToPrincipalResourceDefinition.RECURSIVE.resolveModelAttribute(context, groupToPrincipal).asBoolean(); GroupName searchBy = GroupName.valueOf(GroupToPrincipalResourceDefinition.SEARCH_BY.resolveModelAttribute(context, groupToPrincipal).asString()); boolean preferOriginalConnection = GroupToPrincipalResourceDefinition.PREFER_ORIGINAL_CONNECTION.resolveModelAttribute(context, groupToPrincipal).asBoolean(); groupSearcher = LdapGroupSearcherFactory.createForGroupToPrincipal(baseDn, groupDnAttribute, groupNameAttribute, principalAttribute, recursive, searchBy, preferOriginalConnection); } else { ModelNode principalToGroup = groupSearch.require(PRINCIPAL_TO_GROUP); groupCache = principalToGroup.get(CACHE); String groupAttribute = PrincipalToGroupResourceDefinition.GROUP_ATTRIBUTE.resolveModelAttribute(context, principalToGroup).asString(); boolean preferOriginalConnection = PrincipalToGroupResourceDefinition.PREFER_ORIGINAL_CONNECTION.resolveModelAttribute(context, principalToGroup).asBoolean(); // TODO - Why was this never used? String groupDnAttribute = PrincipalToGroupResourceDefinition.GROUP_DN_ATTRIBUTE.resolveModelAttribute(context, principalToGroup).asString(); groupName = GroupName.valueOf(PrincipalToGroupResourceDefinition.GROUP_NAME.resolveModelAttribute(context, principalToGroup).asString()); String groupNameAttribute = PrincipalToGroupResourceDefinition.GROUP_NAME_ATTRIBUTE.resolveModelAttribute(context, principalToGroup).asString(); iterative = PrincipalToGroupResourceDefinition.ITERATIVE.resolveModelAttribute(context, principalToGroup).asBoolean(); boolean skipMissingGroups = PrincipalToGroupResourceDefinition.SKIP_MISSING_GROUPS.resolveModelAttribute(context, principalToGroup).asBoolean(); groupSearcher = LdapGroupSearcherFactory.createForPrincipalToGroup(groupAttribute, groupNameAttribute, preferOriginalConnection, skipMissingGroups, GroupName.SIMPLE == groupName); } LdapCacheService<LdapEntry[], LdapEntry> groupCacheService = createCacheService(context, groupSearcher, groupCache); ServiceName groupCacheServiceName = LdapSearcherCache.ServiceUtil.createServiceName(false, false, realmName); serviceTarget.addService(groupCacheServiceName, groupCacheService).setInitialMode(ON_DEMAND).install(); String connectionName = LdapAuthorizationResourceDefinition.CONNECTION.resolveModelAttribute(context, ldap).asString(); LdapSubjectSupplementalService service = new LdapSubjectSupplementalService(realmName, shareConnection, forceUserDnSearch, iterative, groupName); ServiceBuilder<SubjectSupplementalService> ldapBuilder = serviceTarget.addService(ldapName, service) .setInitialMode(ON_DEMAND); LdapConnectionManagerService.ServiceUtil.addDependency(ldapBuilder, service.getConnectionManagerInjector(), connectionName, false); if (userSearcher != null) { LdapSearcherCache.ServiceUtil.addDependency(ldapBuilder, LdapSearcherCache.class, service.getLdapUserSearcherInjector(), false, true, realmName); } LdapSearcherCache.ServiceUtil.addDependency(ldapBuilder, LdapSearcherCache.class, service.getLdapGroupSearcherInjector(), false, false, realmName); ldapBuilder.install(); SubjectSupplementalService.ServiceUtil.addDependency(realmBuilder, injector, ldapName, false); } private void addSSLServices(OperationContext context, ModelNode ssl, ModelNode trustStore, String realmName, ServiceTarget serviceTarget, ServiceBuilder<?> realmBuilder, InjectedValue<SSLContext> injector) throws OperationFailedException { // Use undefined structures for null ssl model ssl = (ssl == null) ? new ModelNode() : ssl; ServiceName keyManagerServiceName = null; final String provider = KeystoreAttributes.KEYSTORE_PROVIDER.resolveModelAttribute(context, ssl).asString(); if (ssl.hasDefined(KEYSTORE_PATH) || !JKS.equalsIgnoreCase(provider)) { keyManagerServiceName = AbstractKeyManagerService.ServiceUtil.createServiceName(SecurityRealm.ServiceUtil.createServiceName(realmName)); addKeyManagerService(context, ssl, keyManagerServiceName, serviceTarget); } ServiceName trustManagerServiceName = null; if (trustStore != null) { trustManagerServiceName = AbstractTrustManagerService.ServiceUtil.createServiceName(SecurityRealm.ServiceUtil.createServiceName(realmName)); addTrustManagerService(context, trustStore, trustManagerServiceName, serviceTarget); } String protocol = SSLServerIdentityResourceDefinition.PROTOCOL.resolveModelAttribute(context, ssl).asString(); // Enabled Cipher Suites final Set<String> enabledCipherSuites = new HashSet<String>(); ModelNode suitesNode = SSLServerIdentityResourceDefinition.ENABLED_CIPHER_SUITES.resolveModelAttribute(context, ssl); if (suitesNode.isDefined()) { List<ModelNode> list = suitesNode.asList(); for (ModelNode current : list) { enabledCipherSuites.add(current.asString()); } } // Enabled Protocols final Set<String> enabledProtocols = new HashSet<String>(); ModelNode protocolsNode = SSLServerIdentityResourceDefinition.ENABLED_PROTOCOLS.resolveModelAttribute(context, ssl); if (protocolsNode.isDefined()) { List<ModelNode> list = protocolsNode.asList(); for (ModelNode current : list) { enabledProtocols.add(current.asString()); } } /* * At this point we register two SSLContextService instances, one linked to both the key and trust store and the other just for trust. * * Subsequent dependencies will trigger which (or both) are actually started. */ ServiceName fullServiceName = SSLContextService.ServiceUtil.createServiceName(SecurityRealm.ServiceUtil.createServiceName(realmName), false); ServiceName trustOnlyServiceName = SSLContextService.ServiceUtil.createServiceName(SecurityRealm.ServiceUtil.createServiceName(realmName), true); if (keyManagerServiceName != null) { // An alias will not be set on the trust based SSLContext. SSLContextService fullSSLContextService = new SSLContextService(protocol, enabledCipherSuites, enabledProtocols); ServiceBuilder<SSLContext> fullBuilder = serviceTarget.addService(fullServiceName, fullSSLContextService); AbstractKeyManagerService.ServiceUtil.addDependency(fullBuilder, fullSSLContextService.getKeyManagerInjector(), SecurityRealm.ServiceUtil.createServiceName(realmName)); if (trustManagerServiceName != null) { AbstractTrustManagerService.ServiceUtil.addDependency(fullBuilder, fullSSLContextService.getTrustManagerInjector(), SecurityRealm.ServiceUtil.createServiceName(realmName)); } fullBuilder.setInitialMode(ON_DEMAND).install(); } // Always register this one - if no KeyStore is defined we can add an alias to this. SSLContextService trustOnlySSLContextService = new SSLContextService(protocol, enabledCipherSuites, enabledProtocols); ServiceBuilder<SSLContext> trustBuilder = serviceTarget.addService(trustOnlyServiceName, trustOnlySSLContextService); if (keyManagerServiceName == null) { // No KeyStore so just alias to this. trustBuilder.addAliases(fullServiceName); } if (trustManagerServiceName != null) { AbstractTrustManagerService.ServiceUtil.addDependency(trustBuilder, trustOnlySSLContextService.getTrustManagerInjector(), SecurityRealm.ServiceUtil.createServiceName(realmName)); } trustBuilder.setInitialMode(ON_DEMAND).install(); SSLContextService.ServiceUtil.addDependency(realmBuilder, injector, SecurityRealm.ServiceUtil.createServiceName(realmName), false); } private void addKeyManagerService(OperationContext context, ModelNode ssl, ServiceName serviceName, ServiceTarget serviceTarget) throws OperationFailedException { char[] keystorePassword = KeystoreAttributes.KEYSTORE_PASSWORD.resolveModelAttribute(context, ssl).asString().toCharArray(); final ServiceBuilder<AbstractKeyManagerService> serviceBuilder; String provider = KeystoreAttributes.KEYSTORE_PROVIDER.resolveModelAttribute(context, ssl).asString(); String autoGenerateCertHostName = null; ModelNode autoGenerateCertHostNode = KeystoreAttributes.GENERATE_SELF_SIGNED_CERTIFICATE_HOST.resolveModelAttribute(context, ssl); if(autoGenerateCertHostNode.isDefined()) { autoGenerateCertHostName = autoGenerateCertHostNode.asString(); } ModelNode pathNode = KeystoreAttributes.KEYSTORE_PATH.resolveModelAttribute(context, ssl); if (pathNode.isDefined() == false) { ProviderKeyManagerService keyManagerService = new ProviderKeyManagerService(provider, keystorePassword); serviceBuilder = serviceTarget.addService(serviceName, keyManagerService); } else { String path = pathNode.asString(); final char[] keyPassword; ModelNode pwordNode = KeystoreAttributes.KEY_PASSWORD.resolveModelAttribute(context, ssl); if (pwordNode.isDefined()) { keyPassword = pwordNode.asString().toCharArray(); } else { keyPassword = null; } ModelNode relativeToNode = KeystoreAttributes.KEYSTORE_RELATIVE_TO.resolveModelAttribute(context, ssl); String relativeTo = relativeToNode.isDefined() ? relativeToNode.asString() : null; ModelNode aliasNode = KeystoreAttributes.ALIAS.resolveModelAttribute(context, ssl); String alias = aliasNode.isDefined() ? aliasNode.asString() : null; FileKeyManagerService keyManagerService = new FileKeyManagerService(provider, path, relativeTo, keystorePassword, keyPassword, alias, autoGenerateCertHostName); serviceBuilder = serviceTarget.addService(serviceName, keyManagerService); if (relativeTo != null) { serviceBuilder.addDependency(PathManagerService.SERVICE_NAME, PathManager.class, keyManagerService.getPathManagerInjector()); serviceBuilder.addDependency(pathName(relativeTo)); } } serviceBuilder.setInitialMode(ON_DEMAND).install(); } private void addTrustManagerService(OperationContext context, ModelNode ssl, ServiceName serviceName, ServiceTarget serviceTarget) throws OperationFailedException { final ServiceBuilder<TrustManager[]> serviceBuilder; char[] keystorePassword = KeystoreAttributes.KEYSTORE_PASSWORD.resolveModelAttribute(context, ssl).asString() .toCharArray(); final String provider = KeystoreAttributes.KEYSTORE_PROVIDER.resolveModelAttribute(context, ssl).asString(); if (!JKS.equalsIgnoreCase(provider)) { final ProviderTrustManagerService trustManagerService = new ProviderTrustManagerService(provider, keystorePassword); serviceBuilder = serviceTarget.addService(serviceName, trustManagerService); } else { String path = KeystoreAttributes.KEYSTORE_PATH.resolveModelAttribute(context, ssl).asString(); ModelNode relativeToNode = KeystoreAttributes.KEYSTORE_RELATIVE_TO.resolveModelAttribute(context, ssl); String relativeTo = relativeToNode.isDefined() ? relativeToNode.asString() : null; FileTrustManagerService trustManagerService = new FileTrustManagerService(provider, path, relativeTo, keystorePassword); serviceBuilder = serviceTarget.addService(serviceName, trustManagerService); if (relativeTo != null) { serviceBuilder.addDependency(PathManagerService.SERVICE_NAME, PathManager.class, trustManagerService.getPathManagerInjector()); serviceBuilder.addDependency(pathName(relativeTo)); } } serviceBuilder.setInitialMode(ON_DEMAND).install(); } private void addSecretService(OperationContext context, ModelNode secret, String realmName, ServiceTarget serviceTarget, ServiceBuilder<?> realmBuilder, Injector<CallbackHandlerFactory> injector) throws OperationFailedException { ServiceName secretServiceName = SecretIdentityService.ServiceUtil.createServiceName(realmName); ModelNode resolvedValueNode = SecretServerIdentityResourceDefinition.VALUE.resolveModelAttribute(context, secret); boolean base64 = secret.get(SecretServerIdentityResourceDefinition.VALUE.getName()).getType() != ModelType.EXPRESSION; SecretIdentityService sis = new SecretIdentityService(resolvedValueNode.asString(), base64); serviceTarget.addService(secretServiceName, sis) .setInitialMode(ON_DEMAND) .install(); CallbackHandlerFactory.ServiceUtil.addDependency(realmBuilder, injector, secretServiceName, false); } private void addKerberosIdentityServices(OperationContext context, ModelNode kerberos, String realmName, ServiceTarget serviceTarget, ServiceBuilder<?> realmBuilder, Injector<KeytabIdentityFactoryService> injector) throws OperationFailedException { ServiceName keyIdentityName = KeytabIdentityFactoryService.ServiceUtil.createServiceName(realmName); KeytabIdentityFactoryService kifs = new KeytabIdentityFactoryService(); ServiceBuilder<KeytabIdentityFactoryService> kifsBuilder = serviceTarget.addService(keyIdentityName, kifs) .setInitialMode(ON_DEMAND); if (kerberos.hasDefined(KEYTAB)) { List<Property> keytabList = kerberos.get(KEYTAB).asPropertyList(); for (Property current : keytabList) { String principal = current.getName(); ModelNode keytab = current.getValue(); String path = KeytabResourceDefinition.PATH.resolveModelAttribute(context, keytab).asString(); ModelNode relativeToNode = KeytabResourceDefinition.RELATIVE_TO.resolveModelAttribute(context, keytab); String relativeTo = relativeToNode.isDefined() ? relativeToNode.asString() : null; boolean debug = KeytabResourceDefinition.DEBUG.resolveModelAttribute(context, keytab).asBoolean(); final String[] forHostsValues; ModelNode forHosts = KeytabResourceDefinition.FOR_HOSTS.resolveModelAttribute(context, keytab); if (forHosts.isDefined()) { List<ModelNode> list = forHosts.asList(); forHostsValues = new String[list.size()]; for (int i=0;i<list.size();i++) { forHostsValues[i] = list.get(i).asString(); } } else { forHostsValues = new String[0]; } ServiceName keytabName = KeytabService.ServiceUtil.createServiceName(realmName, principal); KeytabService ks = new KeytabService(principal, path, relativeTo, forHostsValues, debug); ServiceBuilder<KeytabService> keytabBuilder = serviceTarget.addService(keytabName, ks).setInitialMode(ON_DEMAND); if (relativeTo != null) { keytabBuilder.addDependency(PathManagerService.SERVICE_NAME, PathManager.class, ks.getPathManagerInjector()); keytabBuilder.addDependency(pathName(relativeTo)); } keytabBuilder.install(); KeytabService.ServiceUtil.addDependency(kifsBuilder, kifs.getKeytabInjector(), realmName, principal); } } kifsBuilder.install(); KeytabIdentityFactoryService.ServiceUtil.addDependency(realmBuilder, injector, realmName); } private void addUsersService(OperationContext context, ModelNode users, String realmName, ServiceTarget serviceTarget, ServiceBuilder<?> realmBuilder, Injector<CallbackHandlerService> injector) throws OperationFailedException { ServiceName usersServiceName = UserDomainCallbackHandler.ServiceUtil.createServiceName(realmName); UserDomainCallbackHandler usersCallbackHandler = new UserDomainCallbackHandler(realmName, unmaskUsersPasswords(context, users)); serviceTarget.addService(usersServiceName, usersCallbackHandler) .setInitialMode(ServiceController.Mode.ON_DEMAND) .install(); CallbackHandlerService.ServiceUtil.addDependency(realmBuilder, injector, usersServiceName, false); } private static ServiceName pathName(String relativeTo) { return ServiceName.JBOSS.append(SERVER, PATH, relativeTo); } private ModelNode unmaskUsersPasswords(OperationContext context, ModelNode users) throws OperationFailedException { users = users.clone(); for (Property property : users.get(USER).asPropertyList()) { // Don't use the value from property as it is a clone and does not update the returned users ModelNode. ModelNode user = users.get(USER, property.getName()); if (user.hasDefined(PASSWORD)) { //TODO This will be cleaned up once it uses attribute definitions user.set(PASSWORD, context.resolveExpressions(user.get(PASSWORD)).asString()); } } return users; } private static Map<String, String> resolveProperties( final OperationContext context, final ModelNode model) throws OperationFailedException { Map<String, String> configurationProperties; if (model.hasDefined(PROPERTY)) { List<Property> propertyList = model.require(PROPERTY).asPropertyList(); configurationProperties = new HashMap<String, String>(propertyList.size()); for (Property current : propertyList) { String propertyName = current.getName(); ModelNode valueNode = PropertyResourceDefinition.VALUE.resolveModelAttribute(context, current.getValue()); String value = valueNode.isDefined() ? valueNode.asString() : null; configurationProperties.put(propertyName, value); } configurationProperties = Collections.unmodifiableMap(configurationProperties); } else { configurationProperties = Collections.emptyMap(); } return configurationProperties; } private static class ServiceInstallStepHandler implements OperationStepHandler { private static final ServiceInstallStepHandler INSTANCE = new ServiceInstallStepHandler(); @Override public void execute(OperationContext context, ModelNode operation) throws OperationFailedException { final List<ServiceController<?>> newControllers = new ArrayList<ServiceController<?>>(); final String realmName = ManagementUtil.getSecurityRealmName(operation); final ModelNode model = Resource.Tools.readModel(context.readResource(PathAddress.EMPTY_ADDRESS)); SecurityRealmAddHandler.INSTANCE.installServices(context, realmName, model); context.completeStep(new OperationContext.RollbackHandler() { @Override public void handleRollback(OperationContext context, ModelNode operation) { for (ServiceController<?> sc : newControllers) { context.removeService(sc); } } }); } } }