/*
* 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.security;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static org.jboss.as.controller.descriptions.ModelDescriptionConstants.OP_ADDR;
import java.security.Principal;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import org.jboss.as.controller.AbstractRuntimeOnlyHandler;
import org.jboss.as.controller.OperationContext;
import org.jboss.as.controller.OperationFailedException;
import org.jboss.as.controller.PathAddress;
import org.jboss.as.controller.ServiceRemoveStepHandler;
import org.jboss.as.controller.SimpleAttributeDefinition;
import org.jboss.as.controller.SimpleAttributeDefinitionBuilder;
import org.jboss.as.controller.SimpleOperationDefinition;
import org.jboss.as.controller.SimpleOperationDefinitionBuilder;
import org.jboss.as.controller.SimpleResourceDefinition;
import org.jboss.as.controller.access.constraint.ApplicationTypeConfig;
import org.jboss.as.controller.access.management.AccessConstraintDefinition;
import org.jboss.as.controller.access.management.ApplicationTypeAccessConstraintDefinition;
import org.jboss.as.controller.access.management.SensitiveTargetAccessConstraintDefinition;
import org.jboss.as.controller.capability.RuntimeCapability;
import org.jboss.as.controller.operations.common.Util;
import org.jboss.as.controller.registry.ManagementResourceRegistration;
import org.jboss.as.controller.registry.OperationEntry;
import org.jboss.as.controller.transform.description.ResourceTransformationDescriptionBuilder;
import org.jboss.as.security.logging.SecurityLogger;
import org.jboss.as.security.plugins.SecurityDomainContext;
import org.jboss.as.security.service.SecurityDomainService;
import org.jboss.dmr.ModelNode;
import org.jboss.dmr.ModelType;
import org.jboss.msc.service.ServiceController;
import org.jboss.msc.service.ServiceName;
import org.jboss.msc.service.StabilityMonitor;
import org.jboss.security.CacheableManager;
import org.jboss.security.SimplePrincipal;
/**
* @author Jason T. Greene
*/
class SecurityDomainResourceDefinition extends SimpleResourceDefinition {
static final String CACHE_CONTAINER_NAME = "security";
static final String INFINISPAN_CACHE_TYPE = "infinispan";
static final RuntimeCapability<Void> LEGACY_SECURITY_DOMAIN = RuntimeCapability.Builder.of("org.wildfly.security.legacy-security-domain", true)
.setServiceType(SecurityDomainContext.class)
.build();
public static final SimpleAttributeDefinition CACHE_TYPE = new SimpleAttributeDefinitionBuilder(Constants.CACHE_TYPE, ModelType.STRING, true)
.setAllowExpression(true)
.setAllowedValues("default", INFINISPAN_CACHE_TYPE)
.build();
private final boolean registerRuntimeOnly;
private final List<AccessConstraintDefinition> accessConstraints;
SecurityDomainResourceDefinition(boolean registerRuntimeOnly) {
super(new Parameters(SecurityExtension.SECURITY_DOMAIN_PATH,
SecurityExtension.getResourceDescriptionResolver(Constants.SECURITY_DOMAIN))
.setAddHandler(SecurityDomainAdd.INSTANCE)
.setRemoveHandler(new ServiceRemoveStepHandler(SecurityDomainService.SERVICE_NAME, SecurityDomainAdd.INSTANCE))
.setCapabilities(LEGACY_SECURITY_DOMAIN));
this.registerRuntimeOnly = registerRuntimeOnly;
ApplicationTypeConfig atc = new ApplicationTypeConfig(SecurityExtension.SUBSYSTEM_NAME, Constants.SECURITY_DOMAIN);
AccessConstraintDefinition acd = new ApplicationTypeAccessConstraintDefinition(atc);
this.accessConstraints = Arrays.asList(SensitiveTargetAccessConstraintDefinition.SECURITY_DOMAIN, acd);
setDeprecated(SecurityExtension.DEPRECATED_SINCE);
}
@Override
public void registerAttributes(final ManagementResourceRegistration resourceRegistration) {
resourceRegistration.registerReadWriteAttribute(CACHE_TYPE, null, new SecurityDomainReloadWriteHandler());
}
@Override
public void registerOperations(ManagementResourceRegistration resourceRegistration) {
super.registerOperations(resourceRegistration);
if (registerRuntimeOnly) {
resourceRegistration.registerOperationHandler(ListCachePrincipals.DEFINITION, ListCachePrincipals.INSTANCE);
resourceRegistration.registerOperationHandler(FlushOperation.DEFINITION,FlushOperation.INSTANCE);
}
}
@Override
public List<AccessConstraintDefinition> getAccessConstraints() {
return accessConstraints;
}
public static ServiceName getSecurityDomainServiceName(PathAddress pathAddress) {
PathAddress domain = Util.getParentAddressByKey(pathAddress, Constants.SECURITY_DOMAIN);
if (domain == null)
throw SecurityLogger.ROOT_LOGGER.addressDidNotContainSecurityDomain();
return SecurityDomainService.SERVICE_NAME.append(domain.getLastElement().getValue());
}
@SuppressWarnings("unchecked")
private static ServiceController<SecurityDomainContext> getSecurityDomainService(OperationContext context, String securityDomain) {
return (ServiceController<SecurityDomainContext>) context
.getServiceRegistry(false)
.getRequiredService(SecurityDomainService.SERVICE_NAME.append(securityDomain));
}
static class ListCachePrincipals extends AbstractRuntimeOnlyHandler {
static final ListCachePrincipals INSTANCE = new ListCachePrincipals();
static final SimpleOperationDefinition DEFINITION = new SimpleOperationDefinitionBuilder(Constants.LIST_CACHED_PRINCIPALS,
SecurityExtension.getResourceDescriptionResolver(Constants.LIST_CACHED_PRINCIPALS))
.setRuntimeOnly()
.setReplyType(ModelType.LIST)
.setReplyValueType(ModelType.STRING)
.build();
@Override
protected void executeRuntimeStep(OperationContext context, ModelNode operation) throws OperationFailedException {
ModelNode opAddr = operation.require(OP_ADDR);
PathAddress address = PathAddress.pathAddress(opAddr);
final String securityDomain = address.getLastElement().getValue();
ServiceController<SecurityDomainContext> controller = getSecurityDomainService(context, securityDomain);
if (controller != null) {
// FIXME this is nasty.
waitForService(controller);
SecurityDomainContext sdc = controller.getValue();
@SuppressWarnings("unchecked")
CacheableManager<?, Principal> manager = (CacheableManager<?, Principal>) sdc
.getAuthenticationManager();
Set<Principal> cachedPrincipals = manager.getCachedKeys();
ModelNode result = context.getResult();
for (Principal principal : cachedPrincipals) {
result.add(principal.getName());
}
if (!result.isDefined())
result.setEmptyList();
} else {
throw SecurityLogger.ROOT_LOGGER.noAuthenticationCacheAvailable(securityDomain);
}
context.completeStep(OperationContext.RollbackHandler.NOOP_ROLLBACK_HANDLER);
}
}
static final class FlushOperation extends AbstractRuntimeOnlyHandler {
static final FlushOperation INSTANCE = new FlushOperation();
static final SimpleOperationDefinition DEFINITION = new SimpleOperationDefinitionBuilder(Constants.FLUSH_CACHE,
SecurityExtension.getResourceDescriptionResolver(Constants.SECURITY_DOMAIN))
.setEntryType(OperationEntry.EntryType.PUBLIC)
.setRuntimeOnly()
.addParameter(new SimpleAttributeDefinition(Constants.PRINCIPAL_ARGUMENT, ModelType.STRING, true))
.build();
@Override
protected void executeRuntimeStep(OperationContext context, ModelNode operation) throws OperationFailedException {
ModelNode opAddr = operation.require(OP_ADDR);
PathAddress address = PathAddress.pathAddress(opAddr);
final String securityDomain = address.getLastElement().getValue();
String principal = null;
if (operation.hasDefined(Constants.PRINCIPAL_ARGUMENT))
principal = operation.get(Constants.PRINCIPAL_ARGUMENT).asString();
ServiceController<SecurityDomainContext> controller = getSecurityDomainService(context, securityDomain);
if (controller != null) {
// FIXME this is nasty.
waitForService(controller);
SecurityDomainContext sdc = controller.getValue();
@SuppressWarnings("unchecked")
CacheableManager<?, Principal> manager = (CacheableManager<?, Principal>) sdc.getAuthenticationManager();
if (principal != null)
manager.flushCache(new SimplePrincipal(principal));
else
manager.flushCache();
} else {
throw SecurityLogger.ROOT_LOGGER.noAuthenticationCacheAvailable(securityDomain);
}
// Can't rollback
context.completeStep(OperationContext.RollbackHandler.NOOP_ROLLBACK_HANDLER);
}
}
/**
* Wait for the required service to start up and fail otherwise. This method is necessary when a runtime operation
* uses a service that might have been created within a composite operation.
*
* This method will wait at most 100 millis.
*
* @param controller the service to wait for
* @throws OperationFailedException if the service is not available, or the thread was interrupted.
*/
private static void waitForService(final ServiceController<?> controller) throws OperationFailedException {
if (controller.getState() == ServiceController.State.UP) return;
final StabilityMonitor monitor = new StabilityMonitor();
monitor.addController(controller);
try {
monitor.awaitStability(100, MILLISECONDS);
} catch (final InterruptedException e) {
Thread.currentThread().interrupt();
throw SecurityLogger.ROOT_LOGGER.interruptedWaitingForSecurityDomain(controller.getName().getSimpleName());
} finally {
monitor.removeController(controller);
}
if (controller.getState() != ServiceController.State.UP) {
throw SecurityLogger.ROOT_LOGGER.requiredSecurityDomainServiceNotAvailable(controller.getName().getSimpleName());
}
}
static void registerTransformers_1_3_0(ResourceTransformationDescriptionBuilder parentBuilder) {
ResourceTransformationDescriptionBuilder builder = parentBuilder.addChildResource(SecurityExtension.SECURITY_DOMAIN_PATH);
AuditResourceDefinition.registerTransformers_1_3_0(builder);
}
}