diff --git a/controller/src/main/java/org/jboss/as/controller/descriptions/ModelDescriptionConstants.java b/controller/src/main/java/org/jboss/as/controller/descriptions/ModelDescriptionConstants.java index c1e6aa6c3381..475f918d4ee2 100644 --- a/controller/src/main/java/org/jboss/as/controller/descriptions/ModelDescriptionConstants.java +++ b/controller/src/main/java/org/jboss/as/controller/descriptions/ModelDescriptionConstants.java @@ -142,6 +142,7 @@ public class ModelDescriptionConstants { public static final String PRIORITY = "priority"; public static final String PROFILE = "profile"; public static final String PROFILE_NAME = "profile-name"; + public static final String PROPERTIES = "properties"; public static final String PROTOCOL = "protocol"; public static final String READ_ATTRIBUTE_OPERATION = "read-attribute"; public static final String READ_CHILDREN_NAMES_OPERATION = "read-children-names"; diff --git a/controller/src/main/java/org/jboss/as/controller/parsing/CommonXml.java b/controller/src/main/java/org/jboss/as/controller/parsing/CommonXml.java index e45288d7219b..f54d25baa72a 100644 --- a/controller/src/main/java/org/jboss/as/controller/parsing/CommonXml.java +++ b/controller/src/main/java/org/jboss/as/controller/parsing/CommonXml.java @@ -61,6 +61,7 @@ import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.PATH; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.PORT; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.PORT_OFFSET; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.PROPERTIES; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.PROTOCOL; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.RECURSIVE; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.RELATIVE_TO; @@ -692,14 +693,18 @@ protected void parseAuthentication(final XMLExtendedStreamReader reader, final M throw unexpectedElement(reader); } switch (element) { - case USERS: { - parseUsersAuthentication(reader, authentication); - break; - } case LDAP: { parseLdapAuthentication(reader, authentication); break; } + case PROPERTIES: { + parsePropertiesAuthentication(reader, authentication); + break; + } + case USERS: { + parseUsersAuthentication(reader, authentication); + break; + } default: { throw unexpectedElement(reader); } @@ -783,7 +788,46 @@ protected void parseLdapAuthentication(final XMLExtendedStreamReader reader, fin } } - // The user domain element defines users within the domain model, it is a simple authentication for some out of the box users. + protected void parsePropertiesAuthentication(final XMLExtendedStreamReader reader, final ModelNode authentication) throws XMLStreamException { + ModelNode properties = authentication.get(PROPERTIES); + + String path = null; + String relativeTo = null; + + final int count = reader.getAttributeCount(); + for (int i = 0; i < count; i++) { + final String value = reader.getAttributeValue(i); + if (!isNoNamespaceAttribute(reader, i)) { + throw unexpectedAttribute(reader, i); + } else { + final Attribute attribute = Attribute.forName(reader.getAttributeLocalName(i)); + switch (attribute) { + case PATH: + path = value; + break; + case RELATIVE_TO: { + relativeTo = value; + break; + } + default: { + throw unexpectedAttribute(reader, i); + } + } + } + } + + if (path == null) + throw missingRequired(reader, Collections.singleton(Attribute.PATH)); + + requireNoContent(reader); + + properties.get(PATH).set(path); + if (relativeTo != null) { + properties.get(RELATIVE_TO).set(relativeTo); + } + } + + // The users element defines users within the domain model, it is a simple authentication for some out of the box users. protected void parseUsersAuthentication(final XMLExtendedStreamReader reader, final ModelNode authentication) throws XMLStreamException { ModelNode userDomain = authentication.get(USERS); @@ -2289,6 +2333,15 @@ protected void writeManagement(final XMLExtendedStreamWriter writer, final Model if (userLdap.hasDefined(USER_DN)) { writer.writeAttribute(Attribute.USER_DN.getLocalName(), userLdap.require(USER_DN).asString()); } + writer.writeEndElement(); + } else if (authentication.hasDefined(PROPERTIES)) { + ModelNode properties = authentication.require(PROPERTIES); + writer.writeStartElement(Element.PROPERTIES.getLocalName()); + writer.writeAttribute(Attribute.PATH.getLocalName(), properties.require(PATH).asString()); + if (properties.hasDefined(RELATIVE_TO)) { + writer.writeAttribute(Attribute.RELATIVE_TO.getLocalName(), properties.require(RELATIVE_TO).asString()); + } + writer.writeEndElement(); } diff --git a/controller/src/main/java/org/jboss/as/controller/parsing/Element.java b/controller/src/main/java/org/jboss/as/controller/parsing/Element.java index 18f5a7d9f0bc..55e36a915522 100644 --- a/controller/src/main/java/org/jboss/as/controller/parsing/Element.java +++ b/controller/src/main/java/org/jboss/as/controller/parsing/Element.java @@ -104,6 +104,7 @@ public enum Element { PROFILE("profile"), PROFILES("profiles"), PROPERTY("property"), + PROPERTIES("properties"), PUBLIC_ADDRESS("public-address"), REMOTE("remote"), diff --git a/controller/src/main/resources/schema/jboss_7_0.xsd b/controller/src/main/resources/schema/jboss_7_0.xsd index e4d3f1730936..6904048a6890 100644 --- a/controller/src/main/resources/schema/jboss_7_0.xsd +++ b/controller/src/main/resources/schema/jboss_7_0.xsd @@ -281,8 +281,9 @@ - + + @@ -366,6 +367,31 @@ + + + + Declaration of users stored within properties files. + + + + + + The name of another previously named path, or of one of the + standard paths provided by the system. If 'relative-to' is + provided, the value of the 'path' attribute is treated as + relative to the path specified by this attribute. + + + + + + + The path of the properties file containing the users. + + + + + diff --git a/domain-http-api/src/main/java/org/jboss/as/domain/http/server/ConsoleHandler.java b/domain-http-api/src/main/java/org/jboss/as/domain/http/server/ConsoleHandler.java index ab805b0595f1..22c9698ae2b4 100644 --- a/domain-http-api/src/main/java/org/jboss/as/domain/http/server/ConsoleHandler.java +++ b/domain-http-api/src/main/java/org/jboss/as/domain/http/server/ConsoleHandler.java @@ -81,7 +81,6 @@ public ConsoleHandler(ClassLoader loader) { this.loader = loader; } - @Override public void handle(HttpExchange http) throws IOException { final URI uri = http.getRequestURI(); final String requestMethod = http.getRequestMethod(); @@ -185,12 +184,10 @@ private ClassLoader getLoader() { return ConsoleHandler.class.getClassLoader(); } - @Override public void start(HttpServer httpServer, SecurityRealm securityRealm) { httpServer.createContext(CONTEXT, this); } - @Override public void stop(HttpServer httpServer) { httpServer.removeContext(CONTEXT); } diff --git a/domain-management/src/main/java/org/jboss/as/domain/management/operations/SecurityRealmAddHandler.java b/domain-management/src/main/java/org/jboss/as/domain/management/operations/SecurityRealmAddHandler.java index 4273b1b9a61a..4c65f2ee0350 100644 --- a/domain-management/src/main/java/org/jboss/as/domain/management/operations/SecurityRealmAddHandler.java +++ b/domain-management/src/main/java/org/jboss/as/domain/management/operations/SecurityRealmAddHandler.java @@ -26,9 +26,11 @@ import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.KEYSTORE; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.LDAP; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP_ADDR; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.PROPERTIES; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.RELATIVE_TO; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SERVER_IDENTITIES; import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SSL; +import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.USERS; import java.util.Locale; @@ -44,9 +46,13 @@ import org.jboss.as.controller.descriptions.DescriptionProvider; import org.jboss.as.controller.descriptions.ModelDescriptionConstants; import org.jboss.as.domain.management.connections.ConnectionManager; +import org.jboss.as.domain.management.security.DomainCallbackHandler; import org.jboss.as.domain.management.security.LdapConnectionManagerService; +import org.jboss.as.domain.management.security.PropertiesCallbackHandler; import org.jboss.as.domain.management.security.SSLIdentityService; import org.jboss.as.domain.management.security.SecurityRealmService; +import org.jboss.as.domain.management.security.UserDomainCallbackHandler; +import org.jboss.as.domain.management.security.UserLdapCallbackHandler; import org.jboss.dmr.ModelNode; import org.jboss.msc.service.ServiceBuilder; import org.jboss.msc.service.ServiceController; @@ -81,65 +87,122 @@ public OperationResult execute(OperationContext context, ModelNode operation, fi final ModelNode compensatingOperation = new ModelNode(); // TODO - Complete the remove. if (context.getRuntimeContext() != null) { - context.getRuntimeContext().setRuntimeTask(new RuntimeTask() { - public void execute(RuntimeTaskContext context) throws OperationFailedException { - final ServiceTarget serviceTarget = context.getServiceTarget(); + context.getRuntimeContext().setRuntimeTask(new SecurityRealmAddTask(securityRealm, serverIdentities, authentication)); + } else { + resultHandler.handleResultComplete(); + } + return new BasicOperationResult(compensatingOperation); + } - final SecurityRealmService securityRealmService = new SecurityRealmService(securityRealm, authentication); - ServiceName realmServiceName = SecurityRealmService.BASE_SERVICE_NAME.append(securityRealm); - ServiceBuilder realmBuilder = serviceTarget.addService(realmServiceName, securityRealmService); + // TODO - These need to be split into more fine grained services so allow service specific checks. - String connectionManager = requiredConnectionManager(authentication); - if (connectionManager != null) { - realmBuilder.addDependency(LdapConnectionManagerService.BASE_SERVICE_NAME.append(connectionManager), ConnectionManager.class, securityRealmService.getConnectionManagerInjector()); - } - // TODO - The operation will also be split out into it's own handler when I make the operations more fine grained. - if (serverIdentities != null && serverIdentities.has(SSL)) { - ModelNode ssl = serverIdentities.require(SSL); - ServiceName sslServiceName = realmServiceName.append("ssl"); - SSLIdentityService sslIdentityService = new SSLIdentityService(ssl); + public ModelNode getModelDescription(Locale locale) { + // TODO - Complete getModelDescription() + return new ModelNode(); + } +} - ServiceBuilder sslBuilder = serviceTarget.addService(sslServiceName, sslIdentityService); +class SecurityRealmAddTask implements RuntimeTask { - if (ssl.hasDefined(KEYSTORE) && ssl.get(KEYSTORE).hasDefined(RELATIVE_TO)) { - sslBuilder.addDependency(pathName(ssl.get(KEYSTORE, RELATIVE_TO).asString()), String.class, sslIdentityService.getRelativeToInjector()); - } + final String realmName; + final ModelNode serverIdentities; + final ModelNode authentication; - sslBuilder.setInitialMode(ServiceController.Mode.ON_DEMAND) - .install(); + SecurityRealmAddTask(String realmName, ModelNode serverIdentities, ModelNode authentication) { + this.realmName = realmName; + this.serverIdentities = serverIdentities; + this.authentication = authentication; + } - realmBuilder.addDependency(sslServiceName, SSLIdentityService.class, securityRealmService.getSSLIdentityInjector()); - } + public void execute(RuntimeTaskContext context) throws OperationFailedException { + final ServiceTarget serviceTarget = context.getServiceTarget(); - realmBuilder.setInitialMode(ServiceController.Mode.ON_DEMAND) - .install(); - } - }); - } else { - resultHandler.handleResultComplete(); + final SecurityRealmService securityRealmService = new SecurityRealmService(realmName); + final ServiceName realmServiceName = SecurityRealmService.BASE_SERVICE_NAME.append(realmName); + ServiceBuilder realmBuilder = serviceTarget.addService(realmServiceName, securityRealmService); + + ServiceName authenticationName = null; + if (authentication.hasDefined(LDAP)) { + authenticationName = addLdapService(authentication.require(LDAP), realmServiceName, serviceTarget); + } else if (authentication.hasDefined(PROPERTIES)) { + authenticationName = addPropertiesService(authentication.require(PROPERTIES), realmServiceName, realmName, serviceTarget); + } else if (authentication.hasDefined(USERS)) { + authenticationName = addUsersService(authentication.require(USERS), realmServiceName, realmName, serviceTarget); } - return new BasicOperationResult(compensatingOperation); + if (authenticationName != null) { + realmBuilder.addDependency(authenticationName, DomainCallbackHandler.class, securityRealmService.getCallbackHandlerInjector()); + } + + if (serverIdentities != null && serverIdentities.hasDefined(SSL)) { + ServiceName sslServiceName = addSSLService(serverIdentities.require(SSL), realmServiceName, serviceTarget); + realmBuilder.addDependency(sslServiceName, SSLIdentityService.class, securityRealmService.getSSLIdentityInjector()); + } + + realmBuilder.setInitialMode(ServiceController.Mode.ON_DEMAND) + .install(); } - // TODO - These need to be split into more fine grained services so allow service specific checks. + private ServiceName addLdapService(ModelNode ldap, ServiceName realmServiceName, ServiceTarget serviceTarget) { + ServiceName ldapServiceName = realmServiceName.append(UserLdapCallbackHandler.SERVICE_SUFFIX); + UserLdapCallbackHandler ldapCallbackHandler = new UserLdapCallbackHandler(ldap); + + ServiceBuilder ldapBuilder = serviceTarget.addService(ldapServiceName, ldapCallbackHandler); + String connectionManager = ldap.require(CONNECTION).asString(); + ldapBuilder.addDependency(LdapConnectionManagerService.BASE_SERVICE_NAME.append(connectionManager), ConnectionManager.class, ldapCallbackHandler.getConnectionManagerInjector()); + + ldapBuilder.setInitialMode(ServiceController.Mode.ON_DEMAND) + .install(); - private static String requiredConnectionManager(ModelNode authentication) { - String connectionManager = null; - if (authentication != null && authentication.hasDefined(LDAP)) { - ModelNode userLdap = authentication.require(LDAP); - connectionManager = userLdap.require(CONNECTION).asString(); + return ldapServiceName; + } + + private ServiceName addPropertiesService(ModelNode properties, ServiceName realmServiceName, String realmName, ServiceTarget serviceTarget) { + ServiceName propsServiceName = realmServiceName.append(PropertiesCallbackHandler.SERVICE_SUFFIX); + PropertiesCallbackHandler propsCallbackHandler = new PropertiesCallbackHandler(realmName, properties); + + ServiceBuilder propsBuilder = serviceTarget.addService(propsServiceName, propsCallbackHandler); + if (properties.hasDefined(RELATIVE_TO)) { + propsBuilder.addDependency(pathName(properties.get(RELATIVE_TO).asString()), String.class, propsCallbackHandler.getRelativeToInjector()); } - return connectionManager; + propsBuilder.setInitialMode(ServiceController.Mode.ON_DEMAND) + .install(); + + return propsServiceName; } - private static ServiceName pathName(String relativeTo) { - return ServiceName.JBOSS.append("server", "path", relativeTo); + // TODO - The operation will also be split out into it's own handler when I make the operations more fine grained. + private ServiceName addSSLService(ModelNode ssl, ServiceName realmServiceName, ServiceTarget serviceTarget) { + ServiceName sslServiceName = realmServiceName.append(SSLIdentityService.SERVICE_SUFFIX); + SSLIdentityService sslIdentityService = new SSLIdentityService(ssl); + + ServiceBuilder sslBuilder = serviceTarget.addService(sslServiceName, sslIdentityService); + + if (ssl.hasDefined(KEYSTORE) && ssl.get(KEYSTORE).hasDefined(RELATIVE_TO)) { + sslBuilder.addDependency(pathName(ssl.get(KEYSTORE, RELATIVE_TO).asString()), String.class, sslIdentityService.getRelativeToInjector()); + } + + sslBuilder.setInitialMode(ServiceController.Mode.ON_DEMAND) + .install(); + + return sslServiceName; } - public ModelNode getModelDescription(Locale locale) { - // TODO - Complete getModelDescription() - return new ModelNode(); + private ServiceName addUsersService(ModelNode users, ServiceName realmServiceName, String realmName, ServiceTarget serviceTarget) { + ServiceName usersServiceName = realmServiceName.append(UserDomainCallbackHandler.SERVICE_SUFFIX); + UserDomainCallbackHandler usersCallbackHandler = new UserDomainCallbackHandler(realmName, users); + + ServiceBuilder usersBuilder = serviceTarget.addService(usersServiceName, usersCallbackHandler); + + + usersBuilder.setInitialMode(ServiceController.Mode.ON_DEMAND) + .install(); + + return usersServiceName; + } + + private static ServiceName pathName(String relativeTo) { + return ServiceName.JBOSS.append("server", "path", relativeTo); } } diff --git a/domain-management/src/main/java/org/jboss/as/domain/management/security/PropertiesCallbackHandler.java b/domain-management/src/main/java/org/jboss/as/domain/management/security/PropertiesCallbackHandler.java new file mode 100644 index 000000000000..e294db9f89b7 --- /dev/null +++ b/domain-management/src/main/java/org/jboss/as/domain/management/security/PropertiesCallbackHandler.java @@ -0,0 +1,162 @@ +/* + * 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.PATH; + +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.NameCallback; +import javax.security.auth.callback.PasswordCallback; +import javax.security.auth.callback.UnsupportedCallbackException; +import javax.security.sasl.AuthorizeCallback; +import javax.security.sasl.RealmCallback; +import java.io.File; +import java.io.IOException; +import java.net.MalformedURLException; +import java.util.LinkedList; +import java.util.List; +import java.util.Properties; + +import org.jboss.dmr.ModelNode; +import org.jboss.msc.service.Service; +import org.jboss.msc.service.StartContext; +import org.jboss.msc.service.StartException; +import org.jboss.msc.service.StopContext; +import org.jboss.msc.value.InjectedValue; + +/** + * A CallbackHandler obtaining the users and their passwords from a properties file. + * + * @author Darran Lofthouse + */ +public class PropertiesCallbackHandler implements Service, DomainCallbackHandler { + + public static final String SERVICE_SUFFIX = "properties"; + + private static final Class[] supportedCallbacks = {RealmCallback.class, NameCallback.class, PasswordCallback.class}; + + private final String realm; + private final String path; + private final InjectedValue relativeTo = new InjectedValue(); + private final Properties users = new Properties(); + + public PropertiesCallbackHandler(String realm, ModelNode properties) { + this.realm = realm; + path = properties.require(PATH).asString(); + } + + /* + * Service Methods + */ + + public void start(StartContext context) throws StartException { + String relativeTo = this.relativeTo.getOptionalValue(); + String file = relativeTo == null ? path : relativeTo + "/" + path; + + File propertiesFile = new File(file); + try { + users.load(propertiesFile.toURI().toURL().openStream()); + } catch (MalformedURLException mue) { + throw new StartException("Unable to load properties", mue); + } catch (IOException ioe) { + throw new StartException("Unable to load properties", ioe); + } + } + + public void stop(StopContext context) { + users.clear(); + } + + public PropertiesCallbackHandler getValue() throws IllegalStateException, IllegalArgumentException { + return this; + } + + /* + * Injector Accessors + */ + + public InjectedValue getRelativeToInjector() { + return relativeTo; + } + + /* + * DomainCallbackHandler Methods + */ + + public Class[] getSupportedCallbacks() { + return supportedCallbacks; + } + + public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { + List toRespondTo = new LinkedList(); + + String userName = null; + boolean userFound = false; + + // A single pass may be sufficient but by using a two pass approach the Callbackhandler will not + // fail if an unexpected order is encountered. + + // First Pass - is to double check no unsupported callbacks and to retrieve + // information from the callbacks passing in information. + for (Callback current : callbacks) { + + if (current instanceof AuthorizeCallback) { + toRespondTo.add(current); + } else if (current instanceof NameCallback) { + NameCallback nameCallback = (NameCallback) current; + userName = nameCallback.getDefaultName(); + userFound = users.containsKey(userName); + } else if (current instanceof PasswordCallback) { + toRespondTo.add(current); + } else if (current instanceof RealmCallback) { + String realm = ((RealmCallback) current).getDefaultText(); + if (this.realm.equals(realm) == false) { + // TODO - Check if this needs a real error or of just an unexpected internal error. + throw new IllegalStateException("Invalid Realm '" + realm + "' expected '" + this.realm + "'"); + } + } else { + throw new UnsupportedCallbackException(current); + } + } + + if (userFound == false) { + // TODO - Again proper error reporting. + throw new IllegalStateException("User '" + userName + "' not found."); + } + + // Second Pass - Now iterate the Callback(s) requiring a response. + for (Callback current : toRespondTo) { + if (current instanceof AuthorizeCallback) { + AuthorizeCallback authorizeCallback = (AuthorizeCallback) current; + // Don't support impersonating another identity + authorizeCallback.setAuthorized(authorizeCallback.getAuthenticationID().equals(authorizeCallback.getAuthorizedID())); + } else if (current instanceof PasswordCallback) { + String password = users.get(userName).toString(); + ((PasswordCallback) current).setPassword(password.toCharArray()); + } + } + + } + + +} diff --git a/domain-management/src/main/java/org/jboss/as/domain/management/security/SSLIdentityService.java b/domain-management/src/main/java/org/jboss/as/domain/management/security/SSLIdentityService.java index 455d3da79acc..4ea3bec8d43e 100644 --- a/domain-management/src/main/java/org/jboss/as/domain/management/security/SSLIdentityService.java +++ b/domain-management/src/main/java/org/jboss/as/domain/management/security/SSLIdentityService.java @@ -54,6 +54,8 @@ */ public class SSLIdentityService implements Service { + public static final String SERVICE_SUFFIX = "ssl"; + private final ModelNode ssl; private final InjectedValue relativeTo = new InjectedValue(); diff --git a/domain-management/src/main/java/org/jboss/as/domain/management/security/SecurityRealmService.java b/domain-management/src/main/java/org/jboss/as/domain/management/security/SecurityRealmService.java index 66239fff060a..809a527be0d0 100644 --- a/domain-management/src/main/java/org/jboss/as/domain/management/security/SecurityRealmService.java +++ b/domain-management/src/main/java/org/jboss/as/domain/management/security/SecurityRealmService.java @@ -22,35 +22,13 @@ package org.jboss.as.domain.management.security; -import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.FILE; -import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.KEYSTORE; -import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.LDAP; -import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.PASSWORD; -import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.PATH; -import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.PROTOCOL; -import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.SSL; -import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.USERS; - -import javax.net.ssl.KeyManager; -import javax.net.ssl.KeyManagerFactory; import javax.net.ssl.SSLContext; import javax.security.auth.callback.Callback; import javax.security.auth.callback.UnsupportedCallbackException; -import java.io.FileInputStream; -import java.io.FileNotFoundException; import java.io.IOException; -import java.security.KeyManagementException; -import java.security.KeyStore; -import java.security.KeyStoreException; -import java.security.NoSuchAlgorithmException; -import java.security.UnrecoverableKeyException; -import java.security.cert.CertificateException; - -import org.jboss.as.domain.management.connections.ConnectionManager; + import org.jboss.as.domain.management.SecurityRealm; -import org.jboss.dmr.ModelNode; import org.jboss.logging.Logger; -import org.jboss.msc.inject.Injector; import org.jboss.msc.service.Service; import org.jboss.msc.service.ServiceName; import org.jboss.msc.service.StartContext; @@ -67,43 +45,19 @@ public class SecurityRealmService implements Service, SecurityRealm { public static final ServiceName BASE_SERVICE_NAME = ServiceName.JBOSS.append("server", "controller", "management", "security_realm"); - private static final Logger log = Logger.getLogger("org.jboss.as"); + private static final Logger log = Logger.getLogger("org.jboss.as.domain-management"); - private final InjectedValue connectionManagerValue = new InjectedValue(); - private final InjectedValue relativeTo = new InjectedValue(); + private final InjectedValue callbackHandler = new InjectedValue(); private final InjectedValue sslIdentity = new InjectedValue(); private final String name; - private ModelNode authentication; - private DomainCallbackHandler callbackHandler; - - private SSLContext sslContext; - - public SecurityRealmService(String name, ModelNode authentication) { + public SecurityRealmService(String name) { this.name = name; - this.authentication = authentication; } public void start(StartContext context) throws StartException { log.infof("Starting '%s' Security Realm Service", name); - if (authentication != null && authentication.hasDefined(USERS)) { - callbackHandler = new UserDomainCallbackHandler(name, authentication.require(USERS)); - } else if (authentication != null && authentication.hasDefined(LDAP)) { - callbackHandler = new UserLdapCallbackHandler(connectionManagerValue.getValue(), authentication.require(LDAP)); - } else { - callbackHandler = new DomainCallbackHandler() { - - public Class[] getSupportedCallbacks() { - return new Class[0]; - } - - public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { - // TODO - Should be a real error with code. - throw new IllegalStateException("No authentication mechanism defined in security realm."); - } - }; - } } public void stop(StopContext context) { @@ -118,23 +72,35 @@ public String getName() { return name; } - public InjectedValue getConnectionManagerInjector() { - return connectionManagerValue; + public InjectedValue getCallbackHandlerInjector() { + return callbackHandler; } - public InjectedValue getSSLIdentityInjector() { return sslIdentity; } - /** * Used to obtain the callback handler for the configured 'authorizations'. * * @return The CallbackHandler to be used for verifying the identity of the caller. */ public DomainCallbackHandler getCallbackHandler() { - return callbackHandler; + DomainCallbackHandler response = callbackHandler.getOptionalValue(); + if (response == null) { + response = new DomainCallbackHandler() { + public Class[] getSupportedCallbacks() { + return new Class[0]; + } + + public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { + // TODO - Should be a real error with code. + throw new IllegalStateException("No authentication mechanism defined in security realm."); + } + }; + } + + return response; } public SSLContext getSSLContext() { diff --git a/domain-management/src/main/java/org/jboss/as/domain/management/security/UserDomainCallbackHandler.java b/domain-management/src/main/java/org/jboss/as/domain/management/security/UserDomainCallbackHandler.java index 35cc8abefe02..211ac01a25b3 100644 --- a/domain-management/src/main/java/org/jboss/as/domain/management/security/UserDomainCallbackHandler.java +++ b/domain-management/src/main/java/org/jboss/as/domain/management/security/UserDomainCallbackHandler.java @@ -36,13 +36,19 @@ import java.util.List; import org.jboss.dmr.ModelNode; +import org.jboss.msc.service.Service; +import org.jboss.msc.service.StartContext; +import org.jboss.msc.service.StartException; +import org.jboss.msc.service.StopContext; /** * A CallbackHandler for users defined within the domain mode. * * @author Darran Lofthouse */ -public class UserDomainCallbackHandler implements DomainCallbackHandler { +public class UserDomainCallbackHandler implements Service, DomainCallbackHandler { + + public static final String SERVICE_SUFFIX = "users"; private static final Class[] supportedCallbacks = {RealmCallback.class, NameCallback.class, PasswordCallback.class}; @@ -50,11 +56,29 @@ public class UserDomainCallbackHandler implements DomainCallbackHandler { private final ModelNode userDomain; - UserDomainCallbackHandler(String realm, ModelNode userDomain) { + public UserDomainCallbackHandler(String realm, ModelNode userDomain) { this.realm = realm; this.userDomain = userDomain; } + /* + * Service Methods + */ + + public void start(StartContext context) throws StartException { + } + + public void stop(StopContext context) { + } + + public UserDomainCallbackHandler getValue() throws IllegalStateException, IllegalArgumentException { + return this; + } + + /* + * DomainCallbackHandler Methods + */ + public Class[] getSupportedCallbacks() { return supportedCallbacks; } diff --git a/domain-management/src/main/java/org/jboss/as/domain/management/security/UserLdapCallbackHandler.java b/domain-management/src/main/java/org/jboss/as/domain/management/security/UserLdapCallbackHandler.java index 1b88e768e95a..1d069d874d64 100644 --- a/domain-management/src/main/java/org/jboss/as/domain/management/security/UserLdapCallbackHandler.java +++ b/domain-management/src/main/java/org/jboss/as/domain/management/security/UserLdapCallbackHandler.java @@ -43,18 +43,25 @@ import org.jboss.as.domain.management.connections.ConnectionManager; import org.jboss.dmr.ModelNode; +import org.jboss.msc.service.Service; +import org.jboss.msc.service.StartContext; +import org.jboss.msc.service.StartException; +import org.jboss.msc.service.StopContext; +import org.jboss.msc.value.InjectedValue; /** * A CallbackHandler for users within an LDAP directory. * * @author Darran Lofthouse */ -public class UserLdapCallbackHandler implements DomainCallbackHandler { +public class UserLdapCallbackHandler implements Service, DomainCallbackHandler { + + public static final String SERVICE_SUFFIX = "ldap"; private static final Class[] supportedCallbacks = {RealmCallback.class, NameCallback.class, VerifyPasswordCallback.class}; private static final String DEFAULT_USER_DN = "dn"; - private final ConnectionManager connectionManager; + private final InjectedValue connectionManager = new InjectedValue(); private final String baseDn; private final String usernameAttribute; @@ -62,8 +69,7 @@ public class UserLdapCallbackHandler implements DomainCallbackHandler { private final String userDn; protected final int searchTimeLimit = 10000; // TODO - Maybe make configurable. - public UserLdapCallbackHandler(ConnectionManager connectionManager, ModelNode userLdap) { - this.connectionManager = connectionManager; + public UserLdapCallbackHandler(ModelNode userLdap) { baseDn = userLdap.require(BASE_DN).asString(); usernameAttribute = userLdap.require(USERNAME_ATTRIBUTE).asString(); if (userLdap.has(RECURSIVE)) { @@ -78,6 +84,33 @@ public UserLdapCallbackHandler(ConnectionManager connectionManager, ModelNode us } } + /* + * Service Methods + */ + + public void start(StartContext context) throws StartException { + } + + public void stop(StopContext context) { + } + + public UserLdapCallbackHandler getValue() throws IllegalStateException, IllegalArgumentException { + return this; + } + + /* + * Access to Injectors + */ + + public InjectedValue getConnectionManagerInjector() { + return connectionManager; + } + + + /* + * DomainCallbackHandler Methods + */ + public Class[] getSupportedCallbacks() { // TODO - For safety this Array should be cloned or should use an unmodifiable collection to ensure // TODO - caller can not modify. @@ -85,6 +118,7 @@ public Class[] getSupportedCallbacks() { } public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { + ConnectionManager connectionManager = this.connectionManager.getValue(); String username = null; VerifyPasswordCallback verifyPasswordCallback = null;