/*
* JBoss, Home of Professional Open Source
* Copyright 2011-2013 Red Hat Inc. and/or its affiliates and other contributors
* as indicated by the @author tags. All rights reserved.
* See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This copyrighted material is made available to anyone wishing to use,
* modify, copy, or redistribute it subject to the terms and conditions
* of the GNU Lesser General Public License, v. 2.1.
* This program is distributed in the hope that it will be useful, but WITHOUT A
* 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,
* v.2.1 along with this distribution; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA 02110-1301, USA.
*/
package org.infinispan.server.endpoint.subsystem;
import static org.infinispan.server.endpoint.EndpointLogger.ROOT_LOGGER;
import java.net.InetSocketAddress;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import org.infinispan.manager.EmbeddedCacheManager;
import org.infinispan.rest.embedded.netty4.NettyRestServer;
import org.infinispan.rest.configuration.ExtendedHeaders;
import org.infinispan.rest.configuration.RestServerConfigurationBuilder;
import org.infinispan.rest.embedded.netty4.security.Authenticator;
import org.infinispan.rest.embedded.netty4.security.BasicAuthenticator;
import org.infinispan.rest.embedded.netty4.security.ClientCertAuthenticator;
import org.infinispan.server.endpoint.subsystem.security.BasicRestSecurityDomain;
import org.infinispan.server.endpoint.subsystem.security.ClientCertRestSecurityDomain;
import org.jboss.as.controller.services.path.PathManager;
import org.jboss.as.domain.management.SecurityRealm;
import org.jboss.as.network.SocketBinding;
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;
import org.jboss.resteasy.plugins.server.embedded.SecurityDomain;
/**
* A service which starts the REST web application
*
* @author Tristan Tarrant <ttarrant@redhat.com>
* @since 6.0
*/
public class RestService implements Service<NettyRestServer>, EncryptableService {
private final InjectedValue<PathManager> pathManagerInjector = new InjectedValue<>();
private final InjectedValue<EmbeddedCacheManager> cacheManagerInjector = new InjectedValue<>();
private final InjectedValue<SocketBinding> socketBinding = new InjectedValue<>();
private final InjectedValue<SecurityRealm> encryptionSecurityRealm = new InjectedValue<>();
private final InjectedValue<SecurityRealm> authenticationSecurityRealm = new InjectedValue<>();
private final Map<String, InjectedValue<SecurityRealm>> sniDomains = new HashMap<>();
private final RestAuthMethod authMethod;
private final String serverName;
private final String contextPath;
private final ExtendedHeaders extendedHeaders;
private final Set<String> ignoredCaches;
private NettyRestServer restServer;
private boolean clientAuth;
public RestService(String serverName, RestAuthMethod authMethod, String contextPath, ExtendedHeaders extendedHeaders, Set<String> ignoredCaches) {
this.serverName = serverName;
this.authMethod = authMethod;
this.contextPath = contextPath;
this.extendedHeaders = extendedHeaders;
this.ignoredCaches = ignoredCaches;
}
/** {@inheritDoc} */
@Override
public synchronized void start(StartContext startContext) throws StartException {
RestServerConfigurationBuilder builder = new RestServerConfigurationBuilder();
builder.name(serverName).extendedHeaders(extendedHeaders).ignoredCaches(ignoredCaches).contextPath(contextPath);
EncryptableServiceHelper.fillSecurityConfiguration(this, builder.ssl());
String protocolName = getProtocolName();
ROOT_LOGGER.endpointStarting(protocolName);
try {
SocketBinding socketBinding = getSocketBinding().getOptionalValue();
if(socketBinding == null) {
builder.startTransport(false);
ROOT_LOGGER.startingServerWithoutTransport("REST");
} else {
InetSocketAddress socketAddress = socketBinding.getSocketAddress();
builder.host(socketAddress.getAddress().getHostAddress());
builder.port(socketAddress.getPort());
}
Authenticator authenticator;
switch (authMethod) {
case BASIC: {
SecurityRealm authenticationRealm = authenticationSecurityRealm.getOptionalValue();
SecurityDomain restSecurityDomain = new BasicRestSecurityDomain(authenticationRealm);
authenticator = new BasicAuthenticator(restSecurityDomain, EncryptableServiceHelper.isSecurityEnabled(this), authenticationRealm.getName());
break;
}
case CLIENT_CERT: {
if (!EncryptableServiceHelper.isSecurityEnabled(this)) {
throw ROOT_LOGGER.cannotUseCertificateAuthenticationWithoutEncryption();
}
SecurityRealm authenticationRealm = authenticationSecurityRealm.getOptionalValue();
SecurityDomain restSecurityDomain = new ClientCertRestSecurityDomain(authenticationRealm);
authenticator = new ClientCertAuthenticator(restSecurityDomain);
break;
}
case NONE: {
authenticator = null;
break;
}
default:
throw ROOT_LOGGER.restAuthMethodUnsupported(authMethod.toString());
}
restServer = NettyRestServer.createServer(builder.build(), cacheManagerInjector.getValue(), authenticator);
} catch (Exception e) {
throw ROOT_LOGGER.restContextCreationFailed(e);
}
try {
restServer.start();
ROOT_LOGGER.httpEndpointStarted(protocolName, contextPath, "rest");
} catch (Exception e) {
throw ROOT_LOGGER.restContextStartFailed(e);
}
}
private String getProtocolName() {
return EncryptableServiceHelper.isSecurityEnabled(this) ?
(EncryptableServiceHelper.isSniEnabled(this) ? serverName + "+SNI" : serverName + "+SSL") : serverName;
}
/** {@inheritDoc} */
@Override
public synchronized void stop(StopContext stopContext) {
restServer.stop();
}
/** {@inheritDoc} */
@Override
public synchronized NettyRestServer getValue() throws IllegalStateException {
if (restServer == null) {
throw new IllegalStateException();
}
return restServer;
}
public InjectedValue<PathManager> getPathManagerInjector() {
return pathManagerInjector;
}
public InjectedValue<EmbeddedCacheManager> getCacheManager() {
return cacheManagerInjector;
}
public InjectedValue<SecurityRealm> getAuthenticationSecurityRealm() {
return authenticationSecurityRealm;
}
public InjectedValue<SocketBinding> getSocketBinding() {
return socketBinding;
}
@Override
public InjectedValue<SecurityRealm> getEncryptionSecurityRealm() {
return encryptionSecurityRealm;
}
@Override
public InjectedValue<SecurityRealm> getSniSecurityRealm(String sniHostName) {
return sniDomains.computeIfAbsent(sniHostName, v -> new InjectedValue<>());
}
@Override
public Map<String, InjectedValue<SecurityRealm>> getSniConfiguration() {
return sniDomains;
}
@Override
public String getServerName() {
return serverName;
}
@Override
public void setClientAuth(boolean enabled) {
clientAuth = enabled;
}
@Override
public boolean getClientAuth() {
return clientAuth;
}
}