org.wildfly.security.mechanism._private.MechanismUtil Maven / Gradle / Ivy
Go to download
This artifact provides a single jar that contains all classes required to use remote EJB and JMS, including
all dependencies. It is intended for use by those not using maven, maven users should just import the EJB and
JMS 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).
/*
* JBoss, Home of Professional Open Source.
* Copyright 2015 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* 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.mechanism._private;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.Provider;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.InvalidKeySpecException;
import java.util.function.Function;
import java.util.function.Supplier;
import javax.security.auth.callback.Callback;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.callback.PasswordCallback;
import javax.security.auth.callback.UnsupportedCallbackException;
import org.wildfly.common.Assert;
import org.wildfly.security.auth.callback.CredentialCallback;
import org.wildfly.security.credential.PasswordCredential;
import org.wildfly.security.http.HttpScope;
import org.wildfly.security.mechanism.AuthenticationMechanismException;
import org.wildfly.security.password.Password;
import org.wildfly.security.password.PasswordFactory;
import org.wildfly.security.password.TwoWayPassword;
import org.wildfly.security.password.interfaces.ClearPassword;
import org.wildfly.security.password.spec.ClearPasswordSpec;
import org.wildfly.security.password.spec.EncryptablePasswordSpec;
/**
* Utils to be used by authentication mechanism (SASL or HTTP) implementations.
*
* @author David M. Lloyd
*/
public final class MechanismUtil {
private MechanismUtil() {}
/**
* Get a password from a client or server callback, falling back to clear password if needed. Note that the
* parameters, while optional, may be required on the client side of some mechanisms in order to ensure that the
* encoded password is compatible with the server challenge.
*
* @param userName the user name to report for error reporting purposes (must not be {@code null})
* @param callbackHandler the callback handler (must not be {@code null})
* @param passwordType the password class (must not be {@code null})
* @param passwordAlgorithm the password algorithm name (must not be {@code null})
* @param matchParameters the optional parameters to match (may be {@code null})
* @param generateParameters the optional default parameters to use if the password must be generated (may be {@code null})
* @param providers the security providers to use with the {@link PasswordFactory}
* @param the password type
* @param log mechanism specific logger
* @return the password
* @throws AuthenticationMechanismException if there is an error retrieving the password
*/
public static S getPasswordCredential(String userName, CallbackHandler callbackHandler, Class passwordType, String passwordAlgorithm, AlgorithmParameterSpec matchParameters, AlgorithmParameterSpec generateParameters, Supplier providers, ElytronMessages log) throws AuthenticationMechanismException {
Assert.checkNotNullParam("userName", userName);
Assert.checkNotNullParam("callbackHandler", callbackHandler);
Assert.checkNotNullParam("passwordType", passwordType);
Assert.checkNotNullParam("passwordAlgorithm", passwordAlgorithm);
Assert.checkNotNullParam("providers", providers);
try {
final PasswordFactory passwordFactory = PasswordFactory.getInstance(passwordAlgorithm, providers);
CredentialCallback credentialCallback = new CredentialCallback(PasswordCredential.class, passwordAlgorithm, matchParameters);
try {
MechanismUtil.handleCallbacks(log, callbackHandler, credentialCallback);
S password = credentialCallback.applyToCredential(PasswordCredential.class, c -> c.getPassword(passwordType));
if (password != null) {
// update parameters to match requirement, if necessary
return matchParameters != null ? passwordType.cast(passwordFactory.transform(password, matchParameters)) : password;
}
// fall out
} catch (UnsupportedCallbackException e) {
if (e.getCallback() != credentialCallback) {
throw log.mechCallbackHandlerFailedForUnknownReason(e);
}
// fall out
} catch (InvalidAlgorithmParameterException | ClassCastException e) {
// fall out
}
credentialCallback = new CredentialCallback(PasswordCredential.class, ClearPassword.ALGORITHM_CLEAR);
try {
MechanismUtil.handleCallbacks(log, callbackHandler, credentialCallback);
final TwoWayPassword twoWayPassword = credentialCallback.applyToCredential(PasswordCredential.class, c -> c.getPassword(TwoWayPassword.class));
if (twoWayPassword != null) {
final PasswordFactory clearFactory = PasswordFactory.getInstance(twoWayPassword.getAlgorithm(), providers);
final ClearPasswordSpec spec = clearFactory.getKeySpec(clearFactory.translate(twoWayPassword), ClearPasswordSpec.class);
if (matchParameters != null) {
return passwordType.cast(passwordFactory.generatePassword(new EncryptablePasswordSpec(spec.getEncodedPassword(), generateParameters)));
} else {
return passwordType.cast(passwordFactory.generatePassword(spec));
}
}
} catch (UnsupportedCallbackException e) {
if (e.getCallback() != credentialCallback) {
throw log.mechCallbackHandlerFailedForUnknownReason(e);
}
// fall out
}
final PasswordCallback passwordCallback = new PasswordCallback("User password", false);
try {
MechanismUtil.handleCallbacks(log, callbackHandler, passwordCallback);
final char[] password = passwordCallback.getPassword();
if (password != null) {
if (matchParameters != null) {
return passwordType.cast(passwordFactory.generatePassword(new EncryptablePasswordSpec(password, generateParameters)));
} else {
return passwordType.cast(passwordFactory.generatePassword(new ClearPasswordSpec(password)));
}
}
} catch (UnsupportedCallbackException e) {
if (e.getCallback() != passwordCallback) {
throw log.mechCallbackHandlerFailedForUnknownReason(e);
}
// fall out
}
} catch (InvalidKeySpecException | NoSuchAlgorithmException | InvalidKeyException e) {
throw log.mechCallbackHandlerDoesNotSupportCredentialAcquisition(e);
}
throw log.mechUnableToRetrievePassword(userName);
}
/**
* A varargs wrapper method for callback handler invocation.
*
* @param log the logger for error purposes
* @param callbackHandler the callback handler
* @param callbacks the callbacks
* @throws AuthenticationMechanismException if the callback handler fails for some reason
* @throws UnsupportedCallbackException if the callback handler throws this exception
*/
public static void handleCallbacks(ElytronMessages log, CallbackHandler callbackHandler, Callback... callbacks) throws AuthenticationMechanismException, UnsupportedCallbackException {
try {
callbackHandler.handle(callbacks);
} catch (AuthenticationMechanismException | UnsupportedCallbackException e) {
throw e;
} catch (Throwable e) {
throw log.mechCallbackHandlerFailedForUnknownReason(e);
}
}
/**
* Get or compute the value for the given key in HttpScope, storing the computed value (if one is generated).
* The function must not generate a {@code null} value or an unspecified exception will result.
*
* @param scope the HTTP scope to store computed value (must not be {@code null})
* @param key the key to retrieve (must not be {@code null})
* @param mappingFunction the function to apply to acquire the value (must not be {@code null})
* @param the type of returned value
* @return the stored or new value (not {@code null})
*/
public static R computeIfAbsent(HttpScope scope, String key, Function mappingFunction) {
Assert.checkNotNullParam("scope", scope);
Assert.checkNotNullParam("key", key);
Assert.checkNotNullParam("mappingFunction", mappingFunction);
synchronized (scope) {
if (! scope.exists()) {
scope.create();
}
final R existing = (R) scope.getAttachment(key);
if (existing == null) {
R newValue = mappingFunction.apply(key);
Assert.assertNotNull(newValue);
scope.setAttachment(key, newValue);
return newValue;
} else {
return existing;
}
}
}
}