/**
* Copyright 2015-2016 Red Hat, Inc, and individual contributors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.wildfly.swarm.monitor.runtime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.Set;
import javax.enterprise.inject.Vetoed;
import javax.naming.NamingException;
import io.undertow.security.api.AuthenticationMechanism;
import io.undertow.security.api.AuthenticationMode;
import io.undertow.security.handlers.AuthenticationCallHandler;
import io.undertow.security.handlers.AuthenticationConstraintHandler;
import io.undertow.security.handlers.AuthenticationMechanismsHandler;
import io.undertow.security.handlers.SecurityInitialHandler;
import io.undertow.security.idm.DigestAlgorithm;
import io.undertow.security.impl.BasicAuthenticationMechanism;
import io.undertow.security.impl.CachedAuthenticatedSessionMechanism;
import io.undertow.security.impl.DigestAuthenticationMechanism;
import io.undertow.security.impl.DigestQop;
import io.undertow.security.impl.SimpleNonceManager;
import io.undertow.server.HttpHandler;
import io.undertow.server.HttpServerExchange;
import io.undertow.server.handlers.PredicateHandler;
import org.jboss.as.domain.http.server.security.AuthenticationMechanismWrapper;
import org.jboss.as.domain.http.server.security.RealmIdentityManager;
import org.jboss.as.domain.management.AuthMechanism;
import org.jboss.as.domain.management.SecurityRealm;
/**
* Wraps the actual HTTP endpoint and add security to it.
*
* @author Heiko Braun
* @see HttpContexts
* @since 18/02/16
*/
@Vetoed
public class SecureHttpContexts implements HttpHandler {
public SecureHttpContexts(HttpHandler next) {
this.next = next;
try {
this.monitor = Monitor.lookup();
} catch (NamingException e) {
throw new RuntimeException("Failed to lookup monitor", e);
}
Optional<SecurityRealm> securityRealm = monitor.getSecurityRealm();
if (securityRealm.isPresent()) {
delegate = secureHandler(new HttpContexts(next), securityRealm.get());
} else {
delegate = new HttpContexts(next);
}
}
@Override
public void handleRequest(HttpServerExchange exchange) throws Exception {
delegate.handleRequest(exchange);
}
/**
* Wraps the target handler and makes it inheritSecurity.
* Includes a predicate for relevant web contexts.
*/
private HttpHandler secureHandler(final HttpHandler toWrap, SecurityRealm securityRealm) {
HttpHandler handler = toWrap;
handler = new AuthenticationCallHandler(handler);
handler = new AuthenticationConstraintHandler(handler);
RealmIdentityManager idm = new RealmIdentityManager(securityRealm);
Set<AuthMechanism> mechanisms = securityRealm.getSupportedAuthenticationMechanisms();
List<AuthenticationMechanism> undertowMechanisms = new ArrayList<AuthenticationMechanism>(mechanisms.size());
undertowMechanisms.add(wrap(new CachedAuthenticatedSessionMechanism(), null));
for (AuthMechanism current : mechanisms) {
switch (current) {
case DIGEST:
List<DigestAlgorithm> digestAlgorithms = Collections.singletonList(DigestAlgorithm.MD5);
List<DigestQop> digestQops = Collections.singletonList(DigestQop.AUTH);
undertowMechanisms.add(wrap(new DigestAuthenticationMechanism(digestAlgorithms, digestQops,
securityRealm.getName(), "Monitor", new SimpleNonceManager()), current));
break;
case PLAIN:
undertowMechanisms.add(wrap(new BasicAuthenticationMechanism(securityRealm.getName()), current));
break;
case LOCAL:
break;
default:
}
}
handler = new AuthenticationMechanismsHandler(handler, undertowMechanisms);
handler = new SecurityInitialHandler(AuthenticationMode.PRO_ACTIVE, idm, handler);
// the predicate handler takes care that all of the above
// will only be enacted on relevant web contexts
handler = new PredicateHandler(exchange -> {
return monitor.getSecurityRealm().isPresent() &&
(
Queries.isAggregatorEndpoint(monitor, exchange.getRelativePath()) ||
(Queries.isDirectAccessToHealthEndpoint(monitor, exchange.getRelativePath())
&& !hasTokenAuth(exchange)) ||
HttpContexts.getDefaultContextNames().contains(exchange.getRelativePath())
);
}, handler, toWrap);
return handler;
}
private boolean hasTokenAuth(HttpServerExchange exchange) {
String token = exchange.getAttachment(HttpContexts.TOKEN);
return token != null && HttpContexts.EPHEMERAL_TOKEN.equals(token);
}
private static AuthenticationMechanism wrap(final AuthenticationMechanism toWrap, final AuthMechanism mechanism) {
return new AuthenticationMechanismWrapper(toWrap, mechanism);
}
private final HttpHandler delegate;
private final Monitor monitor;
private final HttpHandler next;
}