org.wildfly.security.auth.jaspi.impl.JaspiAuthenticationContext Maven / Gradle / Ivy
Go to download
This artifact provides a single jar that contains all classes required to use remote Jakarta Enterprise Beans and Jakarta Messaging, including
all dependencies. It is intended for use by those not using maven, maven users should just import the Jakarta Enterprise Beans and
Jakarta Messaging BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up
with different versions on classes on the class path).
/*
* Copyright 2018 Red Hat, Inc.
*
* 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.security.auth.jaspi.impl;
import static org.wildfly.common.Assert.checkNotNullParam;
import static org.wildfly.security.auth.jaspi._private.ElytronMessages.log;
import static org.wildfly.security.auth.jaspi.impl.SecurityActions.doPrivileged;
import java.io.IOException;
import java.security.Principal;
import java.security.PrivilegedAction;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
import javax.security.auth.Subject;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.UnsupportedCallbackException;
import javax.security.auth.message.callback.CallerPrincipalCallback;
import javax.security.auth.message.callback.GroupPrincipalCallback;
import javax.security.auth.message.callback.PasswordValidationCallback;
import org.wildfly.security.auth.callback.CallbackUtil;
import org.wildfly.security.auth.principal.NamePrincipal;
import org.wildfly.security.auth.server.SecurityDomain;
import org.wildfly.security.auth.server.SecurityIdentity;
import org.wildfly.security.auth.server.ServerAuthenticationContext;
import org.wildfly.security.authz.RoleMapper;
import org.wildfly.security.authz.Roles;
import org.wildfly.security.evidence.Evidence;
import org.wildfly.security.evidence.PasswordGuessEvidence;
import org.wildfly.security.permission.ElytronPermission;
/**
*
* @author Darran Lofthouse
*/
public class JaspiAuthenticationContext {
static final ElytronPermission CREATE_AUTH_CONTEXT = ElytronPermission.forName("createServerAuthenticationContext");
private final SecurityDomain securityDomain;
private final boolean integrated;
private volatile SecurityIdentity securityIdentity = null;
private final Set roles = new HashSet<>();
JaspiAuthenticationContext(SecurityDomain securityDomain, boolean integrated) {
this.securityDomain = securityDomain;
this.integrated = integrated;
}
/*
* Having a few options makes it feel like we should use a Builder, however that would lead to one more object per request.
*
* For these per-request classes we probably could make them self building with an activation step at the end that allows
* their use whilst at the same time prohibits further config changes.
*/
// TODO Can we find a way to create this from the SecurityDomain similar to ServerAuthContext?
public static JaspiAuthenticationContext newInstance(final SecurityDomain securityDomain, final boolean integrated) {
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
sm.checkPermission(CREATE_AUTH_CONTEXT);
}
return new JaspiAuthenticationContext(checkNotNullParam("securityDomain", securityDomain), integrated);
}
public CallbackHandler createCallbackHandler() {
return createCommonCallbackHandler(integrated);
}
private CallbackHandler createCommonCallbackHandler(final boolean integrated) {
return new CallbackHandler() {
@Override
public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException {
try {
doPrivileged((PrivilegedExceptionAction) () -> {
handleOne(callbacks, 0);
return null;
});
} catch (Exception e) {
if (e instanceof PrivilegedActionException) {
if (e.getCause() instanceof UnsupportedCallbackException) {
throw (UnsupportedCallbackException) e.getCause();
} else if (e.getCause() instanceof IOException) {
throw (IOException) e.getCause();
}
}
throw new IOException(e);
}
}
private void handleOne(Callback[] callbacks, int index) throws IOException, UnsupportedCallbackException {
if (callbacks.length == index) {
return;
}
final Callback callback = callbacks[index];
if (callback instanceof PasswordValidationCallback) {
PasswordValidationCallback pvc = (PasswordValidationCallback) callback;
final String username = pvc.getUsername();
log.tracef("Handling PasswordValidationCallback for '%s'", username);
final Evidence evidence = new PasswordGuessEvidence(pvc.getPassword());
try {
// Not adding TRACE logging here as the transitions from SecurityDomain are logged.
SecurityIdentity authenticated = securityDomain.authenticate(username, evidence);
pvc.setResult(true);
securityIdentity = authenticated; // Take a PasswordValidationCallback as always starting authentication again.
} catch (Exception e) {
log.trace("Authentication failed", e);
pvc.setResult(false);
}
} else if (callback instanceof CallerPrincipalCallback) {
log.trace("Handling CallerPrincipalCallback");
final CallerPrincipalCallback cpc = (CallerPrincipalCallback) callback;
Principal originalPrincipal = cpc.getPrincipal();
final String callerName = cpc.getName();
final Principal callerPrincipal = originalPrincipal != null ? originalPrincipal : callerName != null ? new NamePrincipal(callerName) : null;
log.tracef("Original Principal = '%s', Caller Name = '%s', Resulting Principal = '%s'", originalPrincipal, callerName, callerPrincipal);
SecurityIdentity authorizedIdentity = null;
if (securityIdentity != null) {
if (callerPrincipal != null) {
boolean authorizationRequired = (integrated && !securityIdentity.getPrincipal().equals(callerPrincipal));
// If we are integrated we want an authorization check.
authorizedIdentity = securityIdentity.createRunAsIdentity(callerPrincipal, authorizationRequired);
} else if (integrated) {
// Authorize as the authenticated identity.
try (final ServerAuthenticationContext sac = securityDomain.createNewAuthenticationContext()) {
sac.importIdentity(securityIdentity);
sac.authorize();
authorizedIdentity = sac.getAuthorizedIdentity();
}
} else {
authorizedIdentity = securityIdentity;
}
} else {
if (callerPrincipal == null) {
// Do nothing and don't fail.
handleOne(callbacks, index + 1);
return;
} else {
if (integrated) {
try (final ServerAuthenticationContext sac = securityDomain.createNewAuthenticationContext()) {
sac.setAuthenticationPrincipal(callerPrincipal);
if (sac.authorize()) {
authorizedIdentity = sac.getAuthorizedIdentity();
}
}
} else {
authorizedIdentity = securityDomain.createAdHocIdentity(callerPrincipal);
}
}
}
if (authorizedIdentity != null) {
securityIdentity = authorizedIdentity;
final Subject subject = cpc.getSubject();
if (subject != null && !subject.isReadOnly()) {
subject.getPrincipals().add(authorizedIdentity.getPrincipal());
}
} else {
throw log.authorizationFailed();
}
} else if (callback instanceof GroupPrincipalCallback) {
log.trace("Handling GroupPrincipalCallback");
log.trace("Handling GroupPrincipalCallback");
GroupPrincipalCallback gpc = (GroupPrincipalCallback) callback;
String[] groups = gpc.getGroups();
if (groups != null && groups.length > 0) {
roles.addAll(Arrays.asList(groups));
}
// TODO - Add anything to subject?
} else {
CallbackUtil.unsupported(callback);
handleOne(callbacks, index + 1);
}
handleOne(callbacks, index + 1);
}
};
}
/**
* Get the authorized identity result of this authentication.
*
* @return the authorized identity
* @throws IllegalStateException if the authentication is incomplete
*/
public SecurityIdentity getAuthorizedIdentity() throws IllegalStateException {
SecurityIdentity securityIdentity = this.securityIdentity;
if (securityIdentity != null && roles.size() > 0) {
if (log.isTraceEnabled()) {
Iterator rolesIterator = roles.iterator();
StringBuilder sb = new StringBuilder(rolesIterator.next());
while (rolesIterator.hasNext()) {
sb.append(",").append(rolesIterator.next());
}
log.tracef("Assigning roles '%s' to resulting SecurityIdentity", sb.toString());
}
Roles roles = Roles.fromSet(this.roles);
RoleMapper roleMapper = RoleMapper.constant(roles);
SecurityIdentity temp = securityIdentity;
securityIdentity = doPrivileged((PrivilegedAction) (() -> temp.withDefaultRoleMapper(roleMapper)));
} else {
log.trace("No roles request of CallbackHandler.");
}
return securityIdentity;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy