Index: 3rdParty_sources/3rdParty_sources.iml =================================================================== diff -u -r0830e8691458c7833c2aa84cfd18806b34daeaa8 -r5e9e3c6915701ed7a340d47ff6d32c12ccb9e800 --- 3rdParty_sources/3rdParty_sources.iml (.../3rdParty_sources.iml) (revision 0830e8691458c7833c2aa84cfd18806b34daeaa8) +++ 3rdParty_sources/3rdParty_sources.iml (.../3rdParty_sources.iml) (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -42,6 +42,7 @@ + Index: 3rdParty_sources/elytron/org/wildfly/security/ElytronMessages.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/ElytronMessages.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/ElytronMessages.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,59 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2014 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; + +import java.security.NoSuchAlgorithmException; + +import org.jboss.logging.BasicLogger; +import org.jboss.logging.Logger; +import org.jboss.logging.annotations.Cause; +import org.jboss.logging.annotations.LogMessage; +import org.jboss.logging.annotations.Message; +import org.jboss.logging.annotations.MessageLogger; +import org.jboss.logging.annotations.ValidIdRange; +import org.jboss.logging.annotations.ValidIdRanges; + + +/** + * Log messages and exceptions for Elytron. + * + * @author David M. Lloyd + * @author Darran Lofthouse + */ +@MessageLogger(projectCode = "ELY", length = 5) +@ValidIdRanges({ + @ValidIdRange(min = 1, max = 1), + @ValidIdRange(min = 5, max = 5), + @ValidIdRange(min = 11, max = 11) +}) +interface ElytronMessages extends BasicLogger { + + ElytronMessages log = Logger.getMessageLogger(ElytronMessages.class, "org.wildfly.security"); + + @LogMessage + @Message(id = 1, value = "WildFly Elytron version %s") + void logVersion(String versionString); + + @Message(id = 5, value = "Cannot instantiate self-referential factory") + IllegalStateException cannotInstantiateSelfReferentialFactory(); + + @Message(id = 11, value = "Unable to create service for '%s.%s' ") + NoSuchAlgorithmException noSuchAlgorithmCreateService(String serviceType, String algorithm, @Cause Throwable cause); + +} Index: 3rdParty_sources/elytron/org/wildfly/security/ElytronMessages_$logger.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/ElytronMessages_$logger.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/ElytronMessages_$logger.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,61 @@ +package org.wildfly.security; + +import java.util.Locale; +import java.lang.IllegalStateException; +import java.io.Serializable; +import javax.annotation.Generated; +import org.jboss.logging.DelegatingBasicLogger; +import org.jboss.logging.BasicLogger; +import java.lang.Throwable; +import java.lang.String; +import java.security.NoSuchAlgorithmException; +import org.jboss.logging.Logger; +import java.util.Arrays; + + +import static org.jboss.logging.Logger.Level.INFO; + +/** + * Warning this class consists of generated code. + */ +@Generated(value = "org.jboss.logging.processor.generator.model.MessageLoggerImplementor", date = "2024-01-15T21:42:25+0100") +public class ElytronMessages_$logger extends DelegatingBasicLogger implements ElytronMessages, BasicLogger, Serializable { + private static final long serialVersionUID = 1L; + private static final String FQCN = ElytronMessages_$logger.class.getName(); + public ElytronMessages_$logger(final Logger log) { + super(log); + } + private static final Locale LOCALE = Locale.ROOT; + protected Locale getLoggingLocale() { + return LOCALE; + } + @Override + public final void logVersion(final String versionString) { + super.log.logf(FQCN, INFO, null, logVersion$str(), versionString); + } + protected String logVersion$str() { + return "ELY00001: WildFly Elytron version %s"; + } + protected String cannotInstantiateSelfReferentialFactory$str() { + return "ELY00005: Cannot instantiate self-referential factory"; + } + @Override + public final IllegalStateException cannotInstantiateSelfReferentialFactory() { + final IllegalStateException result = new IllegalStateException(String.format(getLoggingLocale(), cannotInstantiateSelfReferentialFactory$str())); + _copyStackTraceMinusOne(result); + return result; + } + private static void _copyStackTraceMinusOne(final Throwable e) { + final StackTraceElement[] st = e.getStackTrace(); + e.setStackTrace(Arrays.copyOfRange(st, 1, st.length)); + } + protected String noSuchAlgorithmCreateService$str() { + return "ELY00011: Unable to create service for '%s.%s' "; + } + @Override + public final NoSuchAlgorithmException noSuchAlgorithmCreateService(final String serviceType, final String algorithm, final Throwable cause) { + final NoSuchAlgorithmException result = new NoSuchAlgorithmException(String.format(getLoggingLocale(), noSuchAlgorithmCreateService$str(), serviceType, algorithm), cause); + _copyStackTraceMinusOne(result); + return result; + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/EmptyProvider.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/EmptyProvider.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/EmptyProvider.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,47 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2013 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; + +import java.security.Provider; + +/** + * The singleton empty provider. + */ +public final class EmptyProvider extends Provider { + + private static final long serialVersionUID = 2185633278059382100L; + + private static final EmptyProvider INSTANCE = new EmptyProvider(); + + /** + * Construct a new instance. + */ + private EmptyProvider() { + super("EmptyProvider", 0.0, "Empty Provider"); + } + + /** + * Get the empty provider instance. + * + * @return the empty provider instance + */ + public static EmptyProvider getInstance() { + return INSTANCE; + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/FailedSecurityFactory.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/FailedSecurityFactory.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/FailedSecurityFactory.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,45 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2014 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; + +import java.security.GeneralSecurityException; + +/** + * A {@link SecurityFactory} implementation which only throws specified exception on create. + * + * @author David M. Lloyd + */ +public final class FailedSecurityFactory implements SecurityFactory { + + private final GeneralSecurityException exception; + + /** + * Creates a new factory instance. + * + * @param exception the exception to be thrown on create + */ + public FailedSecurityFactory(final GeneralSecurityException exception) { + this.exception = exception; + } + + @Override + public T create() throws GeneralSecurityException { + throw exception; + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/FixedSecurityFactory.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/FixedSecurityFactory.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/FixedSecurityFactory.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,45 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2014 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; + +import java.security.GeneralSecurityException; + +/** + * A {@link SecurityFactory} implementation which returns specified object every time. + * + * @author David M. Lloyd + */ +public final class FixedSecurityFactory implements SecurityFactory { + + private final T value; + + /** + * Creates a new factory instance. + * + * @param value the value to be returned on create + */ + public FixedSecurityFactory(final T value) { + this.value = value; + } + + @Override + public T create() throws GeneralSecurityException { + return value; + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/NullSecurityFactory.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/NullSecurityFactory.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/NullSecurityFactory.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,50 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2014 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; + +import java.security.GeneralSecurityException; + +/** + * A {@link SecurityFactory} implementation which returns null every time. + * + * @author David M. Lloyd + */ +public final class NullSecurityFactory implements SecurityFactory { + + static final NullSecurityFactory INSTANCE = new NullSecurityFactory<>(); + + NullSecurityFactory() { + } + + /** + * Gets an instance of this singleton class. + * + * @param the type of the security factory + * @return the only instance of this factory + */ + @SuppressWarnings("unchecked") + public static NullSecurityFactory getInstance() { + return (NullSecurityFactory) INSTANCE; + } + + @Override + public T create() throws GeneralSecurityException { + return null; + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/OneTimeSecurityFactory.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/OneTimeSecurityFactory.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/OneTimeSecurityFactory.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,60 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2014 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; + +import static org.wildfly.security.ElytronMessages.log; + +import java.security.GeneralSecurityException; + +/** + * A {@link SecurityFactory} implementation which calls delegated factory at first and + * returns created object for any other create call. Thread safe. + * + * @author David M. Lloyd + */ +public final class OneTimeSecurityFactory implements SecurityFactory { + private volatile SecurityFactory factory; + private volatile T obj; + + /** + * Creates a new factory instance. + * + * @param factory a security factory to use to obtain object which should be returned by this factory every time + */ + public OneTimeSecurityFactory(final SecurityFactory factory) { + this.factory = factory; + } + + public T create() throws GeneralSecurityException { + T val = obj; + if (val == null) { + if (Thread.holdsLock(this)) { + throw log.cannotInstantiateSelfReferentialFactory(); + } + synchronized (this) { + val = obj; + if (val == null) { + val = obj = factory.create(); + factory = null; + } + } + } + return val; + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/ParametricPrivilegedAction.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/ParametricPrivilegedAction.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/ParametricPrivilegedAction.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,44 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2014 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; + +import java.util.function.Function; + +/** + * A privileged action which accepts a parameter. + * + * @param the action result type + * @param

the action parameter type + * + * @author David M. Lloyd + */ +@FunctionalInterface +public interface ParametricPrivilegedAction extends Function { + default T apply(P p) { + return run(p); + } + + /** + * Perform the action. + * + * @param parameter the passed-in parameter + * @return the action result + */ + T run(P parameter); +} Index: 3rdParty_sources/elytron/org/wildfly/security/ParametricPrivilegedExceptionAction.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/ParametricPrivilegedExceptionAction.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/ParametricPrivilegedExceptionAction.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,45 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2014 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; + +import org.wildfly.common.function.ExceptionFunction; + +/** + * A privileged action which accepts a parameter and can throw an exception. + * + * @param the action result type + * @param

the action parameter type + * + * @author David M. Lloyd + */ +@FunctionalInterface +public interface ParametricPrivilegedExceptionAction extends ExceptionFunction { + default T apply(P p) throws Exception { + return run(p); + } + + /** + * Perform the action. + * + * @param parameter the passed-in parameter + * @return the action result + * @throws Exception if the action fails + */ + T run(P parameter) throws Exception; +} Index: 3rdParty_sources/elytron/org/wildfly/security/SecurityFactory.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/SecurityFactory.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/SecurityFactory.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,38 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2014 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; + +import java.security.GeneralSecurityException; + +/** + * A factory for preconfigured security objects. + * + * @author David M. Lloyd + */ +@FunctionalInterface +public interface SecurityFactory { + + /** + * Create an instance. + * + * @return the new instance + * @throws GeneralSecurityException if instantiation fails for some reason + */ + T create() throws GeneralSecurityException; +} Index: 3rdParty_sources/elytron/org/wildfly/security/Version.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/Version.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/Version.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,76 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2013 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; + +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.StandardCharsets; +import java.util.Properties; + +/** + * The version of this JAR. + * + * @author David M. Lloyd + */ +public final class Version { + + private Version() { + } + + private static final String VERSION; + private static final String JAR_NAME; + + static { + Properties versionProps = new Properties(); + String versionString = "(unknown)"; + String jarName = "(unknown)"; + try (final InputStream stream = Version.class.getResourceAsStream("Version.properties")) { + try (final InputStreamReader reader = new InputStreamReader(stream, StandardCharsets.UTF_8)) { + versionProps.load(reader); + versionString = versionProps.getProperty("version", versionString); + jarName = versionProps.getProperty("jarName", jarName); + } + } catch (IOException ignored) { + } + VERSION = versionString; + JAR_NAME = jarName; + try { + ElytronMessages.log.logVersion(versionString); + } catch (Throwable ignored) {} + } + + /** + * Get the version. + * + * @return the version + */ + public static String getVersion() { + return VERSION; + } + + /** + * Get the JAR name. + * + * @return the JAR name + */ + public static String getJarName() { + return JAR_NAME; + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/Version.properties =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/Version.properties (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/Version.properties (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,2 @@ +version=${project.version} +jarName=${project.artifactId} Index: 3rdParty_sources/elytron/org/wildfly/security/VersionedProvider.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/VersionedProvider.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/VersionedProvider.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,40 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2018 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; + +import java.security.Provider; + +/** + * A security provider which uses a string version, forward compatible with Java 9. + */ +public abstract class VersionedProvider extends Provider { + private static final long serialVersionUID = 6973461237113228162L; + + /** + * Constructs a provider with the specified name, version number, + * and information. + * + * @param name the provider name + * @param version the provider version number string + * @param info a description of the provider and its services + */ + protected VersionedProvider(final String name, final String version, final String info) { + super(name, Double.parseDouble(version), info); + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/WildFlyElytronBaseProvider.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/WildFlyElytronBaseProvider.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/WildFlyElytronBaseProvider.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,227 @@ +/* + * 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; + +import static org.wildfly.security.ElytronMessages.log; + +import java.lang.ref.Reference; +import java.lang.ref.SoftReference; +import java.lang.ref.WeakReference; +import java.lang.reflect.Constructor; +import java.security.NoSuchAlgorithmException; +import java.security.Provider; +import java.security.Provider.Service; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; + +/** + * The base {@link Provider} implementation for security services made available by Elytron. + * + * @author Darran Lofthouse + * @author David M. Lloyd + */ +public abstract class WildFlyElytronBaseProvider extends VersionedProvider { + + protected static final String HTTP_SERVER_FACTORY_TYPE = "HttpServerAuthenticationMechanismFactory"; + protected static final String SASL_CLIENT_FACTORY_TYPE = "SaslClientFactory"; + protected static final String SASL_SERVER_FACTORY_TYPE = "SaslServerFactory"; + protected static final String PASSWORD_FACTORY_TYPE = "PasswordFactory"; + protected static final String ALG_PARAMS_TYPE = "AlgorithmParameters"; + protected static final List emptyList = Collections.emptyList(); + protected static final Map emptyMap = Collections.emptyMap(); + + private static final Collection MASKED_ALGORITHMS = Collections.unmodifiableCollection(Arrays.asList( + "masked-MD5-DES", "masked-MD5-DES-CBC-PKCS5", "masked-MD5-3DES", "masked-MD5-3DES-CBC-PKCS5", "masked-SHA1-DES-EDE", + "masked-SHA1-DES-EDE-CBC-PKCS5", "masked-SHA1-RC2-40", "masked-SHA1-RC2-40-CBC-PKCS5", "masked-SHA1-RC2-128", + "masked-SHA1-RC2-128-CBC-PKCS5", "masked-SHA1-RC4-40", "masked-SHA1-RC4-40-ECB", "masked-SHA1-RC4-128", + "masked-SHA1-RC4-128-ECB", "masked-HMAC-SHA1-AES-128", "masked-HMAC-SHA224-AES-128", "masked-HMAC-SHA256-AES-128", + "masked-HMAC-SHA384-AES-128", "masked-HMAC-SHA512-AES-128", "masked-HMAC-SHA1-AES-256", + "masked-HMAC-SHA224-AES-256", "masked-HMAC-SHA256-AES-256", "masked-HMAC-SHA384-AES-256", + "masked-HMAC-SHA512-AES-256")); + + protected WildFlyElytronBaseProvider(final String name, final String version, final String info) { + super(name, version, info); + } + + protected void putPasswordImplementations() { + putService(new Service(this, PASSWORD_FACTORY_TYPE, "clear", "org.wildfly.security.password.impl.PasswordFactorySpiImpl", emptyList, emptyMap)); + putService(new Service(this, PASSWORD_FACTORY_TYPE, "crypt-md5", "org.wildfly.security.password.impl.PasswordFactorySpiImpl", emptyList, emptyMap)); + putService(new Service(this, PASSWORD_FACTORY_TYPE, "sun-crypt-md5", "org.wildfly.security.password.impl.PasswordFactorySpiImpl", emptyList, emptyMap)); + putService(new Service(this, PASSWORD_FACTORY_TYPE, "sun-crypt-md5-bare-salt", "org.wildfly.security.password.impl.PasswordFactorySpiImpl", emptyList, emptyMap)); + putService(new Service(this, PASSWORD_FACTORY_TYPE, "crypt-sha-256", "org.wildfly.security.password.impl.PasswordFactorySpiImpl", emptyList, emptyMap)); + putService(new Service(this, PASSWORD_FACTORY_TYPE, "crypt-sha-512", "org.wildfly.security.password.impl.PasswordFactorySpiImpl", emptyList, emptyMap)); + putService(new Service(this, PASSWORD_FACTORY_TYPE, "simple-digest-md2", "org.wildfly.security.password.impl.PasswordFactorySpiImpl", emptyList, emptyMap)); + putService(new Service(this, PASSWORD_FACTORY_TYPE, "simple-digest-md5", "org.wildfly.security.password.impl.PasswordFactorySpiImpl", emptyList, emptyMap)); + putService(new Service(this, PASSWORD_FACTORY_TYPE, "simple-digest-sha-1", "org.wildfly.security.password.impl.PasswordFactorySpiImpl", emptyList, emptyMap)); + putService(new Service(this, PASSWORD_FACTORY_TYPE, "simple-digest-sha-256", "org.wildfly.security.password.impl.PasswordFactorySpiImpl", emptyList, emptyMap)); + putService(new Service(this, PASSWORD_FACTORY_TYPE, "simple-digest-sha-384", "org.wildfly.security.password.impl.PasswordFactorySpiImpl", emptyList, emptyMap)); + putService(new Service(this, PASSWORD_FACTORY_TYPE, "simple-digest-sha-512", "org.wildfly.security.password.impl.PasswordFactorySpiImpl", emptyList, emptyMap)); + putService(new Service(this, PASSWORD_FACTORY_TYPE, "digest-md5", "org.wildfly.security.password.impl.PasswordFactorySpiImpl", emptyList, emptyMap)); + putService(new Service(this, PASSWORD_FACTORY_TYPE, "digest-sha", "org.wildfly.security.password.impl.PasswordFactorySpiImpl", emptyList, emptyMap)); + putService(new Service(this, PASSWORD_FACTORY_TYPE, "digest-sha-256", "org.wildfly.security.password.impl.PasswordFactorySpiImpl", emptyList, emptyMap)); + putService(new Service(this, PASSWORD_FACTORY_TYPE, "digest-sha-384", "org.wildfly.security.password.impl.PasswordFactorySpiImpl", emptyList, emptyMap)); + putService(new Service(this, PASSWORD_FACTORY_TYPE, "digest-sha-512", "org.wildfly.security.password.impl.PasswordFactorySpiImpl", emptyList, emptyMap)); + putService(new Service(this, PASSWORD_FACTORY_TYPE, "digest-sha-512-256", "org.wildfly.security.password.impl.PasswordFactorySpiImpl", emptyList, emptyMap)); + putService(new Service(this, PASSWORD_FACTORY_TYPE, "password-salt-digest-md5", "org.wildfly.security.password.impl.PasswordFactorySpiImpl", emptyList, emptyMap)); + putService(new Service(this, PASSWORD_FACTORY_TYPE, "password-salt-digest-sha-1", "org.wildfly.security.password.impl.PasswordFactorySpiImpl", emptyList, emptyMap)); + putService(new Service(this, PASSWORD_FACTORY_TYPE, "password-salt-digest-sha-256", "org.wildfly.security.password.impl.PasswordFactorySpiImpl", emptyList, emptyMap)); + putService(new Service(this, PASSWORD_FACTORY_TYPE, "password-salt-digest-sha-384", "org.wildfly.security.password.impl.PasswordFactorySpiImpl", emptyList, emptyMap)); + putService(new Service(this, PASSWORD_FACTORY_TYPE, "password-salt-digest-sha-512", "org.wildfly.security.password.impl.PasswordFactorySpiImpl", emptyList, emptyMap)); + putService(new Service(this, PASSWORD_FACTORY_TYPE, "salt-password-digest-md5", "org.wildfly.security.password.impl.PasswordFactorySpiImpl", emptyList, emptyMap)); + putService(new Service(this, PASSWORD_FACTORY_TYPE, "salt-password-digest-sha-1", "org.wildfly.security.password.impl.PasswordFactorySpiImpl", emptyList, emptyMap)); + putService(new Service(this, PASSWORD_FACTORY_TYPE, "salt-password-digest-sha-256", "org.wildfly.security.password.impl.PasswordFactorySpiImpl", emptyList, emptyMap)); + putService(new Service(this, PASSWORD_FACTORY_TYPE, "salt-password-digest-sha-384", "org.wildfly.security.password.impl.PasswordFactorySpiImpl", emptyList, emptyMap)); + putService(new Service(this, PASSWORD_FACTORY_TYPE, "salt-password-digest-sha-512", "org.wildfly.security.password.impl.PasswordFactorySpiImpl", emptyList, emptyMap)); + putService(new Service(this, PASSWORD_FACTORY_TYPE, "crypt-des", "org.wildfly.security.password.impl.PasswordFactorySpiImpl", emptyList, emptyMap)); + putService(new Service(this, PASSWORD_FACTORY_TYPE, "bsd-crypt-des", "org.wildfly.security.password.impl.PasswordFactorySpiImpl", emptyList, emptyMap)); + putService(new Service(this, PASSWORD_FACTORY_TYPE, "bcrypt", "org.wildfly.security.password.impl.PasswordFactorySpiImpl", emptyList, emptyMap)); + putService(new Service(this, PASSWORD_FACTORY_TYPE, "scram-sha-1", "org.wildfly.security.password.impl.PasswordFactorySpiImpl", emptyList, emptyMap)); + putService(new Service(this, PASSWORD_FACTORY_TYPE, "scram-sha-256", "org.wildfly.security.password.impl.PasswordFactorySpiImpl", emptyList, emptyMap)); + putService(new Service(this, PASSWORD_FACTORY_TYPE, "scram-sha-384", "org.wildfly.security.password.impl.PasswordFactorySpiImpl", emptyList, emptyMap)); + putService(new Service(this, PASSWORD_FACTORY_TYPE, "scram-sha-512", "org.wildfly.security.password.impl.PasswordFactorySpiImpl", emptyList, emptyMap)); + putService(new Service(this, PASSWORD_FACTORY_TYPE, "otp-md5", "org.wildfly.security.password.impl.PasswordFactorySpiImpl", emptyList, emptyMap)); + putService(new Service(this, PASSWORD_FACTORY_TYPE, "otp-sha1", "org.wildfly.security.password.impl.PasswordFactorySpiImpl", emptyList, emptyMap)); + putService(new Service(this, PASSWORD_FACTORY_TYPE, "otp-sha256", "org.wildfly.security.password.impl.PasswordFactorySpiImpl", emptyList, emptyMap)); + putService(new Service(this, PASSWORD_FACTORY_TYPE, "otp-sha384", "org.wildfly.security.password.impl.PasswordFactorySpiImpl", emptyList, emptyMap)); + putService(new Service(this, PASSWORD_FACTORY_TYPE, "otp-sha512", "org.wildfly.security.password.impl.PasswordFactorySpiImpl", emptyList, emptyMap)); + putMakedPasswordImplementations(this::putService, this); + } + + static void putMakedPasswordImplementations(Consumer consumer, Provider provider) { + for (String algorithm : MASKED_ALGORITHMS) { + consumer.accept(new Service(provider, PASSWORD_FACTORY_TYPE, algorithm, "org.wildfly.security.password.impl.PasswordFactorySpiImpl", emptyList, emptyMap)); + } + } + + protected void putAlgorithmParametersImplementations() { + putService(new Service(this, ALG_PARAMS_TYPE, "RSA", "org.wildfly.security.key.RSAParameterSpiImpl", emptyList, emptyMap)); + + putService(new Service(this, ALG_PARAMS_TYPE, "crypt-md5", "org.wildfly.security.password.impl.SaltedPasswordAlgorithmParametersSpiImpl", emptyList, emptyMap)); + putService(new Service(this, ALG_PARAMS_TYPE, "sun-crypt-md5", "org.wildfly.security.password.impl.IteratedSaltedPasswordAlgorithmParametersSpiImpl", emptyList, emptyMap)); + putService(new Service(this, ALG_PARAMS_TYPE, "sun-crypt-md5-bare-salt", "org.wildfly.security.password.impl.IteratedSaltedPasswordAlgorithmParametersSpiImpl", emptyList, emptyMap)); + putService(new Service(this, ALG_PARAMS_TYPE, "crypt-sha-256", "org.wildfly.security.password.impl.IteratedSaltedPasswordAlgorithmParametersSpiImpl", emptyList, emptyMap)); + putService(new Service(this, ALG_PARAMS_TYPE, "crypt-sha-512", "org.wildfly.security.password.impl.IteratedSaltedPasswordAlgorithmParametersSpiImpl", emptyList, emptyMap)); + putService(new Service(this, ALG_PARAMS_TYPE, "digest-md5", "org.wildfly.security.password.impl.DigestPasswordAlgorithmParametersSpiImpl", emptyList, emptyMap)); + putService(new Service(this, ALG_PARAMS_TYPE, "digest-sha", "org.wildfly.security.password.impl.DigestPasswordAlgorithmParametersSpiImpl", emptyList, emptyMap)); + putService(new Service(this, ALG_PARAMS_TYPE, "digest-sha-256", "org.wildfly.security.password.impl.DigestPasswordAlgorithmParametersSpiImpl", emptyList, emptyMap)); + putService(new Service(this, ALG_PARAMS_TYPE, "digest-sha-384", "org.wildfly.security.password.impl.DigestPasswordAlgorithmParametersSpiImpl", emptyList, emptyMap)); + putService(new Service(this, ALG_PARAMS_TYPE, "digest-sha-512", "org.wildfly.security.password.impl.DigestPasswordAlgorithmParametersSpiImpl", emptyList, emptyMap)); + putService(new Service(this, ALG_PARAMS_TYPE, "digest-sha-512-256", "org.wildfly.security.password.impl.DigestPasswordAlgorithmParametersSpiImpl", emptyList, emptyMap)); + putService(new Service(this, ALG_PARAMS_TYPE, "password-salt-digest-md5", "org.wildfly.security.password.impl.SaltedPasswordAlgorithmParametersSpiImpl", emptyList, emptyMap)); + putService(new Service(this, ALG_PARAMS_TYPE, "password-salt-digest-sha-1", "org.wildfly.security.password.impl.SaltedPasswordAlgorithmParametersSpiImpl", emptyList, emptyMap)); + putService(new Service(this, ALG_PARAMS_TYPE, "password-salt-digest-sha-256", "org.wildfly.security.password.impl.SaltedPasswordAlgorithmParametersSpiImpl", emptyList, emptyMap)); + putService(new Service(this, ALG_PARAMS_TYPE, "password-salt-digest-sha-384", "org.wildfly.security.password.impl.SaltedPasswordAlgorithmParametersSpiImpl", emptyList, emptyMap)); + putService(new Service(this, ALG_PARAMS_TYPE, "password-salt-digest-sha-512", "org.wildfly.security.password.impl.SaltedPasswordAlgorithmParametersSpiImpl", emptyList, emptyMap)); + putService(new Service(this, ALG_PARAMS_TYPE, "salt-password-digest-md5", "org.wildfly.security.password.impl.SaltedPasswordAlgorithmParametersSpiImpl", emptyList, emptyMap)); + putService(new Service(this, ALG_PARAMS_TYPE, "salt-password-digest-sha-1", "org.wildfly.security.password.impl.SaltedPasswordAlgorithmParametersSpiImpl", emptyList, emptyMap)); + putService(new Service(this, ALG_PARAMS_TYPE, "salt-password-digest-sha-256", "org.wildfly.security.password.impl.SaltedPasswordAlgorithmParametersSpiImpl", emptyList, emptyMap)); + putService(new Service(this, ALG_PARAMS_TYPE, "salt-password-digest-sha-384", "org.wildfly.security.password.impl.SaltedPasswordAlgorithmParametersSpiImpl", emptyList, emptyMap)); + putService(new Service(this, ALG_PARAMS_TYPE, "salt-password-digest-sha-512", "org.wildfly.security.password.impl.SaltedPasswordAlgorithmParametersSpiImpl", emptyList, emptyMap)); + putService(new Service(this, ALG_PARAMS_TYPE, "crypt-des", "org.wildfly.security.password.impl.SaltedPasswordAlgorithmParametersSpiImpl", emptyList, emptyMap)); + putService(new Service(this, ALG_PARAMS_TYPE, "bsd-crypt-des", "org.wildfly.security.password.impl.IteratedSaltedPasswordAlgorithmParametersSpiImpl", emptyList, emptyMap)); + putService(new Service(this, ALG_PARAMS_TYPE, "bcrypt", "org.wildfly.security.password.impl.IteratedSaltedPasswordAlgorithmParametersSpiImpl", emptyList, emptyMap)); + putService(new Service(this, ALG_PARAMS_TYPE, "scram-sha-1", "org.wildfly.security.password.impl.IteratedSaltedPasswordAlgorithmParametersSpiImpl", emptyList, emptyMap)); + putService(new Service(this, ALG_PARAMS_TYPE, "scram-sha-256", "org.wildfly.security.password.impl.IteratedSaltedPasswordAlgorithmParametersSpiImpl", emptyList, emptyMap)); + putService(new Service(this, ALG_PARAMS_TYPE, "scram-sha-384", "org.wildfly.security.password.impl.IteratedSaltedPasswordAlgorithmParametersSpiImpl", emptyList, emptyMap)); + putService(new Service(this, ALG_PARAMS_TYPE, "scram-sha-512", "org.wildfly.security.password.impl.IteratedSaltedPasswordAlgorithmParametersSpiImpl", emptyList, emptyMap)); + putService(new Service(this, ALG_PARAMS_TYPE, "otp-md5", "org.wildfly.security.password.impl.OneTimePasswordAlgorithmParametersSpiImpl", emptyList, emptyMap)); + putService(new Service(this, ALG_PARAMS_TYPE, "otp-sha1", "org.wildfly.security.password.impl.OneTimePasswordAlgorithmParametersSpiImpl", emptyList, emptyMap)); + putService(new Service(this, ALG_PARAMS_TYPE, "otp-sha256", "org.wildfly.security.password.impl.OneTimePasswordAlgorithmParametersSpiImpl", emptyList, emptyMap)); + putService(new Service(this, ALG_PARAMS_TYPE, "otp-sha384", "org.wildfly.security.password.impl.OneTimePasswordAlgorithmParametersSpiImpl", emptyList, emptyMap)); + putService(new Service(this, ALG_PARAMS_TYPE, "otp-sha512", "org.wildfly.security.password.impl.OneTimePasswordAlgorithmParametersSpiImpl", emptyList, emptyMap)); + putMakedAlgorithmParametersImplementations(this::putService, this); + } + + static void putMakedAlgorithmParametersImplementations(Consumer consumer, Provider provider) { + for (String algorithm : MASKED_ALGORITHMS) { + consumer.accept(new Service(provider, ALG_PARAMS_TYPE, algorithm, "org.wildfly.security.password.impl.MaskedPasswordAlgorithmParametersSpiImpl", emptyList, emptyMap)); + } + } + + protected class ProviderService extends Service { + + private final boolean withProvider; + private final boolean reUsable; + private volatile Reference> implementationClassRef; + private volatile Reference instance; + + public ProviderService(Provider provider, String type, String algorithm, String className, List aliases, Map attributes) { + this(provider, type, algorithm, className, aliases, attributes, true, false); + } + + public ProviderService(Provider provider, String type, String algorithm, String className, List aliases, Map attributes, boolean withProvider, boolean reUsable) { + super(provider, type, algorithm, className, aliases, attributes); + this.withProvider = withProvider; + this.reUsable = reUsable; + } + + @Override + public Object newInstance(Object constructorParameter) throws NoSuchAlgorithmException { + if (reUsable) { + Reference instance = this.instance; + Object response; + if (instance == null || (response = instance.get()) == null) { + synchronized(this) { + instance = this.instance; + if (instance == null || (response = instance.get()) == null) { + response = withProvider ? newInstance() : super.newInstance(constructorParameter); + this.instance = new SoftReference(response); + } + } + } + + return response; + } + + return withProvider ? newInstance() : super.newInstance(constructorParameter); + } + + private Object newInstance() throws NoSuchAlgorithmException { + Class implementationClass = getImplementationClass(); + + try { + Constructor constructor = implementationClass.getConstructor(Provider.class); + return constructor.newInstance(WildFlyElytronBaseProvider.this); + } catch (Exception e) { + throw log.noSuchAlgorithmCreateService(getType(), getAlgorithm(), e); + } + } + + private Class getImplementationClass() throws NoSuchAlgorithmException { + Reference> implementationClassRef = this.implementationClassRef; + Class implementationClass = implementationClassRef != null ? implementationClassRef.get() : null; + if (implementationClass == null) { + ClassLoader classLoader = WildFlyElytronBaseProvider.class.getClassLoader(); + try { + implementationClass = Class.forName(getClassName(), false, classLoader); + } catch (ClassNotFoundException e) { + throw log.noSuchAlgorithmCreateService(getType(), getAlgorithm(), e); + } + this.implementationClassRef = new WeakReference<>(implementationClass); + } + + return implementationClass; + } + + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/WildFlyElytronDigestProvider.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/WildFlyElytronDigestProvider.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/WildFlyElytronDigestProvider.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,56 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2018 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; + +import java.security.Provider; +import java.util.Collections; + +import org.kohsuke.MetaInfServices; + +/** + * Provider for Digest implementations. + * + * @author Farah Juma + * @deprecated Use org.wildfly.security.digest.WildFlyElytronDigestProvider instead + */ +@Deprecated +@MetaInfServices(Provider.class) +public final class WildFlyElytronDigestProvider extends WildFlyElytronBaseProvider { + + private static final long serialVersionUID = 2531323760912222262L; + private static WildFlyElytronDigestProvider INSTANCE = new WildFlyElytronDigestProvider(); + + /** + * Construct a new instance. + */ + public WildFlyElytronDigestProvider() { + super("WildFlyElytronDigestProvider", "1.0", "WildFly Elytron Digest Provider"); + putService(new Service(this, "MessageDigest", "SHA-512-256", "org.wildfly.security.digest.SHA512_256MessageDigest", Collections.emptyList(), Collections.emptyMap())); + } + + /** + * Get the Digest implementations provider instance. + * + * @return the Digest implementations provider instance + */ + public static WildFlyElytronDigestProvider getInstance() { + return INSTANCE; + } + +} Index: 3rdParty_sources/elytron/org/wildfly/security/WildFlyElytronHttpBasicProvider.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/WildFlyElytronHttpBasicProvider.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/WildFlyElytronHttpBasicProvider.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,55 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2018 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; + +import java.security.Provider; + +import org.kohsuke.MetaInfServices; + +/** + * Provider for the HTTP BASIC authentication mechanism. + * + * @author Farah Juma + * @deprecated Use org.wildfly.security.http.basic.WildFlyElytronHttpBasicProvider instead + */ +@Deprecated +@MetaInfServices(Provider.class) +public final class WildFlyElytronHttpBasicProvider extends WildFlyElytronBaseProvider { + + private static final long serialVersionUID = 3029961619967561017L; + private static WildFlyElytronHttpBasicProvider INSTANCE = new WildFlyElytronHttpBasicProvider(); + + /** + * Construct a new instance. + */ + public WildFlyElytronHttpBasicProvider() { + super("WildFlyElytronHttpBasicProvider", "1.0", "WildFly Elytron HTTP BASIC Provider"); + putService(new ProviderService(this, HTTP_SERVER_FACTORY_TYPE, "BASIC", "org.wildfly.security.http.basic.BasicMechanismFactory", emptyList, emptyMap, true, true)); + } + + /** + * Get the HTTP BASIC authentication mechanism provider instance. + * + * @return the HTTP BASIC authentication mechanism provider instance + */ + public static WildFlyElytronHttpBasicProvider getInstance() { + return INSTANCE; + } + +} Index: 3rdParty_sources/elytron/org/wildfly/security/WildFlyElytronHttpBearerProvider.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/WildFlyElytronHttpBearerProvider.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/WildFlyElytronHttpBearerProvider.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,55 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2018 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; + +import java.security.Provider; + +import org.kohsuke.MetaInfServices; + +/** + * Provider for the HTTP Bearer authentication mechanism. + * + * @author Farah Juma + * @deprecated use org.wildfly.security.http.bearer.WildFlyElytronHttpBearerProvider instead + */ +@Deprecated +@MetaInfServices(Provider.class) +public final class WildFlyElytronHttpBearerProvider extends WildFlyElytronBaseProvider { + + private static final long serialVersionUID = -797775107834905210L; + private static WildFlyElytronHttpBearerProvider INSTANCE = new WildFlyElytronHttpBearerProvider(); + + /** + * Construct a new instance. + */ + public WildFlyElytronHttpBearerProvider() { + super("WildFlyElytronHttpBearerProvider", "1.0", "WildFly Elytron HTTP Bearer Provider"); + putService(new ProviderService(this, HTTP_SERVER_FACTORY_TYPE, "BEARER_TOKEN", "org.wildfly.security.http.bearer.BearerMechanismFactory", emptyList, emptyMap, true, true)); + } + + /** + * Get the HTTP Bearer authentication mechanism provider instance. + * + * @return the HTTP Bearer authentication mechanism provider instance + */ + public static WildFlyElytronHttpBearerProvider getInstance() { + return INSTANCE; + } + +} Index: 3rdParty_sources/elytron/org/wildfly/security/WildFlyElytronHttpClientCertProvider.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/WildFlyElytronHttpClientCertProvider.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/WildFlyElytronHttpClientCertProvider.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,55 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2018 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; + +import java.security.Provider; + +import org.kohsuke.MetaInfServices; + +/** + * Provider for the HTTP CLIENT_CERT authentication mechanism. + * + * @author Farah Juma + * @deprecated Use org.wildfly.security.http.cert.WildFlyElytronHttpClientCertProvider + */ +@Deprecated +@MetaInfServices(Provider.class) +public final class WildFlyElytronHttpClientCertProvider extends WildFlyElytronBaseProvider { + + private static final long serialVersionUID = -4105163673151031877L; + private static WildFlyElytronHttpClientCertProvider INSTANCE = new WildFlyElytronHttpClientCertProvider(); + + /** + * Construct a new instance. + */ + public WildFlyElytronHttpClientCertProvider() { + super("WildFlyElytronHttpClientCertProvider", "1.0", "WildFly Elytron HTTP CLIENT_CERT Provider"); + putService(new ProviderService(this, HTTP_SERVER_FACTORY_TYPE, "CLIENT_CERT", "org.wildfly.security.http.cert.ClientCertMechanismFactory", emptyList, emptyMap, true, true)); + } + + /** + * Get the HTTP CLIENT_CERT authentication mechanism provider instance. + * + * @return the HTTP CLIENT_CERT authentication mechanism provider instance + */ + public static WildFlyElytronHttpClientCertProvider getInstance() { + return INSTANCE; + } + +} Index: 3rdParty_sources/elytron/org/wildfly/security/WildFlyElytronHttpDigestProvider.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/WildFlyElytronHttpDigestProvider.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/WildFlyElytronHttpDigestProvider.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,61 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2018 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; + +import java.security.Provider; +import java.util.Collections; + +import org.kohsuke.MetaInfServices; + +/** + * Provider for the HTTP DIGEST authentication mechanism. + * + * @author Farah Juma + * @deprecated Use org.wildfly.security.http.digest.WildFlyElytronHttpDigestProvider instead + */ +@Deprecated +@MetaInfServices(Provider.class) +public final class WildFlyElytronHttpDigestProvider extends WildFlyElytronBaseProvider { + + private static final long serialVersionUID = 7343476391300211681L; + private static WildFlyElytronHttpDigestProvider INSTANCE = new WildFlyElytronHttpDigestProvider(); + + /** + * Construct a new instance. + */ + public WildFlyElytronHttpDigestProvider() { + super("WildFlyElytronHttpDigestProvider", "1.0", "WildFly Elytron HTTP DIGEST Provider"); + putService(new ProviderService(this, HTTP_SERVER_FACTORY_TYPE, "DIGEST", "org.wildfly.security.http.digest.DigestMechanismFactory", emptyList, emptyMap, true, true)); + putService(new ProviderService(this, HTTP_SERVER_FACTORY_TYPE, "DIGEST-SHA-256", "org.wildfly.security.http.digest.DigestMechanismFactory", emptyList, emptyMap, true, true)); + putService(new ProviderService(this, HTTP_SERVER_FACTORY_TYPE, "DIGEST-SHA-512-256", "org.wildfly.security.http.digest.DigestMechanismFactory", emptyList, emptyMap, true, true)); + + putService(new Service(this, "MessageDigest", "SHA-512-256", "org.wildfly.security.digest.SHA512_256MessageDigest", Collections.emptyList(), Collections.emptyMap())); + putService(new Service(this, PASSWORD_FACTORY_TYPE, "clear", "org.wildfly.security.password.impl.PasswordFactorySpiImpl", emptyList, emptyMap)); + } + + /** + * Get the HTTP DIGEST authentication mechanism provider instance. + * + * @return the HTTP DIGEST authentication mechanism provider instance + */ + public static WildFlyElytronHttpDigestProvider getInstance() { + return INSTANCE; + } + +} Index: 3rdParty_sources/elytron/org/wildfly/security/WildFlyElytronHttpFormProvider.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/WildFlyElytronHttpFormProvider.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/WildFlyElytronHttpFormProvider.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,55 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2018 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; + +import java.security.Provider; + +import org.kohsuke.MetaInfServices; + +/** + * Provider for the HTTP FORM authentication mechanism. + * + * @author Farah Juma + * @deprecated Use org.wildfly.security.http.form.WildFlyElytronHttpFormProvider instead + */ +@Deprecated +@MetaInfServices(Provider.class) +public final class WildFlyElytronHttpFormProvider extends WildFlyElytronBaseProvider { + + private static final long serialVersionUID = 3872696509387755963L; + private static WildFlyElytronHttpFormProvider INSTANCE = new WildFlyElytronHttpFormProvider(); + + /** + * Construct a new instance. + */ + public WildFlyElytronHttpFormProvider() { + super("WildFlyElytronHttpFormProvider", "1.0", "WildFly Elytron HTTP FORM Provider"); + putService(new ProviderService(this, HTTP_SERVER_FACTORY_TYPE, "FORM", "org.wildfly.security.http.form.FormMechanismFactory", emptyList, emptyMap, true, true)); + } + + /** + * Get the HTTP FORM authentication mechanism provider instance. + * + * @return the HTTP FORM authentication mechanism provider instance + */ + public static WildFlyElytronHttpFormProvider getInstance() { + return INSTANCE; + } + +} Index: 3rdParty_sources/elytron/org/wildfly/security/WildFlyElytronHttpSpnegoProvider.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/WildFlyElytronHttpSpnegoProvider.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/WildFlyElytronHttpSpnegoProvider.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,55 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2018 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; + +import java.security.Provider; + +import org.kohsuke.MetaInfServices; + +/** + * Provider for the HTTP SPNEGO authentication mechanism. + * + * @author Farah Juma + * @deprecated use org.wildfly.security.http.spnego.WildFlyElytronHttpSpnegoProvider instead + */ +@Deprecated +@MetaInfServices(Provider.class) +public final class WildFlyElytronHttpSpnegoProvider extends WildFlyElytronBaseProvider { + + private static final long serialVersionUID = 9211317885580156246L; + private static WildFlyElytronHttpSpnegoProvider INSTANCE = new WildFlyElytronHttpSpnegoProvider(); + + /** + * Construct a new instance. + */ + public WildFlyElytronHttpSpnegoProvider() { + super("WildFlyElytronHttpSpnegoProvider", "1.0", "WildFly Elytron HTTP SPNEGO Provider"); + putService(new ProviderService(this, HTTP_SERVER_FACTORY_TYPE, "SPNEGO", "org.wildfly.security.http.spnego.SpnegoMechanismFactory", emptyList, emptyMap, true, true)); + } + + /** + * Get the HTTP SPNEGO authentication mechanism provider instance. + * + * @return the HTTP SPNEGO authentication mechanism provider instance + */ + public static WildFlyElytronHttpSpnegoProvider getInstance() { + return INSTANCE; + } + +} Index: 3rdParty_sources/elytron/org/wildfly/security/WildFlyElytronProvider.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/WildFlyElytronProvider.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/WildFlyElytronProvider.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,330 @@ +/* + * 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; + +import static org.wildfly.security.ElytronMessages.log; + +import java.lang.ref.Reference; +import java.lang.ref.SoftReference; +import java.lang.ref.WeakReference; +import java.lang.reflect.Constructor; +import java.security.NoSuchAlgorithmException; +import java.security.Provider; +import java.util.Collections; +import java.util.List; +import java.util.Map; + +import org.kohsuke.MetaInfServices; + + +/** + * The {@link Provider} implementation covering all security services made available by Elytron. + * + * @author Darran Lofthouse + * @author David M. Lloyd + */ +@MetaInfServices(Provider.class) +@Deprecated +public class WildFlyElytronProvider extends VersionedProvider { + + private static final long serialVersionUID = 1267015094996624988L; + + private static final String HTTP_SERVER_FACTORY_TYPE = "HttpServerAuthenticationMechanismFactory"; + + private static final String SASL_CLIENT_FACTORY_TYPE = "SaslClientFactory"; + + private static final String SASL_SERVER_FACTORY_TYPE = "SaslServerFactory"; + + private static final String PASSWORD_FACTORY_TYPE = "PasswordFactory"; + + private static final String ALG_PARAMS_TYPE = "AlgorithmParameters"; + + /** + * Default constructor for this security provider. + */ + public WildFlyElytronProvider() { + super("WildFlyElytron", "1.0", "WildFly Elytron Provider"); + + putHttpAuthenticationMechanismImplementations(); + putKeyStoreImplementations(); + putPasswordImplementations(); + putSaslMechanismImplementations(); + putCredentialStoreProviderImplementations(); + putAlgorithmParametersImplementations(); + putService(new Service(this, "SecretKeyFactory", "1.2.840.113549.1.7.1", "org.wildfly.security.key.RawSecretKeyFactory", Collections.emptyList(), Collections.emptyMap())); + putService(new Service(this, "MessageDigest", "SHA-512-256", "org.wildfly.security.digest.SHA512_256MessageDigest", Collections.emptyList(), Collections.emptyMap())); + } + + private void putAlgorithmParametersImplementations() { + final List emptyList = Collections.emptyList(); + final Map emptyMap = Collections.emptyMap(); + + putService(new Service(this, ALG_PARAMS_TYPE, "RSA", "org.wildfly.security.key.RSAParameterSpiImpl", emptyList, emptyMap)); + + putService(new Service(this, ALG_PARAMS_TYPE, "crypt-md5", "org.wildfly.security.password.impl.SaltedPasswordAlgorithmParametersSpiImpl", emptyList, emptyMap)); + putService(new Service(this, ALG_PARAMS_TYPE, "sun-crypt-md5", "org.wildfly.security.password.impl.IteratedSaltedPasswordAlgorithmParametersSpiImpl", emptyList, emptyMap)); + putService(new Service(this, ALG_PARAMS_TYPE, "sun-crypt-md5-bare-salt", "org.wildfly.security.password.impl.IteratedSaltedPasswordAlgorithmParametersSpiImpl", emptyList, emptyMap)); + putService(new Service(this, ALG_PARAMS_TYPE, "crypt-sha-256", "org.wildfly.security.password.impl.IteratedSaltedPasswordAlgorithmParametersSpiImpl", emptyList, emptyMap)); + putService(new Service(this, ALG_PARAMS_TYPE, "crypt-sha-512", "org.wildfly.security.password.impl.IteratedSaltedPasswordAlgorithmParametersSpiImpl", emptyList, emptyMap)); + putService(new Service(this, ALG_PARAMS_TYPE, "digest-md5", "org.wildfly.security.password.impl.DigestPasswordAlgorithmParametersSpiImpl", emptyList, emptyMap)); + putService(new Service(this, ALG_PARAMS_TYPE, "digest-sha", "org.wildfly.security.password.impl.DigestPasswordAlgorithmParametersSpiImpl", emptyList, emptyMap)); + putService(new Service(this, ALG_PARAMS_TYPE, "digest-sha-256", "org.wildfly.security.password.impl.DigestPasswordAlgorithmParametersSpiImpl", emptyList, emptyMap)); + putService(new Service(this, ALG_PARAMS_TYPE, "digest-sha-384", "org.wildfly.security.password.impl.DigestPasswordAlgorithmParametersSpiImpl", emptyList, emptyMap)); + putService(new Service(this, ALG_PARAMS_TYPE, "digest-sha-512", "org.wildfly.security.password.impl.DigestPasswordAlgorithmParametersSpiImpl", emptyList, emptyMap)); + putService(new Service(this, ALG_PARAMS_TYPE, "digest-sha-512-256", "org.wildfly.security.password.impl.DigestPasswordAlgorithmParametersSpiImpl", emptyList, emptyMap)); + putService(new Service(this, ALG_PARAMS_TYPE, "password-salt-digest-md5", "org.wildfly.security.password.impl.SaltedPasswordAlgorithmParametersSpiImpl", emptyList, emptyMap)); + putService(new Service(this, ALG_PARAMS_TYPE, "password-salt-digest-sha-1", "org.wildfly.security.password.impl.SaltedPasswordAlgorithmParametersSpiImpl", emptyList, emptyMap)); + putService(new Service(this, ALG_PARAMS_TYPE, "password-salt-digest-sha-256", "org.wildfly.security.password.impl.SaltedPasswordAlgorithmParametersSpiImpl", emptyList, emptyMap)); + putService(new Service(this, ALG_PARAMS_TYPE, "password-salt-digest-sha-384", "org.wildfly.security.password.impl.SaltedPasswordAlgorithmParametersSpiImpl", emptyList, emptyMap)); + putService(new Service(this, ALG_PARAMS_TYPE, "password-salt-digest-sha-512", "org.wildfly.security.password.impl.SaltedPasswordAlgorithmParametersSpiImpl", emptyList, emptyMap)); + putService(new Service(this, ALG_PARAMS_TYPE, "salt-password-digest-md5", "org.wildfly.security.password.impl.SaltedPasswordAlgorithmParametersSpiImpl", emptyList, emptyMap)); + putService(new Service(this, ALG_PARAMS_TYPE, "salt-password-digest-sha-1", "org.wildfly.security.password.impl.SaltedPasswordAlgorithmParametersSpiImpl", emptyList, emptyMap)); + putService(new Service(this, ALG_PARAMS_TYPE, "salt-password-digest-sha-256", "org.wildfly.security.password.impl.SaltedPasswordAlgorithmParametersSpiImpl", emptyList, emptyMap)); + putService(new Service(this, ALG_PARAMS_TYPE, "salt-password-digest-sha-384", "org.wildfly.security.password.impl.SaltedPasswordAlgorithmParametersSpiImpl", emptyList, emptyMap)); + putService(new Service(this, ALG_PARAMS_TYPE, "salt-password-digest-sha-512", "org.wildfly.security.password.impl.SaltedPasswordAlgorithmParametersSpiImpl", emptyList, emptyMap)); + putService(new Service(this, ALG_PARAMS_TYPE, "crypt-des", "org.wildfly.security.password.impl.SaltedPasswordAlgorithmParametersSpiImpl", emptyList, emptyMap)); + putService(new Service(this, ALG_PARAMS_TYPE, "bsd-crypt-des", "org.wildfly.security.password.impl.IteratedSaltedPasswordAlgorithmParametersSpiImpl", emptyList, emptyMap)); + putService(new Service(this, ALG_PARAMS_TYPE, "bcrypt", "org.wildfly.security.password.impl.IteratedSaltedPasswordAlgorithmParametersSpiImpl", emptyList, emptyMap)); + putService(new Service(this, ALG_PARAMS_TYPE, "scram-sha-1", "org.wildfly.security.password.impl.IteratedSaltedPasswordAlgorithmParametersSpiImpl", emptyList, emptyMap)); + putService(new Service(this, ALG_PARAMS_TYPE, "scram-sha-256", "org.wildfly.security.password.impl.IteratedSaltedPasswordAlgorithmParametersSpiImpl", emptyList, emptyMap)); + putService(new Service(this, ALG_PARAMS_TYPE, "scram-sha-384", "org.wildfly.security.password.impl.IteratedSaltedPasswordAlgorithmParametersSpiImpl", emptyList, emptyMap)); + putService(new Service(this, ALG_PARAMS_TYPE, "scram-sha-512", "org.wildfly.security.password.impl.IteratedSaltedPasswordAlgorithmParametersSpiImpl", emptyList, emptyMap)); + putService(new Service(this, ALG_PARAMS_TYPE, "otp-md5", "org.wildfly.security.password.impl.OneTimePasswordAlgorithmParametersSpiImpl", emptyList, emptyMap)); + putService(new Service(this, ALG_PARAMS_TYPE, "otp-sha1", "org.wildfly.security.password.impl.OneTimePasswordAlgorithmParametersSpiImpl", emptyList, emptyMap)); + putService(new Service(this, ALG_PARAMS_TYPE, "otp-sha256", "org.wildfly.security.password.impl.OneTimePasswordAlgorithmParametersSpiImpl", emptyList, emptyMap)); + putService(new Service(this, ALG_PARAMS_TYPE, "otp-sha384", "org.wildfly.security.password.impl.OneTimePasswordAlgorithmParametersSpiImpl", emptyList, emptyMap)); + putService(new Service(this, ALG_PARAMS_TYPE, "otp-sha512", "org.wildfly.security.password.impl.OneTimePasswordAlgorithmParametersSpiImpl", emptyList, emptyMap)); + WildFlyElytronBaseProvider.putMakedAlgorithmParametersImplementations(this::putService, this); + } + + private void putKeyStoreImplementations() { + final List emptyList = Collections.emptyList(); + final Map emptyMap = Collections.emptyMap(); + + putService(new Service(this, "KeyStore", "PasswordFile", "org.wildfly.security.keystore.PasswordKeyStoreSpi", emptyList, emptyMap)); + } + + private void putHttpAuthenticationMechanismImplementations() { + final List emptyList = Collections.emptyList(); + final Map emptyMap = Collections.emptyMap(); + + putService(new ProviderService(this, HTTP_SERVER_FACTORY_TYPE, "BASIC", "org.wildfly.security.http.basic.BasicMechanismFactory", emptyList, emptyMap, true, true)); + putService(new ProviderService(this, HTTP_SERVER_FACTORY_TYPE, "CLIENT_CERT", "org.wildfly.security.http.cert.ClientCertMechanismFactory", emptyList, emptyMap, true, true)); + putService(new ProviderService(this, HTTP_SERVER_FACTORY_TYPE, "DIGEST", "org.wildfly.security.http.digest.DigestMechanismFactory", emptyList, emptyMap, true, true)); + putService(new ProviderService(this, HTTP_SERVER_FACTORY_TYPE, "DIGEST-SHA-256", "org.wildfly.security.http.digest.DigestMechanismFactory", emptyList, emptyMap, true, true)); + putService(new ProviderService(this, HTTP_SERVER_FACTORY_TYPE, "DIGEST-SHA-512-256", "org.wildfly.security.http.digest.DigestMechanismFactory", emptyList, emptyMap, true, true)); + putService(new ProviderService(this, HTTP_SERVER_FACTORY_TYPE, "EXTERNAL", "org.wildfly.security.http.external.ExternalMechanismFactory", emptyList, emptyMap, true, true)); + putService(new ProviderService(this, HTTP_SERVER_FACTORY_TYPE, "FORM", "org.wildfly.security.http.form.FormMechanismFactory", emptyList, emptyMap, true, true)); + putService(new ProviderService(this, HTTP_SERVER_FACTORY_TYPE, "SPNEGO", "org.wildfly.security.http.spnego.SpnegoMechanismFactory", emptyList, emptyMap, true, true)); + + putService(new ProviderService(this, HTTP_SERVER_FACTORY_TYPE, "BEARER_TOKEN", "org.wildfly.security.http.bearer.BearerMechanismFactory", emptyList, emptyMap, true, true)); + } + + private void putPasswordImplementations() { + final List emptyList = Collections.emptyList(); + final Map emptyMap = Collections.emptyMap(); + + putService(new Service(this, PASSWORD_FACTORY_TYPE, "clear", "org.wildfly.security.password.impl.PasswordFactorySpiImpl", emptyList, emptyMap)); + putService(new Service(this, PASSWORD_FACTORY_TYPE, "crypt-md5", "org.wildfly.security.password.impl.PasswordFactorySpiImpl", emptyList, emptyMap)); + putService(new Service(this, PASSWORD_FACTORY_TYPE, "sun-crypt-md5", "org.wildfly.security.password.impl.PasswordFactorySpiImpl", emptyList, emptyMap)); + putService(new Service(this, PASSWORD_FACTORY_TYPE, "sun-crypt-md5-bare-salt", "org.wildfly.security.password.impl.PasswordFactorySpiImpl", emptyList, emptyMap)); + putService(new Service(this, PASSWORD_FACTORY_TYPE, "crypt-sha-256", "org.wildfly.security.password.impl.PasswordFactorySpiImpl", emptyList, emptyMap)); + putService(new Service(this, PASSWORD_FACTORY_TYPE, "crypt-sha-512", "org.wildfly.security.password.impl.PasswordFactorySpiImpl", emptyList, emptyMap)); + putService(new Service(this, PASSWORD_FACTORY_TYPE, "simple-digest-md2", "org.wildfly.security.password.impl.PasswordFactorySpiImpl", emptyList, emptyMap)); + putService(new Service(this, PASSWORD_FACTORY_TYPE, "simple-digest-md5", "org.wildfly.security.password.impl.PasswordFactorySpiImpl", emptyList, emptyMap)); + putService(new Service(this, PASSWORD_FACTORY_TYPE, "simple-digest-sha-1", "org.wildfly.security.password.impl.PasswordFactorySpiImpl", emptyList, emptyMap)); + putService(new Service(this, PASSWORD_FACTORY_TYPE, "simple-digest-sha-256", "org.wildfly.security.password.impl.PasswordFactorySpiImpl", emptyList, emptyMap)); + putService(new Service(this, PASSWORD_FACTORY_TYPE, "simple-digest-sha-384", "org.wildfly.security.password.impl.PasswordFactorySpiImpl", emptyList, emptyMap)); + putService(new Service(this, PASSWORD_FACTORY_TYPE, "simple-digest-sha-512", "org.wildfly.security.password.impl.PasswordFactorySpiImpl", emptyList, emptyMap)); + putService(new Service(this, PASSWORD_FACTORY_TYPE, "digest-md5", "org.wildfly.security.password.impl.PasswordFactorySpiImpl", emptyList, emptyMap)); + putService(new Service(this, PASSWORD_FACTORY_TYPE, "digest-sha", "org.wildfly.security.password.impl.PasswordFactorySpiImpl", emptyList, emptyMap)); + putService(new Service(this, PASSWORD_FACTORY_TYPE, "digest-sha-256", "org.wildfly.security.password.impl.PasswordFactorySpiImpl", emptyList, emptyMap)); + putService(new Service(this, PASSWORD_FACTORY_TYPE, "digest-sha-384", "org.wildfly.security.password.impl.PasswordFactorySpiImpl", emptyList, emptyMap)); + putService(new Service(this, PASSWORD_FACTORY_TYPE, "digest-sha-512", "org.wildfly.security.password.impl.PasswordFactorySpiImpl", emptyList, emptyMap)); + putService(new Service(this, PASSWORD_FACTORY_TYPE, "digest-sha-512-256", "org.wildfly.security.password.impl.PasswordFactorySpiImpl", emptyList, emptyMap)); + putService(new Service(this, PASSWORD_FACTORY_TYPE, "password-salt-digest-md5", "org.wildfly.security.password.impl.PasswordFactorySpiImpl", emptyList, emptyMap)); + putService(new Service(this, PASSWORD_FACTORY_TYPE, "password-salt-digest-sha-1", "org.wildfly.security.password.impl.PasswordFactorySpiImpl", emptyList, emptyMap)); + putService(new Service(this, PASSWORD_FACTORY_TYPE, "password-salt-digest-sha-256", "org.wildfly.security.password.impl.PasswordFactorySpiImpl", emptyList, emptyMap)); + putService(new Service(this, PASSWORD_FACTORY_TYPE, "password-salt-digest-sha-384", "org.wildfly.security.password.impl.PasswordFactorySpiImpl", emptyList, emptyMap)); + putService(new Service(this, PASSWORD_FACTORY_TYPE, "password-salt-digest-sha-512", "org.wildfly.security.password.impl.PasswordFactorySpiImpl", emptyList, emptyMap)); + putService(new Service(this, PASSWORD_FACTORY_TYPE, "salt-password-digest-md5", "org.wildfly.security.password.impl.PasswordFactorySpiImpl", emptyList, emptyMap)); + putService(new Service(this, PASSWORD_FACTORY_TYPE, "salt-password-digest-sha-1", "org.wildfly.security.password.impl.PasswordFactorySpiImpl", emptyList, emptyMap)); + putService(new Service(this, PASSWORD_FACTORY_TYPE, "salt-password-digest-sha-256", "org.wildfly.security.password.impl.PasswordFactorySpiImpl", emptyList, emptyMap)); + putService(new Service(this, PASSWORD_FACTORY_TYPE, "salt-password-digest-sha-384", "org.wildfly.security.password.impl.PasswordFactorySpiImpl", emptyList, emptyMap)); + putService(new Service(this, PASSWORD_FACTORY_TYPE, "salt-password-digest-sha-512", "org.wildfly.security.password.impl.PasswordFactorySpiImpl", emptyList, emptyMap)); + putService(new Service(this, PASSWORD_FACTORY_TYPE, "crypt-des", "org.wildfly.security.password.impl.PasswordFactorySpiImpl", emptyList, emptyMap)); + putService(new Service(this, PASSWORD_FACTORY_TYPE, "bsd-crypt-des", "org.wildfly.security.password.impl.PasswordFactorySpiImpl", emptyList, emptyMap)); + putService(new Service(this, PASSWORD_FACTORY_TYPE, "bcrypt", "org.wildfly.security.password.impl.PasswordFactorySpiImpl", emptyList, emptyMap)); + putService(new Service(this, PASSWORD_FACTORY_TYPE, "scram-sha-1", "org.wildfly.security.password.impl.PasswordFactorySpiImpl", emptyList, emptyMap)); + putService(new Service(this, PASSWORD_FACTORY_TYPE, "scram-sha-256", "org.wildfly.security.password.impl.PasswordFactorySpiImpl", emptyList, emptyMap)); + putService(new Service(this, PASSWORD_FACTORY_TYPE, "scram-sha-384", "org.wildfly.security.password.impl.PasswordFactorySpiImpl", emptyList, emptyMap)); + putService(new Service(this, PASSWORD_FACTORY_TYPE, "scram-sha-512", "org.wildfly.security.password.impl.PasswordFactorySpiImpl", emptyList, emptyMap)); + putService(new Service(this, PASSWORD_FACTORY_TYPE, "otp-md5", "org.wildfly.security.password.impl.PasswordFactorySpiImpl", emptyList, emptyMap)); + putService(new Service(this, PASSWORD_FACTORY_TYPE, "otp-sha1", "org.wildfly.security.password.impl.PasswordFactorySpiImpl", emptyList, emptyMap)); + putService(new Service(this, PASSWORD_FACTORY_TYPE, "otp-sha256", "org.wildfly.security.password.impl.PasswordFactorySpiImpl", emptyList, emptyMap)); + putService(new Service(this, PASSWORD_FACTORY_TYPE, "otp-sha384", "org.wildfly.security.password.impl.PasswordFactorySpiImpl", emptyList, emptyMap)); + putService(new Service(this, PASSWORD_FACTORY_TYPE, "otp-sha512", "org.wildfly.security.password.impl.PasswordFactorySpiImpl", emptyList, emptyMap)); + WildFlyElytronBaseProvider.putMakedPasswordImplementations(this::putService, this); + } + + private void putSaslMechanismImplementations() { + final List emptyList = Collections.emptyList(); + final Map emptyMap = Collections.emptyMap(); + + putService(new ProviderService(this, SASL_SERVER_FACTORY_TYPE, "ANONYMOUS", "org.wildfly.security.sasl.anonymous.AnonymousServerFactory", emptyList, emptyMap, false, true)); + putService(new ProviderService(this, SASL_SERVER_FACTORY_TYPE, "DIGEST-SHA-512", "org.wildfly.security.sasl.digest.DigestServerFactory", emptyList, emptyMap, true, true)); + putService(new ProviderService(this, SASL_SERVER_FACTORY_TYPE, "DIGEST-SHA-512-256", "org.wildfly.security.sasl.digest.DigestServerFactory", emptyList, emptyMap, true, true)); + putService(new ProviderService(this, SASL_SERVER_FACTORY_TYPE, "DIGEST-SHA-256", "org.wildfly.security.sasl.digest.DigestServerFactory", emptyList, emptyMap, true, true)); + putService(new ProviderService(this, SASL_SERVER_FACTORY_TYPE, "DIGEST-SHA-384", "org.wildfly.security.sasl.digest.DigestServerFactory", emptyList, emptyMap, true, true)); + putService(new ProviderService(this, SASL_SERVER_FACTORY_TYPE, "DIGEST-SHA", "org.wildfly.security.sasl.digest.DigestServerFactory", emptyList, emptyMap, true, true)); + putService(new ProviderService(this, SASL_SERVER_FACTORY_TYPE, "DIGEST-MD5", "org.wildfly.security.sasl.digest.DigestServerFactory", emptyList, emptyMap, true, true)); + putService(new ProviderService(this, SASL_SERVER_FACTORY_TYPE, "9798-U-RSA-SHA1-ENC", "org.wildfly.security.sasl.entity.EntitySaslServerFactory", emptyList, emptyMap, false, true)); + putService(new ProviderService(this, SASL_SERVER_FACTORY_TYPE, "9798-M-RSA-SHA1-ENC", "org.wildfly.security.sasl.entity.EntitySaslServerFactory", emptyList, emptyMap, false, true)); + putService(new ProviderService(this, SASL_SERVER_FACTORY_TYPE, "9798-U-DSA-SHA1", "org.wildfly.security.sasl.entity.EntitySaslServerFactory", emptyList, emptyMap, false, true)); + putService(new ProviderService(this, SASL_SERVER_FACTORY_TYPE, "9798-M-DSA-SHA1", "org.wildfly.security.sasl.entity.EntitySaslServerFactory", emptyList, emptyMap, false, true)); + putService(new ProviderService(this, SASL_SERVER_FACTORY_TYPE, "9798-U-ECDSA-SHA1", "org.wildfly.security.sasl.entity.EntitySaslServerFactory", emptyList, emptyMap, false, true)); + putService(new ProviderService(this, SASL_SERVER_FACTORY_TYPE, "9798-M-ECDSA-SHA1", "org.wildfly.security.sasl.entity.EntitySaslServerFactory", emptyList, emptyMap, false, true)); + putService(new ProviderService(this, SASL_SERVER_FACTORY_TYPE, "EXTERNAL", "org.wildfly.security.sasl.external.ExternalSaslServerFactory", emptyList, emptyMap, false, true)); + putService(new ProviderService(this, SASL_SERVER_FACTORY_TYPE, "GS2-KRB5-PLUS", "org.wildfly.security.sasl.gs2.Gs2SaslServerFactory", emptyList, emptyMap, false, true)); + putService(new ProviderService(this, SASL_SERVER_FACTORY_TYPE, "GS2-KRB5", "org.wildfly.security.sasl.gs2.Gs2SaslServerFactory", emptyList, emptyMap, false, true)); + + putService(new ProviderService(this, SASL_SERVER_FACTORY_TYPE, "GSSAPI", "org.wildfly.security.sasl.gssapi.GssapiServerFactory", emptyList, emptyMap, false, true)); + putService(new ProviderService(this, SASL_SERVER_FACTORY_TYPE, "JBOSS-LOCAL-USER", "org.wildfly.security.sasl.localuser.LocalUserServerFactory", emptyList, emptyMap, false, true)); + putService(new ProviderService(this, SASL_SERVER_FACTORY_TYPE, "OAUTHBEARER", "org.wildfly.security.sasl.oauth2.OAuth2SaslServerFactory", emptyList, emptyMap, false, true)); + putService(new ProviderService(this, SASL_SERVER_FACTORY_TYPE, "OTP", "org.wildfly.security.sasl.otp.OTPSaslServerFactory", emptyList, emptyMap, true, true)); + putService(new ProviderService(this, SASL_SERVER_FACTORY_TYPE, "PLAIN", "org.wildfly.security.sasl.plain.PlainSaslServerFactory", emptyList, emptyMap, false, true)); + + putService(new ProviderService(this, SASL_SERVER_FACTORY_TYPE, "SCRAM-SHA-512-PLUS", "org.wildfly.security.sasl.scram.ScramSaslServerFactory", emptyList, emptyMap, true, true)); + putService(new ProviderService(this, SASL_SERVER_FACTORY_TYPE, "SCRAM-SHA-384-PLUS", "org.wildfly.security.sasl.scram.ScramSaslServerFactory", emptyList, emptyMap, true, true)); + putService(new ProviderService(this, SASL_SERVER_FACTORY_TYPE, "SCRAM-SHA-256-PLUS", "org.wildfly.security.sasl.scram.ScramSaslServerFactory", emptyList, emptyMap, true, true)); + putService(new ProviderService(this, SASL_SERVER_FACTORY_TYPE, "SCRAM-SHA-1-PLUS", "org.wildfly.security.sasl.scram.ScramSaslServerFactory", emptyList, emptyMap, true, true)); + putService(new ProviderService(this, SASL_SERVER_FACTORY_TYPE, "SCRAM-SHA-512", "org.wildfly.security.sasl.scram.ScramSaslServerFactory", emptyList, emptyMap, true, true)); + putService(new ProviderService(this, SASL_SERVER_FACTORY_TYPE, "SCRAM-SHA-384", "org.wildfly.security.sasl.scram.ScramSaslServerFactory", emptyList, emptyMap, true, true)); + putService(new ProviderService(this, SASL_SERVER_FACTORY_TYPE, "SCRAM-SHA-256", "org.wildfly.security.sasl.scram.ScramSaslServerFactory", emptyList, emptyMap, true, true)); + putService(new ProviderService(this, SASL_SERVER_FACTORY_TYPE, "SCRAM-SHA-1", "org.wildfly.security.sasl.scram.ScramSaslServerFactory", emptyList, emptyMap, true, true)); + + putService(new ProviderService(this, SASL_CLIENT_FACTORY_TYPE, "ANONYMOUS", "org.wildfly.security.sasl.anonymous.AnonymousClientFactory", emptyList, emptyMap, false, true)); + putService(new ProviderService(this, SASL_CLIENT_FACTORY_TYPE, "DIGEST-SHA-512", "org.wildfly.security.sasl.digest.DigestClientFactory", emptyList, emptyMap, true, true)); + putService(new ProviderService(this, SASL_CLIENT_FACTORY_TYPE, "DIGEST-SHA-512-256", "org.wildfly.security.sasl.digest.DigestClientFactory", emptyList, emptyMap, true, true)); + putService(new ProviderService(this, SASL_CLIENT_FACTORY_TYPE, "DIGEST-SHA-256", "org.wildfly.security.sasl.digest.DigestClientFactory", emptyList, emptyMap, true, true)); + putService(new ProviderService(this, SASL_CLIENT_FACTORY_TYPE, "DIGEST-SHA-384", "org.wildfly.security.sasl.digest.DigestClientFactory", emptyList, emptyMap, true, true)); + putService(new ProviderService(this, SASL_CLIENT_FACTORY_TYPE, "DIGEST-SHA", "org.wildfly.security.sasl.digest.DigestClientFactory", emptyList, emptyMap, true, true)); + putService(new ProviderService(this, SASL_CLIENT_FACTORY_TYPE, "DIGEST-MD5", "org.wildfly.security.sasl.digest.DigestClientFactory", emptyList, emptyMap, true, true)); + putService(new ProviderService(this, SASL_CLIENT_FACTORY_TYPE, "9798-U-RSA-SHA1-ENC", "org.wildfly.security.sasl.entity.EntitySaslClientFactory", emptyList, emptyMap, false, true)); + putService(new ProviderService(this, SASL_CLIENT_FACTORY_TYPE, "9798-M-RSA-SHA1-ENC", "org.wildfly.security.sasl.entity.EntitySaslClientFactory", emptyList, emptyMap, false, true)); + putService(new ProviderService(this, SASL_CLIENT_FACTORY_TYPE, "9798-U-DSA-SHA1", "org.wildfly.security.sasl.entity.EntitySaslClientFactory", emptyList, emptyMap, false, true)); + putService(new ProviderService(this, SASL_CLIENT_FACTORY_TYPE, "9798-M-DSA-SHA1", "org.wildfly.security.sasl.entity.EntitySaslClientFactory", emptyList, emptyMap, false, true)); + putService(new ProviderService(this, SASL_CLIENT_FACTORY_TYPE, "9798-U-ECDSA-SHA1", "org.wildfly.security.sasl.entity.EntitySaslClientFactory", emptyList, emptyMap, false, true)); + putService(new ProviderService(this, SASL_CLIENT_FACTORY_TYPE, "9798-M-ECDSA-SHA1", "org.wildfly.security.sasl.entity.EntitySaslClientFactory", emptyList, emptyMap, false, true)); + putService(new ProviderService(this, SASL_CLIENT_FACTORY_TYPE, "EXTERNAL", "org.wildfly.security.sasl.external.ExternalSaslClientFactory", emptyList, emptyMap, false, true)); + putService(new ProviderService(this, SASL_CLIENT_FACTORY_TYPE, "GS2-KRB5-PLUS", "org.wildfly.security.sasl.gs2.Gs2SaslClientFactory", emptyList, emptyMap, false, true)); + putService(new ProviderService(this, SASL_CLIENT_FACTORY_TYPE, "GS2-KRB5", "org.wildfly.security.sasl.gs2.Gs2SaslClientFactory", emptyList, emptyMap, false, true)); + + putService(new ProviderService(this, SASL_CLIENT_FACTORY_TYPE, "GSSAPI", "org.wildfly.security.sasl.gssapi.GssapiClientFactory", emptyList, emptyMap, false, true)); + putService(new ProviderService(this, SASL_CLIENT_FACTORY_TYPE, "JBOSS-LOCAL-USER", "org.wildfly.security.sasl.localuser.LocalUserClientFactory", emptyList, emptyMap, false, true)); + putService(new ProviderService(this, SASL_CLIENT_FACTORY_TYPE, "OAUTHBEARER", "org.wildfly.security.sasl.oauth2.OAuth2SaslClientFactory", emptyList, emptyMap, false, true)); + putService(new ProviderService(this, SASL_CLIENT_FACTORY_TYPE, "OTP", "org.wildfly.security.sasl.otp.OTPSaslClientFactory", emptyList, emptyMap, true, true)); + putService(new ProviderService(this, SASL_CLIENT_FACTORY_TYPE, "PLAIN", "org.wildfly.security.sasl.plain.PlainSaslClientFactory", emptyList, emptyMap, false, true)); + putService(new ProviderService(this, SASL_CLIENT_FACTORY_TYPE, "SCRAM-SHA-512-PLUS", "org.wildfly.security.sasl.scram.ScramSaslClientFactory", emptyList, emptyMap, true, true)); + putService(new ProviderService(this, SASL_CLIENT_FACTORY_TYPE, "SCRAM-SHA-384-PLUS", "org.wildfly.security.sasl.scram.ScramSaslClientFactory", emptyList, emptyMap, true, true)); + putService(new ProviderService(this, SASL_CLIENT_FACTORY_TYPE, "SCRAM-SHA-256-PLUS", "org.wildfly.security.sasl.scram.ScramSaslClientFactory", emptyList, emptyMap, true, true)); + putService(new ProviderService(this, SASL_CLIENT_FACTORY_TYPE, "SCRAM-SHA-1-PLUS", "org.wildfly.security.sasl.scram.ScramSaslClientFactory", emptyList, emptyMap, true, true)); + putService(new ProviderService(this, SASL_CLIENT_FACTORY_TYPE, "SCRAM-SHA-512", "org.wildfly.security.sasl.scram.ScramSaslClientFactory", emptyList, emptyMap, true, true)); + putService(new ProviderService(this, SASL_CLIENT_FACTORY_TYPE, "SCRAM-SHA-384", "org.wildfly.security.sasl.scram.ScramSaslClientFactory", emptyList, emptyMap, true, true)); + putService(new ProviderService(this, SASL_CLIENT_FACTORY_TYPE, "SCRAM-SHA-256", "org.wildfly.security.sasl.scram.ScramSaslClientFactory", emptyList, emptyMap, true, true)); + putService(new ProviderService(this, SASL_CLIENT_FACTORY_TYPE, "SCRAM-SHA-1", "org.wildfly.security.sasl.scram.ScramSaslClientFactory", emptyList, emptyMap, true, true)); + } + + private void putCredentialStoreProviderImplementations() { + final List emptyList = Collections.emptyList(); + final Map emptyMap = Collections.emptyMap(); + putService(new Service(this, "CredentialStore", "KeyStoreCredentialStore", "org.wildfly.security.credential.store.impl.KeyStoreCredentialStore", emptyList, emptyMap)); + putService(new Service(this, "CredentialStore", "VaultCredentialStore", "org.wildfly.security.credential.store.impl.VaultCredentialStore", emptyList, emptyMap)); + putService(new Service(this, "CredentialStore", "MapCredentialStore", "org.wildfly.security.credential.store.impl.MapCredentialStore", emptyList, emptyMap)); + putService(new Service(this, "CredentialStore", "PropertiesCredentialStore", "org.wildfly.security.credential.store.impl.PropertiesCredentialStore", emptyList, emptyMap)); + } + + class ProviderService extends Service { + + private final boolean withProvider; + private final boolean reUsable; + private volatile Reference> implementationClassRef; + private volatile Reference instance; + + ProviderService(Provider provider, String type, String algorithm, String className, List aliases, Map attributes) { + this(provider, type, algorithm, className, aliases, attributes, true, false); + } + + ProviderService(Provider provider, String type, String algorithm, String className, List aliases, Map attributes, boolean withProvider, boolean reUsable) { + super(provider, type, algorithm, className, aliases, attributes); + this.withProvider = withProvider; + this.reUsable = reUsable; + } + + @Override + public Object newInstance(Object constructorParameter) throws NoSuchAlgorithmException { + if (reUsable) { + Reference instance = this.instance; + Object response; + if (instance == null || (response = instance.get()) == null) { + synchronized(this) { + instance = this.instance; + if (instance == null || (response = instance.get()) == null) { + response = withProvider ? newInstance() : super.newInstance(constructorParameter); + this.instance = new SoftReference(response); + } + } + } + + return response; + } + + return withProvider ? newInstance() : super.newInstance(constructorParameter); + } + + private Object newInstance() throws NoSuchAlgorithmException { + Class implementationClass = getImplementationClass(); + + try { + Constructor constructor = implementationClass.getConstructor(Provider.class); + return constructor.newInstance(WildFlyElytronProvider.this); + } catch (Exception e) { + throw log.noSuchAlgorithmCreateService(getType(), getAlgorithm(), e); + } + } + + private Class getImplementationClass() throws NoSuchAlgorithmException { + Reference> implementationClassRef = this.implementationClassRef; + Class implementationClass = implementationClassRef != null ? implementationClassRef.get() : null; + if (implementationClass == null) { + ClassLoader classLoader = WildFlyElytronProvider.class.getClassLoader(); + try { + implementationClass = Class.forName(getClassName(), false, classLoader); + } catch (ClassNotFoundException e) { + throw log.noSuchAlgorithmCreateService(getType(), getAlgorithm(), e); + } + this.implementationClassRef = new WeakReference<>(implementationClass); + } + + return implementationClass; + } + + } + +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/AuthenticationException.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/AuthenticationException.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/AuthenticationException.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,70 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2014 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.auth; + +import java.io.IOException; + +/** + * An exception indicating that an initial authentication (login) operation has failed. + * + * @author David M. Lloyd + */ +public class AuthenticationException extends IOException { + + private static final long serialVersionUID = -1038330178933137221L; + + /** + * Constructs a new {@code org.wildfly.security.auth.AuthenticationException} instance. The message is left blank + * ({@code null}), and no cause is specified. + */ + public AuthenticationException() { + } + + /** + * Constructs a new {@code org.wildfly.security.auth.AuthenticationException} instance with an initial message. No + * cause is specified. + * + * @param msg the message + */ + public AuthenticationException(final String msg) { + super(msg); + } + + /** + * Constructs a new {@code org.wildfly.security.auth.AuthenticationException} instance with an initial cause. If a + * non-{@code null} cause is specified, its message is used to initialize the message of this {@code + * org.wildfly.security.auth.AuthenticationException}; otherwise the message is left blank ({@code null}). + * + * @param cause the cause + */ + public AuthenticationException(final Throwable cause) { + super(cause); + } + + /** + * Constructs a new {@code org.wildfly.security.auth.AuthenticationException} instance with an initial message and + * cause. + * + * @param msg the message + * @param cause the cause + */ + public AuthenticationException(final String msg, final Throwable cause) { + super(msg, cause); + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/ReauthenticationException.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/ReauthenticationException.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/ReauthenticationException.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,68 @@ +/* + * 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.auth; + +/** + * A run-time exception indicating that a reauthentication was required for an operation, but the reauthentication + * failed, preventing the operation from proceeding. Reauthentication can happen when (for example) a persistent + * connection is broken and reestablished, or an authentication session was forcibly terminated, or because a backing + * system uses an authentication-per-request strategy, or other reasons. + * + * @author David M. Lloyd + */ +public class ReauthenticationException extends SecurityException { + private static final long serialVersionUID = 6765807441459168511L; + + /** + * Constructs a new {@code ReauthenticationException} instance. The message is left blank ({@code null}), and no + * cause is specified. + */ + public ReauthenticationException() { + } + + /** + * Constructs a new {@code ReauthenticationException} instance with an initial message. No cause is specified. + * + * @param msg the message + */ + public ReauthenticationException(final String msg) { + super(msg); + } + + /** + * Constructs a new {@code ReauthenticationException} instance with an initial cause. If a non-{@code null} cause + * is specified, its message is used to initialize the message of this {@code ReauthenticationException}; otherwise + * the message is left blank ({@code null}). + * + * @param cause the cause + */ + public ReauthenticationException(final Throwable cause) { + super(cause); + } + + /** + * Constructs a new {@code ReauthenticationException} instance with an initial message and cause. + * + * @param msg the message + * @param cause the cause + */ + public ReauthenticationException(final String msg, final Throwable cause) { + super(msg, cause); + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/SupportLevel.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/SupportLevel.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/SupportLevel.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,148 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2016 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.auth; + +import java.util.EnumSet; + +import org.wildfly.common.Assert; + +/** + * The different support levels. + * + * @author David M. Lloyd + */ +public enum SupportLevel { + + /** + * The given credential type is definitely not supported. + */ + UNSUPPORTED, + /** + * The given credential type may be supported. + */ + POSSIBLY_SUPPORTED, + /** + * The given credential type is definitely supported. + */ + SUPPORTED, + ; + + /** + * Determine if this object represents definite support. + * + * @return {@code true} if this object represents definite support, {@code false} otherwise + */ + public boolean isDefinitelySupported() { + return this == SUPPORTED; + } + + /** + * Determine if this object represents possible or definite support. + * + * @return {@code true} if this object represents possible or definite support, {@code false} otherwise + */ + public boolean mayBeSupported() { + return this != UNSUPPORTED; + } + + /** + * Determine if this object represents definite lack of support. + * + * @return {@code true} if this object represents definite lack of support, {@code false} otherwise + */ + public boolean isNotSupported() { + return this == UNSUPPORTED; + } + + private static final int fullSize = values().length; + + /** + * Determine whether the given set is fully populated (or "full"), meaning it contains all possible values. + * + * @param set the set + * + * @return {@code true} if the set is full, {@code false} otherwise + */ + public static boolean isFull(final EnumSet set) { + return set != null && set.size() == fullSize; + } + + /** + * Determine whether this instance is equal to one of the given instances. + * + * @param v1 the first instance + * + * @return {@code true} if one of the instances matches this one, {@code false} otherwise + */ + public boolean in(final SupportLevel v1) { + return this == v1; + } + + /** + * Determine whether this instance is equal to one of the given instances. + * + * @param v1 the first instance + * @param v2 the second instance + * + * @return {@code true} if one of the instances matches this one, {@code false} otherwise + */ + public boolean in(final SupportLevel v1, final SupportLevel v2) { + return this == v1 || this == v2; + } + + /** + * Determine whether this instance is equal to one of the given instances. + * + * @param v1 the first instance + * @param v2 the second instance + * @param v3 the third instance + * + * @return {@code true} if one of the instances matches this one, {@code false} otherwise + */ + public boolean in(final SupportLevel v1, final SupportLevel v2, final SupportLevel v3) { + return this == v1 || this == v2 || this == v3; + } + + /** + * Determine whether this instance is equal to one of the given instances. + * + * @param values the possible values + * + * @return {@code true} if one of the instances matches this one, {@code false} otherwise + */ + public boolean in(final SupportLevel... values) { + if (values != null) for (SupportLevel value : values) { + if (this == value) return true; + } + return false; + } + + /** + * Get the maximum support level between two candidates. + * + * @param o1 the first support level (must not be {@code null}) + * @param o2 the second support level (must not be {@code null}) + * @return the maximum support level (not {@code null}) + */ + public static SupportLevel max(SupportLevel o1, SupportLevel o2) { + Assert.checkNotNullParam("o1", o1); + Assert.checkNotNullParam("o2", o2); + return o1.compareTo(o2) < 0 ? o2 : o1; + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/callback/AbstractCredentialCallback.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/callback/AbstractCredentialCallback.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/callback/AbstractCredentialCallback.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,236 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2017 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.auth.callback; + +import java.security.spec.AlgorithmParameterSpec; +import java.util.function.Function; + +import org.wildfly.common.Assert; +import org.wildfly.security.auth.server._private.ElytronMessages; +import org.wildfly.security.credential.AlgorithmCredential; +import org.wildfly.security.credential.Credential; + +/** + * Abstract base class for credential callbacks. + * + * @author David M. Lloyd + */ +public abstract class AbstractCredentialCallback implements ExtendedCallback { + + private final Class credentialType; + private final String algorithm; + private final AlgorithmParameterSpec parameterSpec; + private Credential credential; + + AbstractCredentialCallback(final Class credentialType, final String algorithm, final AlgorithmParameterSpec parameterSpec) { + Assert.checkNotNullParam("credentialType", credentialType); + if (parameterSpec != null) Assert.checkNotNullParam("algorithm", algorithm); + this.algorithm = algorithm; + this.credentialType = credentialType; + this.parameterSpec = parameterSpec; + } + + /** + * Get the acquired credential. + * + * @return the acquired credential, or {@code null} if it wasn't set yet. + */ + public Credential getCredential() { + return credential; + } + + /** + * Get the acquired credential, if it is set and of the given type, and if so, return the credential cast to the type. + * + * @param credentialType the credential type class (must not be {@code null}) + * @param the credential type + * @return the credential, or {@code null} if the criteria wasn't met + */ + public C getCredential(Class credentialType) { + return applyToCredential(credentialType, Function.identity()); + } + + /** + * Get the acquired credential, if it is set and of the given type and algorithm, and if so, return the credential cast to the type. + * + * @param credentialType the credential type class (must not be {@code null}) + * @param algorithmName the algorithm name + * @param the credential type + * @return the credential, or {@code null} if the criteria are not met + */ + public C getCredential(Class credentialType, String algorithmName) { + return applyToCredential(credentialType, algorithmName, Function.identity()); + } + + /** + * Get the acquired credential, if it is set and of the given type, algorithm, and parameters, and if so, return the credential cast to the type. + * + * @param credentialType the credential type class (must not be {@code null}) + * @param algorithmName the algorithm name + * @param parameterSpec the parameter specification to match, or {@code null} if any parameters are allowed or parameters are not used by + * the credential type + * @param the credential type + * @return the credential, or {@code null} if the criteria are not met + */ + public C getCredential(Class credentialType, String algorithmName, AlgorithmParameterSpec parameterSpec) { + return applyToCredential(credentialType, algorithmName, parameterSpec, Function.identity()); + } + + /** + * Apply the given function to the acquired credential, if it is set and of the given type. By calling this method, + * it is possible to apply transformations to the stored credential without failing if the credential was not set. + * + * @param credentialType the credential type class (must not be {@code null}) + * @param function the function to apply (must not be {@code null}) + * @param the credential type + * @param the return type + * @return the result of the function, or {@code null} if the criteria are not met + */ + public R applyToCredential(Class credentialType, Function function) { + final Credential credential = this.credential; + return credential == null ? null : credential.castAndApply(credentialType, function); + } + + /** + * Apply the given function to the acquired credential, if it is set and of the given type and algorithm. By calling this method, + * it is possible to apply transformations to the stored credential without failing if the credential was not set. + * + * @param credentialType the credential type class (must not be {@code null}) + * @param algorithmName the algorithm name + * @param function the function to apply (must not be {@code null}) + * @param the credential type + * @param the return type + * @return the result of the function, or {@code null} if the criteria are not met + */ + public R applyToCredential(Class credentialType, String algorithmName, Function function) { + final Credential credential = this.credential; + return credential == null ? null : credential.castAndApply(credentialType, algorithmName, function); + } + + /** + * Apply the given function to the acquired credential, if it is set and of the given type and algorithm. By calling this method, + * it is possible to apply transformations to the stored credential without failing if the credential was not set. + * + * @param credentialType the credential type class (must not be {@code null}) + * @param algorithmName the algorithm name + * @param parameterSpec the parameter specification to match, or {@code null} if any parameters are allowed or parameters are not used by + * the credential type + * @param function the function to apply (must not be {@code null}) + * @param the credential type + * @param the return type + * @return the result of the function, or {@code null} if the criteria are not met + */ + public R applyToCredential(Class credentialType, String algorithmName, AlgorithmParameterSpec parameterSpec, Function function) { + final Credential credential = this.credential; + return credential == null ? null : credential.castAndApply(credentialType, algorithmName, parameterSpec, function); + } + + /** + * Set the credential. The credential must be of the supported type and algorithm. + * + * @param credential the credential, or {@code null} to indicate that no credential is available + * @throws IllegalArgumentException if the given credential is not supported + */ + public void setCredential(final Credential credential) { + if (credential != null && ! isCredentialSupported(credential)) { + throw ElytronMessages.log.credentialNotSupported(); + } + this.credential = credential; + } + + /** + * Determine whether the given credential type is supported. Will be {@code false} if the credential type requires + * an algorithm name; in this case, use {@link #isCredentialTypeSupported(Class, String)} instead. + * + * @param credentialType the credential type (must not be {@code null}) + * @return {@code true} if the credential type is supported, {@code false} otherwise + */ + public boolean isCredentialTypeSupported(final Class credentialType) { + return isCredentialTypeSupported(credentialType, null); + } + + /** + * Determine whether the given credential type is supported for the given algorithm name. + * + * @param credentialType the credential type (must not be {@code null}) + * @param algorithmName the algorithm name, or {@code null} to indicate that no algorithm name will be available + * @return {@code true} if the credential type is supported, {@code false} otherwise + */ + public boolean isCredentialTypeSupported(final Class credentialType, final String algorithmName) { + Assert.checkNotNullParam("credentialType", credentialType); + return this.credentialType.isAssignableFrom(credentialType) && (algorithm == null || AlgorithmCredential.class.isAssignableFrom(credentialType) && algorithm.equals(algorithmName)); + } + + /** + * Determine whether the given credential type is supported for the given algorithm name. + * + * @param credentialType the credential type (must not be {@code null}) + * @param algorithmName the algorithm name, or {@code null} to indicate that no algorithm name will be available + * @param parameterSpec the parameters, or {@code null} if no parameters are present in the credential + * @return {@code true} if the credential type is supported, {@code false} otherwise + */ + public boolean isCredentialTypeSupported(final Class credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec) { + Assert.checkNotNullParam("credentialType", credentialType); + return isCredentialTypeSupported(credentialType, algorithmName) && (this.parameterSpec == null || this.parameterSpec.equals(parameterSpec)); + } + + /** + * Determine whether the given credential can be set on this callback. + * + * @param credential the credential (must not be {@code null}) + * @return {@code true} if the credential matches the type and optional algorithm of this callback, {@code false} otherwise + */ + public boolean isCredentialSupported(final Credential credential) { + Assert.checkNotNullParam("credential", credential); + return credential.castAs(credentialType, algorithm, parameterSpec) != null; + } + + /** + * Get the supported credential type. + * + * @return the supported credential type (not {@code null}) + */ + public Class getCredentialType() { + return credentialType; + } + + /** + * Get the algorithm name, if any. + * + * @return the algorithm name, or {@code null} if any algorithm is suitable or the credential + * type does not use algorithm names + */ + public String getAlgorithm() { + return algorithm; + } + + /** + * Get the parameter specification, if any. + * + * @return the parameter specification, or {@code null} if any parameters are suitable or the credential type + * does not use parameters + */ + public AlgorithmParameterSpec getParameterSpec() { + return parameterSpec; + } + + public boolean needsInformation() { + return true; + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/callback/AnonymousAuthorizationCallback.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/callback/AnonymousAuthorizationCallback.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/callback/AnonymousAuthorizationCallback.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,85 @@ +/* + * 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.auth.callback; + +import java.io.Serializable; + +/** + * A callback to authorize anonymous authentication. + * + * @author David M. Lloyd + */ +public final class AnonymousAuthorizationCallback implements ExtendedCallback, Serializable { + + private static final long serialVersionUID = -6532813145396004679L; + + /** + * @serial The authorization information from the client. + */ + private final String authorizationInfo; + /** + * @serial The authorization result flag. + */ + private boolean authorized; + + /** + * Construct a new instance. + * + * @param authorizationInfo the authorization information string from the client + */ + public AnonymousAuthorizationCallback(final String authorizationInfo) { + this.authorizationInfo = authorizationInfo; + } + + /** + * Get the authorization name string from the client. This name is only informative and must not be used + * for authentication purposes. + * + * @return the authorization name string from the client + */ + public String getAuthorizationInfo() { + return authorizationInfo; + } + + /** + * Determine whether anonymous access was allowed by the callback handler. + * + * @return {@code true} if anonymous authentication was allowed, {@code false} otherwise + */ + public boolean isAuthorized() { + return authorized; + } + + /** + * Set whether anonymous access is allowed. + * + * @param authorized {@code true} if anonymous access is allowed, {@code false} otherwise + */ + public void setAuthorized(final boolean authorized) { + this.authorized = authorized; + } + + public boolean isOptional() { + return false; + } + + public boolean needsInformation() { + return true; + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/callback/AuthenticationCompleteCallback.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/callback/AuthenticationCompleteCallback.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/callback/AuthenticationCompleteCallback.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,79 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2014 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.auth.callback; + +import java.io.Serializable; + +/** + * An optional callback indicating the success or failure of the authentication operation. When this callback is + * received, the callback handler may free any resources that were required to perform the authentication. This + * callback should always be sent to the callback handler last. + * + * @author David M. Lloyd + */ +public final class AuthenticationCompleteCallback implements ExtendedCallback, Serializable { + + private static final long serialVersionUID = -8336218311376736914L; + + public static final AuthenticationCompleteCallback SUCCEEDED = new AuthenticationCompleteCallback(true); + + public static final AuthenticationCompleteCallback FAILED = new AuthenticationCompleteCallback(false); + + /** + * @serial The flag indicating whether the authentication was successful. + */ + private final boolean success; + + /** + * Construct a new instance. + * + * @param success {@code true} if the authentication was successful, {@code false} otherwise + */ + private AuthenticationCompleteCallback(final boolean success) { + this.success = success; + } + + /** + * Determine whether authentication succeeded. Always returns the opposite of {@link #failed()}. + * + * @return {@code true} if authentication succeeded, {@code false} otherwise + */ + public boolean succeeded() { + return success; + } + + /** + * Determine whether authentication failed. Always returns the opposite of {@link #succeeded()}. + * + * @return {@code true} if the authentication failed, {@code false} otherwise + */ + public boolean failed() { + return ! success; + } + + /** + * Replace the deserialized instance with the sppropriate singleton instance. + * + * @return The singleton instance. + */ + Object readResolve() { + return success ? SUCCEEDED : FAILED; + } + +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/callback/AuthenticationConfigurationCallback.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/callback/AuthenticationConfigurationCallback.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/callback/AuthenticationConfigurationCallback.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,41 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2023 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.auth.callback; + + +/** + * A {@link javax.security.auth.callback.Callback} to inform a server authentication context of configured mechanism properties. + * + * As an informational {@code Callback} it is optional for the {@code CallbackHandler} to handle this. + * + */ +public class AuthenticationConfigurationCallback implements ExtendedCallback{ + + /** + * Property of the SASL EXTERNAL mechanism that indicates whether a certificate should be verified against the security realm. + */ + private boolean saslSkipCertificateVerification; + + public boolean getSaslSkipCertificateVerification() { + return this.saslSkipCertificateVerification; + } + + public void setSaslSkipCertificateVerification(boolean skipCertificateVerification) { + this.saslSkipCertificateVerification = skipCertificateVerification; + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/callback/AvailableRealmsCallback.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/callback/AvailableRealmsCallback.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/callback/AvailableRealmsCallback.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,69 @@ +/* + * 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.auth.callback; + +import org.wildfly.common.Assert; +import org.wildfly.security.auth.server.SecurityRealm; + +/** + * A callback used to query a server participant for the names of realms that it is prepared to offer. Note that the + * authentication mechanism realm concept is not directly related to the Elytron {@linkplain SecurityRealm security realm concept}. + * + * @author David M. Lloyd + */ +public final class AvailableRealmsCallback implements ExtendedCallback { + private String[] realmNames; + + /** + * Construct a new instance. + */ + public AvailableRealmsCallback() { + } + + /** + * Get the array of realm names that was set. + * + * @return the realm names array + */ + public String[] getRealmNames() { + return realmNames; + } + + /** + * Set the realm names. None of the realm names may be {@code null}. The array is not copied, so care must + * be taken to avoid modifying the realm array after it is set. + * + * @param realmNames the realm names (may not be {@code null}, may not contain {@code null}) + */ + public void setRealmNames(final String... realmNames) { + Assert.checkNotNullParam("realmNames", realmNames); + for (int i = 0, realmNamesLength = realmNames.length; i < realmNamesLength; i++) { + Assert.checkNotNullArrayParam("realmNames", i, realmNames[i]); + } + this.realmNames = realmNames; + } + + public boolean isOptional() { + return true; + } + + public boolean needsInformation() { + return true; + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/callback/CachedIdentityAuthorizeCallback.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/callback/CachedIdentityAuthorizeCallback.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/callback/CachedIdentityAuthorizeCallback.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,232 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2016 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.auth.callback; + +import org.wildfly.common.Assert; +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.cache.CachedIdentity; +import org.wildfly.security.cache.IdentityCache; + +import java.security.Principal; +import java.util.function.Function; + +import static org.wildfly.common.Assert.checkNotNullParam; + +/** + *

A callback that is capable of perform authorization based on the identities managed by an {@link IdentityCache}. + * + *

This callback can be used in two ways: + * + *

    + *
  • As an alternative to {@link javax.security.sasl.AuthorizeCallback}. As a result, the identity (if successfully authorized) will be cached
  • + *
  • To perform a lookup in the cache and authorize the cached identity locally
  • + *
+ * + * @author Pedro Igor + */ +public class CachedIdentityAuthorizeCallback implements ExtendedCallback { + + private final Function identityCache; + private final boolean localCache; + private Principal principal; + private boolean authorized; + private SecurityDomain securityDomain; + + /** + * Creates a new instance in order to authorize identities managed by the given identityCache. + * + * @param identityCache the identity cache + */ + public CachedIdentityAuthorizeCallback(IdentityCache identityCache) { + this(identityCache, false); + } + + /** + * Creates a new instance in order to authorize identities managed by the given identityCache. + * + * @param identityCache the identity cache + * @param localCache if true, indicates that authorization should be based on the given {@code identityCache} only. In case the mechanism + * performing the authorization is wrapped by another one that provides a top-level cache (eg.: SSO), only the given + * {@code identityCache} will be considered. + */ + public CachedIdentityAuthorizeCallback(IdentityCache identityCache, boolean localCache) { + this(securityDomain1 -> identityCache, localCache); + } + + /** + *

Creates a new instance in order to authorize identities managed by the given identityCache. + * + *

This constructor can be used to perform caching operations (e.g.: put, get and remove) in the context of a {@link SecurityDomain}. + * + * @param identityCache a function that creates an {@link IdentityCache} given a {@link SecurityDomain} + * @param localCache if true, indicates that authorization should be based on the given {@code identityCache} only. In case the mechanism + * performing the authorization is wrapped by another one that provides a top-level cache (eg.: SSO), only the given + * {@code identityCache} will be considered. + */ + public CachedIdentityAuthorizeCallback(Function identityCache, boolean localCache) { + checkNotNullParam("identityCache", identityCache); + this.identityCache = identityCache; + this.localCache = localCache; + } + + /** + * Creates a new instance to authenticate, authorize and cache the identity associated with the given name. + * + * @param name the name associated with the identity + * @param identityCache the identity cache + */ + public CachedIdentityAuthorizeCallback(String name, IdentityCache identityCache) { + this(new NamePrincipal(name), identityCache); + } + + /** + * Creates a new instance to authenticate, authorize and cache the identity associated with the given principal. + * + * @param principal the principal associated with the identity + * @param identityCache the identity cache + * @param localCache if true, indicates that authorization should be based on the given {@code identityCache} only. In case the mechanism + * performing the authorization is wrapped by another one that provides a top-level cache (eg.: SSO), only the given + * {@code identityCache} will be considered. + */ + public CachedIdentityAuthorizeCallback(Principal principal, IdentityCache identityCache, boolean localCache) { + this(principal, securityDomain -> identityCache, localCache); + } + + /** + * Creates a new instance to authenticate, authorize and cache the identity associated with the given principal. + * + * @param principal the principal associated with the identity + * @param identityCache the identity cache + */ + public CachedIdentityAuthorizeCallback(Principal principal, IdentityCache identityCache) { + this(principal, securityDomain -> identityCache, false); + } + + /** + *

Creates a new instance to authenticate, authorize and cache the identity associated with the given principal. + * + *

This constructor can be used to perform caching operations (e.g.: put, get and remove) in the context of a {@link SecurityDomain}. + * + * @param principal the principal associated with the identity + * @param identityCache a function that creates an {@link IdentityCache} given a {@link SecurityDomain} + * @param localCache if true, indicates that authorization should be based on the given {@code identityCache} only. In case the mechanism + * performing the authorization is wrapped by another one that provides a top-level cache (eg.: SSO), only the given + * {@code identityCache} will be considered. + */ + public CachedIdentityAuthorizeCallback(Principal principal, Function identityCache, boolean localCache) { + checkNotNullParam("principal", principal); + checkNotNullParam("identityCache", identityCache); + this.principal = principal; + this.identityCache = identityCache; + this.localCache = localCache; + } + + /** + * Indicates if a cached identity was successfully authorized. + * + * @return true if the cached identity was successfully authorized. Otherwise, false + */ + public boolean isAuthorized() { + return authorized; + } + + /** + * Authorizes and caches the given securityIdentity. + * + * @param securityIdentity the identity to authorize and cache. If null, the corresponding identity will be removed from the cache + */ + public void setAuthorized(SecurityIdentity securityIdentity) { + authorized = securityIdentity != null; + if (authorized) { + createDomainCache().put(securityIdentity); + } else { + createDomainCache().remove(); + } + } + + /** + * Returns the {@link Principal} representing the cached identity. + * + * @return the principal (not {@code null}) + */ + public Principal getPrincipal() { + CachedIdentity cachedIdentity = createDomainCache().get(); + if (cachedIdentity != null) { + return new NamePrincipal(cachedIdentity.getName()); + } + return null; + } + + /** + * Returns the authorization {@link Principal}. + * + * @return the principal (not {@code null}) + */ + public Principal getAuthorizationPrincipal() { + return this.principal; + } + + /** + * Returns a cached {@link SecurityIdentity}, if present in the cache. + * + * @return the cached identity or null if there is no entry in the cache + */ + public SecurityIdentity getIdentity() { + CachedIdentity cachedIdentity = createDomainCache().get(); + if (cachedIdentity != null) { + return cachedIdentity.getSecurityIdentity(); + } + return null; + } + + /** + * Indicates if authorization decisions should be performed based on the given {@link IdentityCache} only. + * + * @return true indicating that authorization decisions should be performed based on the given {@link IdentityCache} only. Otherwise, false + */ + public boolean isLocalCache() { + return localCache; + } + + /** + * Set the current {@link SecurityDomain} in order to obtain identities from the cache + * + * @param securityDomain the current security domain + */ + public void setSecurityDomain(SecurityDomain securityDomain) { + Assert.checkNotNullParam("securityDomain", securityDomain); + this.securityDomain = securityDomain; + } + + @Override + public boolean isOptional() { + return false; + } + + @Override + public boolean needsInformation() { + return false; + } + + private IdentityCache createDomainCache() { + return this.identityCache.apply(securityDomain); + } +} \ No newline at end of file Index: 3rdParty_sources/elytron/org/wildfly/security/auth/callback/CallbackUtil.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/callback/CallbackUtil.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/callback/CallbackUtil.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,56 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2014 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.auth.callback; + +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.UnsupportedCallbackException; + +/** + * Helper utility methods for callback handlers. + * + * @author David M. Lloyd + */ +public final class CallbackUtil { + + private CallbackUtil() { + } + + /** + * Determine whether a callback is optional. + * + * @param callback the callback + * @return {@code true} if it is optional, {@code false} if it is not optional or optionality could not be determined + */ + public static boolean isOptional(Callback callback) { + return callback instanceof ExtendedCallback && ((ExtendedCallback) callback).isOptional(); + } + + /** + * A utility to handle a callback which is unsupported. Optional callbacks will be ignored, otherwise the + * exception will be thrown. In the case of optional callbacks, this method will return. + * + * @param callback the callback which is not supported + * @throws UnsupportedCallbackException if the callback is not optional + */ + public static void unsupported(Callback callback) throws UnsupportedCallbackException { + if (! isOptional(callback)) { + throw new FastUnsupportedCallbackException(callback); + } + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/callback/ChannelBindingCallback.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/callback/ChannelBindingCallback.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/callback/ChannelBindingCallback.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,91 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2014 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.auth.callback; + +import java.io.Serializable; + +/** + * A callback used to establish the channel binding for a security mechanism which supports it. Both the binding type + * and data must be set, otherwise no channel binding will be established. The channel binding type should be one of + * the types described in IANA's + * channel binding type registry. + * + * @author David M. Lloyd + */ +public final class ChannelBindingCallback implements ExtendedCallback, Serializable { + + private static final long serialVersionUID = 779300207924589036L; + + /** + * @serial The channel binding type. + */ + private String bindingType; + /** + * @serial The channel binding data. + */ + private byte[] bindingData; + + /** + * Construct a new instance. + */ + public ChannelBindingCallback() { + } + + public boolean needsInformation() { + return true; + } + + /** + * Get the selected channel binding type. + * + * @return the selected channel binding type + */ + public String getBindingType() { + return bindingType; + } + + /** + * Get the opaque channel binding data. This data may come from the connection negotiation or from another security + * layer. + * + * @return the opaque channel binding data + */ + public byte[] getBindingData() { + return bindingData; + } + + /** + * Set the selected channel binding type. The type should be one registered with + * IANA. + * + * @param bindingType the selected channel binding type + */ + public void setBindingType(final String bindingType) { + this.bindingType = bindingType; + } + + /** + * Set the channel binding data. This data may come from the connection negotiation or from another security layer. + * + * @param bindingData the channel binding data + */ + public void setBindingData(final byte[] bindingData) { + this.bindingData = bindingData; + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/callback/CredentialCallback.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/callback/CredentialCallback.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/callback/CredentialCallback.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,75 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2014 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.auth.callback; + +import java.security.spec.AlgorithmParameterSpec; + +import org.wildfly.security.credential.Credential; + +/** + * A callback used to acquire credentials. On the client side of an authentication mechanism, the callback handler is + * required to supply a credential for use in outbound authentication. On the server side, the callback handler is + * required to supply a credential for use in inbound authentication, possibly for both verification as well as establishing + * authentication parameters. + *

+ * This callback must be handled if a default credential was not supplied. The callback + * handler is expected to provide a credential to this callback if one is not present. If no credential is available, + * {@code null} is set, and authentication may fail. If an unsupported credential type is set, an exception is thrown. + * + * @author David M. Lloyd + */ +public final class CredentialCallback extends AbstractCredentialCallback { + + /** + * Construct a new instance. + * + * @param credentialType the desired credential type (must not be {@code null}) + * @param algorithm the algorithm name, or {@code null} if any algorithm is suitable or the credential + * type does not use algorithm names + * @param parameterSpec the parameters to match, or {@code null} if any parameters are acceptable or the credential + * type does not support parameters + */ + public CredentialCallback(final Class credentialType, final String algorithm, final AlgorithmParameterSpec parameterSpec) { + super(credentialType, algorithm, parameterSpec); + } + + /** + * Construct a new instance which accepts any parameters. + * + * @param credentialType the desired credential type (must not be {@code null}) + * @param algorithm the algorithm name, or {@code null} if any algorithm is suitable or the credential + * type does not use algorithm names + */ + public CredentialCallback(final Class credentialType, final String algorithm) { + this(credentialType, algorithm, null); + } + + /** + * Construct a new instance which accepts any algorithm name or parameters. + * + * @param credentialType the desired credential type (must not be {@code null}) + */ + public CredentialCallback(final Class credentialType) { + this(credentialType, null, null); + } + + public boolean isOptional() { + return getCredential() != null; + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/callback/CredentialUpdateCallback.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/callback/CredentialUpdateCallback.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/callback/CredentialUpdateCallback.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,67 @@ +/* + * 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.auth.callback; + +import org.wildfly.common.Assert; +import org.wildfly.security.credential.Credential; + +/** + * A callback to inform the callback handler of a credential change. + * + * @author Farah Juma + */ +public final class CredentialUpdateCallback implements ExtendedCallback { + + private final Credential credential; + + /** + * Construct a new instance. + * + * @param credential the new credential + */ + public CredentialUpdateCallback(final Credential credential) { + Assert.checkNotNullParam("credential", credential); + this.credential = credential; + } + + /** + * Get the new credential. + * + * @return the new credential + */ + public Credential getCredential() { + return credential; + } + + /** + * Get the new credential, if it is of the given credential class. + * + * @param credentialClass the credential class + * @param the credential type + * @return the credential, or {@code null} if it is not of the given type + */ + public C getCredential(final Class credentialClass) { + final Credential credential = this.credential; + return credentialClass.isInstance(credential) ? credentialClass.cast(credential) : null; + } + + public boolean isOptional() { + return false; + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/callback/EvidenceDecodePrincipalCallback.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/callback/EvidenceDecodePrincipalCallback.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/callback/EvidenceDecodePrincipalCallback.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,107 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2019 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.auth.callback; + +import java.util.function.Function; + +import javax.security.auth.callback.Callback; + +import org.wildfly.security.evidence.Evidence; + +/** + * A server-side {@link Callback} to pass the decoded evidence principal from the callback handler + * to the authentication mechanism. + * + * @author Farah Juma + * @since 1.10.0 + */ +public class EvidenceDecodePrincipalCallback implements ExtendedCallback { + + private final Evidence evidence; + + /** + * Construct a new instance of this {@link Callback}. + * + * @param evidence the evidence to be decoded + */ + public EvidenceDecodePrincipalCallback(final Evidence evidence) { + this.evidence = evidence; + } + + /** + * Get the evidence being decoded. + * + * @return the evidence being decoded + */ + public Evidence getEvidence() { + return evidence; + } + + /** + * Get the acquired evidence, if it is set and of the given type, and if so, return the evidence cast to the type. + * + * @param evidenceType the evidence type class (must not be {@code null}) + * @param the evidence type + * @return the evidence, or {@code null} if the criteria wasn't met + */ + public C getEvidence(Class evidenceType) { + return applyToEvidence(evidenceType, Function.identity()); + } + + /** + * Get the acquired evidence, if it is set and of the given type and algorithm, and if so, return the evidence cast to the type. + * + * @param evidenceType the evidence type class (must not be {@code null}) + * @param algorithmName the algorithm name + * @param the evidence type + * @return the evidence, or {@code null} if the criteria are not met + */ + public C getEvidence(Class evidenceType, String algorithmName) { + return applyToEvidence(evidenceType, algorithmName, Function.identity()); + } + + /** + * Apply the given function to the acquired evidence, if it is set and of the given type. + * + * @param evidenceType the evidence type class (must not be {@code null}) + * @param function the function to apply (must not be {@code null}) + * @param the evidence type + * @param the return type + * @return the result of the function, or {@code null} if the criteria are not met + */ + public R applyToEvidence(Class evidenceType, Function function) { + final Evidence evidence = this.evidence; + return evidence == null ? null : evidence.castAndApply(evidenceType, function); + } + + /** + * Apply the given function to the acquired evidence, if it is set and of the given type and algorithm. + * + * @param evidenceType the evidence type class (must not be {@code null}) + * @param algorithmName the algorithm name + * @param function the function to apply (must not be {@code null}) + * @param the evidence type + * @param the return type + * @return the result of the function, or {@code null} if the criteria are not met + */ + public R applyToEvidence(Class evidenceType, String algorithmName, Function function) { + final Evidence evidence = this.evidence; + return evidence == null ? null : evidence.castAndApply(evidenceType, algorithmName, function); + } + +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/callback/EvidenceVerifyCallback.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/callback/EvidenceVerifyCallback.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/callback/EvidenceVerifyCallback.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,140 @@ +/* + * 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.auth.callback; + +import java.util.function.Function; + +import org.wildfly.security.evidence.Evidence; + +import javax.security.auth.callback.Callback; + +/** + * A {@link Callback} for use where credential verification is required. + * + * @author Darran Lofthouse + */ +public class EvidenceVerifyCallback implements ExtendedCallback { + + private final Evidence evidence; + private boolean verified; + + /** + * Construct a new instance of this {@link Callback}. + * + * @param evidence the evidence to be verified + */ + public EvidenceVerifyCallback(final Evidence evidence) { + this.evidence = evidence; + } + + /** + * Get the evidence being verified. + * + * @return the evidence being verified + */ + public Evidence getEvidence() { + return evidence; + } + + /** + * Get the acquired evidence, if it is set and of the given type, and if so, return the evidence cast to the type. + * + * @param evidenceType the evidence type class (must not be {@code null}) + * @param the evidence type + * @return the evidence, or {@code null} if the criteria wasn't met + */ + public C getEvidence(Class evidenceType) { + return applyToEvidence(evidenceType, Function.identity()); + } + + /** + * Get the acquired evidence, if it is set and of the given type and algorithm, and if so, return the evidence cast to the type. + * + * @param evidenceType the evidence type class (must not be {@code null}) + * @param algorithmName the algorithm name + * @param the evidence type + * @return the evidence, or {@code null} if the criteria are not met + */ + public C getEvidence(Class evidenceType, String algorithmName) { + return applyToEvidence(evidenceType, algorithmName, Function.identity()); + } + + /** + * Apply the given function to the acquired evidence, if it is set and of the given type. + * + * @param evidenceType the evidence type class (must not be {@code null}) + * @param function the function to apply (must not be {@code null}) + * @param the evidence type + * @param the return type + * @return the result of the function, or {@code null} if the criteria are not met + */ + public R applyToEvidence(Class evidenceType, Function function) { + final Evidence evidence = this.evidence; + return evidence == null ? null : evidence.castAndApply(evidenceType, function); + } + + /** + * Apply the given function to the acquired evidence, if it is set and of the given type and algorithm. + * + * @param evidenceType the evidence type class (must not be {@code null}) + * @param algorithmName the algorithm name + * @param function the function to apply (must not be {@code null}) + * @param the evidence type + * @param the return type + * @return the result of the function, or {@code null} if the criteria are not met + */ + public R applyToEvidence(Class evidenceType, String algorithmName, Function function) { + final Evidence evidence = this.evidence; + return evidence == null ? null : evidence.castAndApply(evidenceType, algorithmName, function); + } + + /** + * Set if the evidence referenced here has been verified. + * + * @param verified the verification state of the evidence + */ + public void setVerified(final boolean verified) { + this.verified = verified; + } + + /** + * Get the verification state for the evidence referenced here. + * + * @return {@code true} if the evidence has been verified, {@code false} otherwise + */ + public boolean isVerified() { + return verified; + } + + /** + * This {@link Callback} is not optional as verification is required. + */ + @Override + public boolean isOptional() { + return false; + } + + /** + * This {@link Callback} needs to know if evidence validation was successful. + */ + @Override + public boolean needsInformation() { + return true; + } + +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/callback/ExclusiveNameCallback.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/callback/ExclusiveNameCallback.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/callback/ExclusiveNameCallback.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,108 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2016 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.auth.callback; + +import javax.security.auth.callback.NameCallback; + +/** + * A variation on {@code NameCallback} which allows exclusive access to the backing identity to be requested. + * + * @author Farah Juma + */ +public class ExclusiveNameCallback extends NameCallback implements ExtendedCallback { + + private static final long serialVersionUID = 3332866436399055886L; + + /** + * @serial A flag indicating whether exclusive access to the backing identity is required. + */ + private final boolean needsExclusiveAccess; + + /** + * @serial A flag indicating whether exclusive access to the backing identity was granted. + */ + private boolean exclusiveAccess; + + /** + * @serial A flag indicating whether the callback is optional. + */ + private final boolean optional; + + /** + * Construct a new instance. + * + * @param prompt the text prompt (must not be {@code null}) + * @param needsExclusiveAccess {@code true} if exclusive access to the backing identity is required + * @param optional {@code true} if the support for the callback is optional + */ + public ExclusiveNameCallback(final String prompt, final boolean needsExclusiveAccess, final boolean optional) { + super(prompt); + this.needsExclusiveAccess = needsExclusiveAccess; + this.optional = optional; + } + + /** + * Construct a new instance. + * + * @param prompt the text prompt (must not be {@code null}) + * @param defaultName the name to be used as the default name displayed with the prompt + * @param needsExclusiveAccess {@code true} if exclusive access to the backing identity is required + * @param optional {@code true} if the support for the callback is optional + */ + public ExclusiveNameCallback(final String prompt, final String defaultName, final boolean needsExclusiveAccess, final boolean optional) { + super(prompt, defaultName); + this.needsExclusiveAccess = needsExclusiveAccess; + this.optional = optional; + } + + /** + * Determine if exclusive access to the backing identity is required. + * + * @return {@code true} if exclusive access to the backing identity is required, {@code false} otherwise + */ + public boolean needsExclusiveAccess() { + return needsExclusiveAccess; + } + + /** + * Determine if exclusive access to the backing identity was granted. + * + * @return {@code true} if exclusive access to the backing identity was granted, {@code false} otherwise + */ + public boolean hasExclusiveAccess() { + return exclusiveAccess; + } + + /** + * Set whether exclusive access to the backing identity was granted. + * + * @param exclusiveAccess {@code true} if exclusive access to the backing identity was granted, {@code false} otherwise + */ + public void setExclusiveAccess(final boolean exclusiveAccess) { + this.exclusiveAccess = exclusiveAccess; + } + + public boolean isOptional() { + return optional; + } + + public boolean needsInformation() { + return true; + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/callback/ExtendedCallback.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/callback/ExtendedCallback.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/callback/ExtendedCallback.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,60 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2013 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.auth.callback; + +import javax.security.auth.callback.Callback; + +/** + * A callback which provides extended information about its usage. + *

+ * For example, the following code can be used to detect an optional callback instead of failing if the callback is + * unrecognized: + *

{@code
+    if (callback instanceof ExtendedCallback && ((ExtendedCallback) callback).isOptional()) {
+        // The callback is harmless
+        return;
+    }
+    // Let the caller know that we failed to support this callback
+    throw new UnsupportedCallbackException(callback);
+ * }
+ * Or, the utility method in {@link CallbackUtil} can be used: + *
{@code
+    CallbackUtils.unsupported(callback);
+ * }
+ */ +public interface ExtendedCallback extends Callback { + + /** + * Determine if this callback is optional. + * + * @return {@code true} if the callback is optional, {@code false} if it is mandatory + */ + default boolean isOptional() { + return true; + } + + /** + * Determine if this callback is requesting information. + * + * @return {@code true} if the callback is requesting information, {@code false} if it is only providing information + */ + default boolean needsInformation() { + return false; + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/callback/ExtendedChoiceCallback.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/callback/ExtendedChoiceCallback.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/callback/ExtendedChoiceCallback.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,58 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2014 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.auth.callback; + +import javax.security.auth.callback.ChoiceCallback; + +/** + * A variation on {@code ChoiceCallback} which supports the extended callback interface. + * + * @author David M. Lloyd + */ +public class ExtendedChoiceCallback extends ChoiceCallback implements ExtendedCallback { + + private static final long serialVersionUID = 2222777746412093737L; + + /** + * @serial A flag indicating whether the callback is optional. + */ + private final boolean optional; + + /** + * Construct a new instance. + * + * @param prompt the text prompt (must not be {@code null}) + * @param choices the choices (must not be {@code null}) + * @param defaultChoice the default choice as an index into the {@code choices} array + * @param multipleSelectionsAllowed {@code true} if multiple selections are allowed + * @param optional {@code true} if the support for the callback is optional + */ + public ExtendedChoiceCallback(final String prompt, final String[] choices, final int defaultChoice, final boolean multipleSelectionsAllowed, final boolean optional) { + super(prompt, choices, defaultChoice, multipleSelectionsAllowed); + this.optional = optional; + } + + public boolean isOptional() { + return optional; + } + + public boolean needsInformation() { + return true; + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/callback/FastUnsupportedCallbackException.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/callback/FastUnsupportedCallbackException.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/callback/FastUnsupportedCallbackException.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,102 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2014 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.auth.callback; + +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.UnsupportedCallbackException; + +/** + * A version of {@code UnsupportedCallbackException} which does not initialize a full stack trace, and thus is much + * more efficient to construct. + * + * @author David M. Lloyd + */ +public class FastUnsupportedCallbackException extends UnsupportedCallbackException { + + private static final long serialVersionUID = -990072831042809709L; + + private static final StackTraceElement[] NO_STACK = new StackTraceElement[0]; + + /** + * Constructs a new {@code FastUnsupportedCallbackException} instance. The message is left blank ({@code null}), + * and no cause is specified. + * + * @param callback the callback which is not supported (should not be {@code null}) + */ + public FastUnsupportedCallbackException(final Callback callback) { + super(callback); + } + + /** + * Constructs a new {@code FastUnsupportedCallbackException} instance with an initial message. No cause is + * specified. + * + * @param callback the callback which is not supported (should not be {@code null}) + * @param msg the message + */ + public FastUnsupportedCallbackException(final Callback callback, final String msg) { + super(callback, msg); + } + + /** + * Does nothing but return this instance. + * + * @param cause ignored + * @return this instance + */ + public Throwable initCause(final Throwable cause) { + return this; + } + + /** + * Returns an empty stack. + * + * @return an empty stack + */ + public StackTraceElement[] getStackTrace() { + return NO_STACK; + } + + /** + * Does nothing but return this instance. + * + * @return this instance + */ + public Throwable fillInStackTrace() { + return this; + } + + /** + * Does nothing. + * + * @param stackTrace ignored + */ + public void setStackTrace(final StackTraceElement[] stackTrace) { + } + + /** + * Get the message for this exception, formatted with the callback. + * + * @return the message for this exception, with the callback (not {@code null}) + */ + public String getMessage() { + final String message = super.getMessage(); + return message != null ? String.format("%s: %s", getCallback(), message) : String.valueOf(getCallback()); + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/callback/IdentityCredentialCallback.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/callback/IdentityCredentialCallback.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/callback/IdentityCredentialCallback.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,61 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2016 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.auth.callback; + +import org.wildfly.security.credential.Credential; + +/** + * A callback to inform a server authentication mechanism of a credential which may be cached on the authentication + * identity (if any). The credential may be public or private. + * + * @author David M. Lloyd + */ +public final class IdentityCredentialCallback implements ExtendedCallback { + private final Credential credential; + private final boolean isPrivate; + + /** + * Construct a new instance. + * + * @param credential the credential (must not be {@code null}) + * @param isPrivate {@code true} if the credential should be private, {@code false} if it can be public + */ + public IdentityCredentialCallback(final Credential credential, final boolean isPrivate) { + this.credential = credential; + this.isPrivate = isPrivate; + } + + /** + * Get the credential. + * + * @return the credential (not {@code null}) + */ + public Credential getCredential() { + return credential; + } + + /** + * Determine whether the credential should be treated as private. + * + * @return {@code true} to treat the credential as private, {@code false} otherwise + */ + public boolean isPrivate() { + return isPrivate; + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/callback/MechanismInformationCallback.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/callback/MechanismInformationCallback.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/callback/MechanismInformationCallback.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,54 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2016 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.auth.callback; + +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; + +import org.wildfly.security.auth.server.MechanismInformation; + +/** + * A {@link Callback} to pass the information about the current mechanism to the {@link CallbackHandler}. + * + * As an informational {@code Callback} it is optional for the {@code CallbackHandler} to handle this. + * + * @author Darran Lofthouse + */ +public class MechanismInformationCallback implements ExtendedCallback { + + private final MechanismInformation mechanismInformation; + + /** + * Construct a new instance with the appropriate mechanism information. + * + * @param mechanismInformation the mechanism information for the current authentication attempt. + */ + public MechanismInformationCallback(final MechanismInformation mechanismInformation) { + this.mechanismInformation = mechanismInformation; + } + + /** + * Get the type of the mechanism information for the current authentication attempt. + * + * @return the type of the mechanism for the current authentication. + */ + public MechanismInformation getMechanismInformation() { + return mechanismInformation; + } + +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/callback/OptionalNameCallback.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/callback/OptionalNameCallback.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/callback/OptionalNameCallback.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,53 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2017 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.auth.callback; + +import javax.security.auth.callback.NameCallback; + +/** + * A {@code NameCallback} which is optional, for mechanisms that can accept a name from the server. + * + * @author David M. Lloyd + */ +public final class OptionalNameCallback extends NameCallback implements ExtendedCallback { + private static final long serialVersionUID = 1848637046120873969L; + + /** + * Construct a new instance. + * + * @param prompt the prompt to offer the user + */ + public OptionalNameCallback(final String prompt) { + super(prompt); + } + + /** + * Construct a new instance. + * + * @param prompt the prompt to offer the user + * @param defaultName the default name to specify (must not be {@code null}) + */ + public OptionalNameCallback(final String prompt, final String defaultName) { + super(prompt, defaultName); + } + + public boolean needsInformation() { + return true; + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/callback/ParameterCallback.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/callback/ParameterCallback.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/callback/ParameterCallback.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,125 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2014 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.auth.callback; + +import static org.wildfly.security.auth.server._private.ElytronMessages.log; + +import java.io.Serializable; +import java.security.spec.AlgorithmParameterSpec; + +/** + * A callback used to acquire parameter specifications, either for outbound or inbound authentication. The supplied parameter + * information may be used to select an authentication mechanism, or to set parameters for an establishing mechanism. + * The supplied parameter specification should be of a supported + * type; the {@link #isParameterSupported(AlgorithmParameterSpec)} and {@link #isParameterTypeSupported(Class)} methods can be + * used to query the types that are supported. If no credential is available, {@code null} is set, and + * authentication may fail. If an unsupported credential type is set, authentication may fail. + * + * @author David M. Lloyd + */ +public final class ParameterCallback implements ExtendedCallback, Serializable { + + private static final long serialVersionUID = -6000106115779144082L; + + /** + * @serial The list of allowed parameter specification types. + */ + private final Class[] allowedTypes; + /** + * @serial The algorithm parameter specification. + */ + private AlgorithmParameterSpec parameterSpec; + + /** + * Construct a new instance. + * + * @param allowedTypes the allowed types of parameter specification + */ + @SafeVarargs + public ParameterCallback(final Class... allowedTypes) { + this.allowedTypes = allowedTypes; + } + + /** + * Construct a new instance. + * + * @param parameterSpec the default parameter spec value, if any + * @param allowedTypes the allowed types of parameter spec + */ + @SafeVarargs + public ParameterCallback(final AlgorithmParameterSpec parameterSpec, final Class... allowedTypes) { + this.allowedTypes = allowedTypes; + this.parameterSpec = parameterSpec; + } + + /** + * Get the parameter specification. + * + * @return the parameter specification, or {@code null} if it wasn't set yet + */ + public AlgorithmParameterSpec getParameterSpec() { + return parameterSpec; + } + + /** + * Set the parameter specification. + * + * @param parameterSpec the parameter specification, or {@code null} if no parameter specification is available + */ + public void setParameterSpec(final AlgorithmParameterSpec parameterSpec) { + if (! isParameterSupported(parameterSpec)) { + throw log.invalidCredentialTypeSpecified(); + } + this.parameterSpec = parameterSpec; + } + + /** + * Determine whether a parameter specification would be supported by the authentication. + * + * @param parameterSpec the parameter specification to test + * @return {@code true} if the parameter specification is non-{@code null} and supported, {@code false} otherwise + */ + public boolean isParameterSupported(final AlgorithmParameterSpec parameterSpec) { + for (final Class allowedType : allowedTypes) { + if (allowedType.isInstance(parameterSpec)) return true; + } + return false; + } + + /** + * Determine whether a credential type would be supported by the authentication. + * + * @param parameterType the parameter specification type to test + * @return {@code true} if the parameter specification type is supported, {@code false} otherwise + */ + public boolean isParameterTypeSupported(final Class parameterType) { + for (final Class allowedType : allowedTypes) { + if (allowedType.isAssignableFrom(parameterType)) return true; + } + return false; + } + + public boolean isOptional() { + return parameterSpec != null; + } + + public boolean needsInformation() { + return true; + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/callback/PasswordResetCallback.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/callback/PasswordResetCallback.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/callback/PasswordResetCallback.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,95 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2017 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.auth.callback; + +import java.io.Serializable; +import java.util.Arrays; + +import org.wildfly.common.Assert; + +/** + * A callback used when a password reset is required. Interactive callback handlers should have the user enter the + * password two times, comparing them for equality. + * + * @author David M. Lloyd + */ +public final class PasswordResetCallback implements ExtendedCallback, Serializable{ + private static final long serialVersionUID = - 8789058459408593766L; + + private final String prompt; + + private char[] password; + + /** + * Construct a new instance. + * + * @param prompt the password reset prompt (must not be {@code null} or empty) + */ + public PasswordResetCallback(final String prompt) { + Assert.checkNotNullParam("prompt", prompt); + Assert.checkNotEmptyParam("prompt", prompt); + this.prompt = prompt; + } + + /** + * Get the password reset prompt. + * + * @return the password reset prompt + */ + public String getPrompt() { + return prompt; + } + + /** + * Get the new password. + * + * @return the new password, or {@code null} if it was not set + */ + public char[] getPassword() { + final char[] password = this.password; + return password == null ? null : password.clone(); + } + + /** + * Set the new password. + * + * @param password the new password + */ + public void setPassword(final char[] password) { + this.password = password == null ? null : password.clone(); + } + + /** + * Clear the stored password bytes by setting them to {@code ' '}. + */ + public void clearPassword() { + final char[] password = this.password; + if (password != null) { + Arrays.fill(password, ' '); + } + } + + public boolean isOptional() { + return false; + } + + public boolean needsInformation() { + return true; + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/callback/PeerPrincipalCallback.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/callback/PeerPrincipalCallback.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/callback/PeerPrincipalCallback.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,58 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2014 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.auth.callback; + +import org.wildfly.common.Assert; + +import java.io.Serializable; +import java.security.Principal; + +/** + * An optional callback to inform the callback handler of the peer's principal identity. + * + * @author David M. Lloyd + */ +public final class PeerPrincipalCallback implements ExtendedCallback, Serializable { + + private static final long serialVersionUID = -2876104318406026491L; + + /** + * @serial The peer principal. + */ + private final Principal principal; + + /** + * Construct a new instance. + * + * @param principal the peer principal + */ + public PeerPrincipalCallback(final Principal principal) { + Assert.checkNotNullParam("principal", principal); + this.principal = principal; + } + + /** + * Get the peer principal. + * + * @return the peer principal + */ + public Principal getPrincipal() { + return principal; + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/callback/PrincipalAuthorizeCallback.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/callback/PrincipalAuthorizeCallback.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/callback/PrincipalAuthorizeCallback.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,81 @@ +/* + * Copyright 2020 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.callback; + +import java.security.Principal; +import org.wildfly.common.Assert; +import org.wildfly.security.auth.principal.NamePrincipal; + +/** + *

An authorization callback similar to javase {@link javax.security.sasl.AuthorizeCallback} + * but using a generic principal.

+ * + * @author rmartinc + */ +public class PrincipalAuthorizeCallback implements ExtendedCallback { + + private final Principal principal; + private boolean authorized; + + /** + * Creates a new instance to authorize the associated name. + * It will be transformed in a {@link NamePrincipal}. + * + * @param name the name to authorize + */ + public PrincipalAuthorizeCallback(String name) { + Assert.checkNotNullParam("name", name); + this.principal = new NamePrincipal(name); + } + + /** + * Creates a new instance to authorize the associated principal. + * + * @param principal the principal to authorize + */ + public PrincipalAuthorizeCallback(Principal principal) { + Assert.checkNotNullParam("principal", principal); + this.principal = principal; + } + + /** + * Indicates if the principal was successfully authorized. + * + * @return true if the principal was successfully authorized. Otherwise, false + */ + public boolean isAuthorized() { + return authorized; + } + + /** + * Sets whether the authorization is allowed for the principal. + * + * @param authorized authorization result + */ + public void setAuthorized(boolean authorized) { + this.authorized = authorized; + } + + /** + * Returns the {@link Principal}. + * + * @return the principal (not {@code null}) + */ + public Principal getPrincipal() { + return this.principal; + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/callback/RequestInformationCallback.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/callback/RequestInformationCallback.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/callback/RequestInformationCallback.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,54 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2023 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.auth.callback; + +import javax.security.auth.callback.Callback; +import java.util.HashMap; + +import static org.wildfly.common.Assert.checkNotNullParam; + +/** + * A {@link javax.security.auth.callback.Callback} to inform a server authentication context about current authentication request. + * + */ +public class RequestInformationCallback implements ExtendedCallback { + + /** + * Properties of the current authentication request + */ + private final HashMap props; + + /** + * Construct a new instance of this {@link Callback}. + * + * @param props Properties of the current authentication request + */ + public RequestInformationCallback(HashMap props) { + checkNotNullParam("props", props); + this.props = props; + } + + /** + * Get the properties of this request. + * + * @return properties of the current authentication request + */ + public HashMap getProperties() { + return this.props; + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/callback/SSLCallback.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/callback/SSLCallback.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/callback/SSLCallback.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,66 @@ +/* + * 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.auth.callback; + +import static org.wildfly.common.Assert.checkNotNullParam; + +import javax.net.ssl.SSLSession; + +import org.wildfly.security.ssl.SSLConnection; + +/** + * A callback which provides information to the callback handler about the established SSLSession. + * + * @author David M. Lloyd + */ +public final class SSLCallback implements ExtendedCallback { + + /** + * @serial The SSL session. + */ + private final SSLConnection sslConnection; + + /** + * Construct a new instance. + * + * @param sslConnection the SSL connection (must not be {@code null}) + */ + public SSLCallback(final SSLConnection sslConnection) { + checkNotNullParam("sslConnection", sslConnection); + this.sslConnection = sslConnection; + } + + /** + * Get the SSL session in force. + * + * @return the SSL session in force + */ + public SSLSession getSslSession() { + return sslConnection.getSession(); + } + + /** + * Get the SSL connection. + * + * @return the SSL connection + */ + public SSLConnection getSslConnection() { + return sslConnection; + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/callback/SecurityIdentityCallback.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/callback/SecurityIdentityCallback.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/callback/SecurityIdentityCallback.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,56 @@ +/* + * 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.auth.callback; + +import org.wildfly.security.auth.server.SecurityIdentity; + +/** + * A server-side callback used to pass a realm identity from the callback handler to the authentication mechanism. If + * no realm identity is returned, any inflowed security context will be treated as anonymous. + * + * @author David M. Lloyd + */ +public final class SecurityIdentityCallback implements ExtendedCallback { + + private SecurityIdentity securityIdentity; + + /** + * Construct a new instance. + */ + public SecurityIdentityCallback() { + } + + /** + * Get the realm identity. + * + * @return the realm identity, or {@code null} if there is none + */ + public SecurityIdentity getSecurityIdentity() { + return securityIdentity; + } + + /** + * Set the realm identity. + * + * @param securityIdentity the realm identity, or {@code null} if there is none + */ + public void setSecurityIdentity(final SecurityIdentity securityIdentity) { + this.securityIdentity = securityIdentity; + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/callback/SecurityLayerDisposedCallback.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/callback/SecurityLayerDisposedCallback.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/callback/SecurityLayerDisposedCallback.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,54 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2014 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.auth.callback; + +import java.io.Serializable; + +/** + * A callback which indicates that the corresponding security layer (SASL client, SASL server, etc.) has been disposed + * and any related resources may be relinquished. + * + * @author David M. Lloyd + */ +public final class SecurityLayerDisposedCallback implements ExtendedCallback, Serializable { + + private static final long serialVersionUID = 3720690487735346163L; + + private static final SecurityLayerDisposedCallback INSTANCE = new SecurityLayerDisposedCallback(); + + private SecurityLayerDisposedCallback() { + } + + /** + * Get the singleton instance. + * + * @return the singleton instance + */ + public static SecurityLayerDisposedCallback getInstance() { + return INSTANCE; + } + + Object readResolve() { + return INSTANCE; + } + + Object writeReplace() { + return INSTANCE; + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/callback/ServerCredentialCallback.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/callback/ServerCredentialCallback.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/callback/ServerCredentialCallback.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,70 @@ +/* + * 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.auth.callback; + +import java.security.spec.AlgorithmParameterSpec; + +import org.wildfly.security.credential.Credential; + +/** + * A callback used to acquire the server (or "host") credential. This callback is only used on the server side of + * authentication mechanisms; client callback handlers do not need to recognize this callback. The callback handler is + * expected to provide a credential to this callback. If no credential is available, {@code null} is set, and + * authentication may fail. If an unsupported credential type is set, an exception is thrown. + * + * @author David M. Lloyd + */ +public final class ServerCredentialCallback extends AbstractCredentialCallback { + + /** + * Construct a new instance. + * + * @param credentialType the desired credential type (must not be {@code null}) + * @param algorithm the algorithm name, or {@code null} if any algorithm is suitable or the credential + * type does not use algorithm names + * @param parameterSpec the parameters to use or {@code null} for no parameters + */ + public ServerCredentialCallback(final Class credentialType, final String algorithm, final AlgorithmParameterSpec parameterSpec) { + super(credentialType, algorithm, parameterSpec); + } + + /** + * Construct a new instance. + * + * @param credentialType the desired credential type (must not be {@code null}) + * @param algorithm the algorithm name, or {@code null} if any algorithm is suitable or the credential + * type does not use algorithm names + */ + public ServerCredentialCallback(final Class credentialType, final String algorithm) { + this(credentialType, algorithm, null); + } + + /** + * Construct a new instance which accepts any algorithm name. + * + * @param credentialType the desired credential type (must not be {@code null}) + */ + public ServerCredentialCallback(final Class credentialType) { + this(credentialType, null, null); + } + + public boolean isOptional() { + return false; + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/callback/SocketAddressCallback.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/callback/SocketAddressCallback.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/callback/SocketAddressCallback.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,97 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2014 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.auth.callback; + +import org.wildfly.common.Assert; + +import java.io.Serializable; +import java.net.SocketAddress; + +/** + * An optional callback which is used to inform the callback handler of the endpoint addresses of a connection being + * authenticated. + * + * @author David M. Lloyd + */ +public final class SocketAddressCallback implements ExtendedCallback, Serializable { + + private static final long serialVersionUID = -4287450716990929230L; + + /** + * @serial The socket address. + */ + private final SocketAddress address; + /** + * @serial The socket address disposition or "kind". + */ + private final Kind kind; + + /** + * Construct a new instance. + * + * @param address the endpoint socket address + * @param kind the disposition of the endpoint + */ + public SocketAddressCallback(final SocketAddress address, final Kind kind) { + this.address = Assert.checkNotNullParam("address", address); + this.kind = Assert.checkNotNullParam("kind", kind); + } + + /** + * Construct a new instance. The disposition is assumed to be {@link Kind#PEER}. + * + * @param address the endpoint socket address + */ + public SocketAddressCallback(final SocketAddress address) { + this(address, Kind.PEER); + } + + /** + * Get the endpoint socket address. + * + * @return the endpoint socket address + */ + public SocketAddress getAddress() { + return address; + } + + /** + * Get the endpoint disposition. + * + * @return the endpoint disposition + */ + public Kind getKind() { + return kind; + } + + /** + * Endpoint disposition kinds. + */ + public enum Kind { + /** + * The local endpoint. + */ + LOCAL, + /** + * The remote (peer) endpoint. + */ + PEER, + ; + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/callback/SocketAddressQueryCallbackHandler.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/callback/SocketAddressQueryCallbackHandler.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/callback/SocketAddressQueryCallbackHandler.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,89 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2014 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.auth.callback; + +import java.io.IOException; +import java.net.SocketAddress; +import java.util.concurrent.atomic.AtomicBoolean; + +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.UnsupportedCallbackException; + +/** + * A callback handler which delegates to another callback handler, passing the local and/or peer socket address to that + * callback handler on its first invocation. + * + * @author David M. Lloyd + */ +public final class SocketAddressQueryCallbackHandler implements CallbackHandler { + private final CallbackHandler delegate; + private final SocketAddress localAddress; + private final SocketAddress peerAddress; + + private final AtomicBoolean once = new AtomicBoolean(); + + /** + * Construct a new instance. + * + * @param delegate the callback handler to delegate to + * @param localAddress the local socket address + * @param peerAddress the peer socket address + */ + public SocketAddressQueryCallbackHandler(final CallbackHandler delegate, final SocketAddress localAddress, final SocketAddress peerAddress) { + this.delegate = delegate; + this.localAddress = localAddress; + this.peerAddress = peerAddress; + } + + public void handle(final Callback[] callbacks) throws IOException, UnsupportedCallbackException { + if (localAddress == null && peerAddress == null || ! once.compareAndSet(false, true)) { + delegate.handle(callbacks); + return; + } + final SocketAddressCallback localCallback = localAddress == null ? null : new SocketAddressCallback(localAddress, SocketAddressCallback.Kind.LOCAL); + final SocketAddressCallback peerCallback = peerAddress == null ? null : new SocketAddressCallback(peerAddress, SocketAddressCallback.Kind.PEER); + final Callback[] newCallbacks; + final int length = callbacks.length; + if (localCallback != null && peerCallback != null) { + newCallbacks = new Callback[length + 2]; + newCallbacks[0] = localCallback; + newCallbacks[1] = peerCallback; + System.arraycopy(callbacks, 0, newCallbacks, 2, length); + } else if (localCallback != null) { + newCallbacks = new Callback[length + 1]; + newCallbacks[0] = localCallback; + System.arraycopy(callbacks, 0, newCallbacks, 1, length); + } else { + // peerCallback != null + newCallbacks = new Callback[length + 1]; + newCallbacks[0] = peerCallback; + System.arraycopy(callbacks, 0, newCallbacks, 1, length); + } + try { + delegate.handle(newCallbacks); + } catch (UnsupportedCallbackException e) { + if (e.getCallback() instanceof SocketAddressCallback) { + delegate.handle(callbacks); + return; + } + throw e; + } + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/callback/TrustedAuthoritiesCallback.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/callback/TrustedAuthoritiesCallback.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/callback/TrustedAuthoritiesCallback.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,62 @@ +/* + * 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.auth.callback; + +import java.util.List; + +import org.wildfly.security.x500.TrustedAuthority; + +/** + * An optional callback used to retrieve information about trusted certificate authorities + * for authenticating peers. + * + * @author Farah Juma + */ +public final class TrustedAuthoritiesCallback implements ExtendedCallback { + + private List trustedAuthorities; + + /** + * Construct a new instance. + */ + public TrustedAuthoritiesCallback() { + } + + /** + * Get the retrieved trusted authorities. + * + * @return the retrieved trusted authorities (may be {@code null}) + */ + public List getTrustedAuthorities() { + return trustedAuthorities; + } + + /** + * Set the retrieved trusted authorities. + * + * @param trustedAuthorities the retrieved trusted authorities (may be {@code null}) + */ + public void setTrustedAuthorities(final List trustedAuthorities) { + this.trustedAuthorities = trustedAuthorities; + } + + public boolean needsInformation() { + return true; + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/callback/package-info.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/callback/package-info.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/callback/package-info.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,22 @@ +/* + * 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. + */ + +/** + * Extended callbacks and supporting utilities which allow efficient callback handler implementation. + */ +package org.wildfly.security.auth.callback; Index: 3rdParty_sources/elytron/org/wildfly/security/auth/package-info.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/package-info.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/package-info.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,22 @@ +/* + * 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. + */ + +/** + * Classes relating to authentication on the client and server side. + */ +package org.wildfly.security.auth; Index: 3rdParty_sources/elytron/org/wildfly/security/auth/permission/ChangeRoleMapperPermission.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/permission/ChangeRoleMapperPermission.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/permission/ChangeRoleMapperPermission.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,52 @@ +/* + * 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.auth.permission; + +import org.wildfly.security.permission.AbstractNameOnlyPermission; + +/** + * The permission to change a role mapper category on a security identity. + */ +public final class ChangeRoleMapperPermission extends AbstractNameOnlyPermission { + + private static final long serialVersionUID = - 6742662884954321082L; + + /** + * Construct a new instance. + * + * @param name the category name, or {@code *} for all categories + */ + public ChangeRoleMapperPermission(final String name) { + super(name); + } + + /** + * Construct a new instance. + * + * @param name the category name, or {@code *} for all categories + * @param ignored the permission actions (ignored) + */ + public ChangeRoleMapperPermission(final String name, @SuppressWarnings("unused") final String ignored) { + this(name); + } + + public ChangeRoleMapperPermission withName(final String name) { + return new ChangeRoleMapperPermission(name); + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/permission/LoginPermission.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/permission/LoginPermission.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/permission/LoginPermission.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,65 @@ +/* + * 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.auth.permission; + +import org.wildfly.security.permission.AbstractBooleanPermission; + +/** + * Establish whether the current identity has permission to complete an authentication ("log in"). + * + * @author David M. Lloyd + */ +public final class LoginPermission extends AbstractBooleanPermission { + + private static final long serialVersionUID = - 5776174571770792690L; + + /** + * Construct a new instance. + */ + public LoginPermission() { + } + + /** + * Construct a new instance. + * + * @param name ignored + */ + public LoginPermission(@SuppressWarnings("unused") final String name) { + } + + /** + * Construct a new instance. + * + * @param name ignored + * @param actions ignored + */ + public LoginPermission(@SuppressWarnings("unused") final String name, @SuppressWarnings("unused") final String actions) { + } + + private static final LoginPermission INSTANCE = new LoginPermission(); + + /** + * Get the instance of this class. + * + * @return the instance of this class + */ + public static LoginPermission getInstance() { + return INSTANCE; + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/permission/RunAsPrincipalPermission.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/permission/RunAsPrincipalPermission.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/permission/RunAsPrincipalPermission.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,54 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2013 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.auth.permission; + +import org.wildfly.security.permission.AbstractNameOnlyPermission; + +/** + * The permission to run as another principal within some security domain. Note that this permission is checked relative + * to the security domain that the user is authenticated to. The principal name is the effective name after all rewrite + * operations have taken place. + */ +public final class RunAsPrincipalPermission extends AbstractNameOnlyPermission { + + private static final long serialVersionUID = -3361334389433669815L; + + /** + * Construct a new instance. + * + * @param name the principal name, or {@code *} for global run-as permissions + */ + public RunAsPrincipalPermission(final String name) { + super(name); + } + + /** + * Construct a new instance. + * + * @param name the principal name, or {@code *} for global run-as permissions + * @param ignored the permission actions (ignored) + */ + public RunAsPrincipalPermission(final String name, @SuppressWarnings("unused") final String ignored) { + this(name); + } + + public RunAsPrincipalPermission withName(final String name) { + return new RunAsPrincipalPermission(name); + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/permission/package-info.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/permission/package-info.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/permission/package-info.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,22 @@ +/* + * 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. + */ + +/** + * Permissions which pertain to authentication and authorization. + */ +package org.wildfly.security.auth.permission; Index: 3rdParty_sources/elytron/org/wildfly/security/auth/principal/AnonymousPrincipal.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/principal/AnonymousPrincipal.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/principal/AnonymousPrincipal.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,100 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2013 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.auth.principal; + +import java.io.Serializable; +import java.security.Principal; + +/** + * The singleton anonymous principal. + */ +public final class AnonymousPrincipal implements Principal, Serializable { + + private static final long serialVersionUID = -2539713938519809712L; + + private static final AnonymousPrincipal INSTANCE = new AnonymousPrincipal(); + + /** + * Construct a new instance (should not be used; call {@link #getInstance()} instead). + */ + public AnonymousPrincipal() { + } + + /** + * Construct a new instance (should not be used; call {@link #getInstance()} instead). + * + * @param ignored ignored + */ + public AnonymousPrincipal(String ignored) { + } + + /** + * Get the anonymous principal instance. + * + * @return the anonymous principal instance + */ + public static AnonymousPrincipal getInstance() { + return INSTANCE; + } + + /** + * Get the principal name (always "anonymous"). + * + * @return the principal name (always "anonymous") + */ + public String getName() { + return "anonymous"; + } + + /** + * Determine whether the given object is also an anonymous principal. + * + * @param o the other object + * @return {@code true} if the object is an anonymous principal, {@code false} otherwise + */ + public boolean equals(final Object o) { + return o instanceof AnonymousPrincipal; + } + + /** + * Get the hash code of this principal. + * + * @return the hash code of this principal + */ + public int hashCode() { + return 3; + } + + /** + * Get a representation of the principal name (always "anonymous"). + * + * @return the string representation of the principal name (always "anonymous") + */ + public String toString() { + return "anonymous"; + } + + Object writeReplace() { + return INSTANCE; + } + + Object readResolve() { + return INSTANCE; + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/principal/CompositePrincipal.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/principal/CompositePrincipal.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/principal/CompositePrincipal.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,225 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2016 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.auth.principal; + +import java.io.IOException; +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; +import java.io.Serializable; +import java.security.Principal; +import java.util.Arrays; +import java.util.Collection; +import java.util.Iterator; + +import org.wildfly.common.Assert; +import org.wildfly.security.util.ArrayIterator; + +/** + * A composite principal that consists of multiple elements of possibly disparate type. This may be used to locate + * a unique principal in a realm which is backed by a database that uses a composite key; in this case, the constituent + * principals may be names or numbers, or a combination of both. + * + * @author David M. Lloyd + */ +public final class CompositePrincipal implements Principal, Serializable, Iterable { + private static final long serialVersionUID = - 2610733957848661774L; + private static final Principal[] NO_PRINCIPALS = new Principal[0]; + + private final Principal[] p; + + /** + * Construct a new instance. + * + * @param principals the collection of principals to use (must not be {@code null}) + */ + public CompositePrincipal(Collection principals) { + this(principals.toArray(NO_PRINCIPALS), false); + } + + /** + * Construct a new instance. + * + * @param principals the principals to use (must not be {@code null}) + */ + public CompositePrincipal(Principal... principals) { + this(principals, true); + } + + private CompositePrincipal(Principal[] principals, boolean clone) { + p = principals.length == 0 ? NO_PRINCIPALS : clone ? principals.clone() : principals; + for (int i = 0; i < p.length; i++) { + Assert.checkNotNullArrayParam("principals", i, p[i]); + } + } + + /** + * Get the principal name. + * + * @return the principal name, which is a string containing all of the nested principals + */ + public String getName() { + return Arrays.toString(p); + } + + /** + * Determine whether this composite principal contains the given nested principal. + * + * @param principal the nested principal (must not be {@code null}) + * @return {@code true} if this principal contains the nested principal, {@code false} otherwise + */ + public boolean contains(final Principal principal) { + Assert.checkNotNullParam("principal", principal); + for (Principal test : p) { + if (test.equals(principal)) { + return true; + } + } + return false; + } + + /** + * Determine whether this composite principal contains the a nested principal of the given type class. + * + * @param type the nested principal type class (must not be {@code null}) + * @return {@code true} if this principal contains a nested principal of the given type, {@code false} otherwise + */ + public boolean contains(final Class type) { + Assert.checkNotNullParam("type", type); + for (Principal test : p) { + if (type.isInstance(test)) { + return true; + } + } + return false; + } + + /** + * Get the number of nested principals. + * + * @return the number of nested principals + */ + public int size() { + return p.length; + } + + /** + * Get the principal at the given index. + * + * @param idx the index + * @return the principal at the given index (not {@code null}) + * @throws IndexOutOfBoundsException if the given index is less than zero or greater than or equal to {@link #size()} + */ + public Principal get(int idx) { + try { + return p[idx]; + } catch (ArrayIndexOutOfBoundsException e) { + throw new IndexOutOfBoundsException(); + } + } + + /** + * Get the principal at the given index, if it is of the given type. + * + * @param idx the index + * @param type the principal type class (must not be {@code null}) + * @param

the principal type + * @return the principal at the given index or {@code null} if that principal is not of the given type + * @throws IndexOutOfBoundsException if the given index is less than zero or greater than or equal to {@link #size()} + */ + public

P get(int idx, Class

type) { + Assert.checkNotNullParam("type", type); + final Principal item = get(idx); + return type.isInstance(item) ? type.cast(item) : null; + } + + /** + * Get the first principal with the given type, if any. + * + * @param type the principal type class (must not be {@code null}) + * @param

the principal type + * @return the first principal with the given type, or {@code null} if none was found + * @throws IndexOutOfBoundsException if the given index is less than zero or greater than or equal to {@link #size()} + */ + public

P get(Class

type) { + Assert.checkNotNullParam("type", type); + for (Principal item : p) { + if (type.isInstance(item)) type.cast(item); + } + return null; + } + + /** + * Get an iterator over this principal. + * + * @return an iterator over this principal (not {@code null}) + */ + public Iterator iterator() { + return new ArrayIterator(p); + } + + /** + * Determine whether this principal is equal to the given object. + * + * @param obj the object + * @return {@code true} if they are equal, {@code false} otherwise + */ + public boolean equals(final Object obj) { + return obj instanceof CompositePrincipal && equals((CompositePrincipal) obj); + } + + /** + * Determine whether this principal is equal to the given object. + * + * @param obj the object + * @return {@code true} if they are equal, {@code false} otherwise + */ + public boolean equals(final CompositePrincipal obj) { + return obj == this || obj != null && Arrays.deepEquals(p, obj.p); + } + + /** + * Get the hash code of this principal. + * + * @return the hash code of this principal + */ + public int hashCode() { + return Arrays.deepHashCode(p); + } + + /** + * Get this principal as a string. + * + * @return this principal as a string (not {@code null}) + */ + public String toString() { + return getName(); + } + + private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { + ois.defaultReadObject(); + if (p == null) { + throw new InvalidObjectException("Null principals array"); + } + for (Principal principal : p) { + if (principal == null) { + throw new InvalidObjectException("Null principal array element"); + } + } + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/principal/NamePrincipal.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/principal/NamePrincipal.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/principal/NamePrincipal.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,128 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2014 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.auth.principal; + +import java.io.Serializable; +import java.security.Principal; + +import org.wildfly.common.Assert; + +/** + * A principal which is comprised of a simple {@code String} name. + * + * @author David M. Lloyd + */ +public final class NamePrincipal implements Principal, Serializable { + + private static final long serialVersionUID = -6380283371738985125L; + + /** + * @serial The principal name. + */ + private final String name; + + /** + * Construct a new instance. + * + * @param name the principal name + */ + public NamePrincipal(final String name) { + Assert.checkNotNullParam("name", name); + this.name = name; + } + + /** + * Get the principal name. + * + * @return the principal name + */ + public String getName() { + return name; + } + + /** + * Get the hash code of this principal. + * + * @return the hash code of this principal + */ + public int hashCode() { + return name.hashCode(); + } + + /** + * Determine whether this principal is equal to the given object. + * + * @param obj the object + * @return {@code true} if they are equal, {@code false} otherwise + */ + public boolean equals(final Object obj) { + return obj instanceof NamePrincipal && equals((NamePrincipal) obj); + } + + /** + * Determine whether this principal is equal to the given object. + * + * @param obj the object + * @return {@code true} if they are equal, {@code false} otherwise + */ + public boolean equals(final NamePrincipal obj) { + return obj != null && name.equals(obj.name); + } + + /** + * Get a string representation of this principal. + * + * @return the string representation of this principal + */ + public String toString() { + return name; + } + + /** + * Attempt to convert the given principal to a {@code NamePrincipal}. + * + * @param principal the original principal + * @return the {@code NamePrincipal} or {@code null} if the principal cannot be converted + */ + public static NamePrincipal from(Principal principal) { + if (principal instanceof NamePrincipal) { + return (NamePrincipal) principal; + } + return isConvertibleTo(principal) ? new NamePrincipal(principal.getName()) : null; + } + + /** + * Check if the given principal could be converted to a {@code NamePrincipal}. + * + * @param principal the original principal + * @return {@code true} if the principal can be converted to a {@code NamePrincipal} and {@code false} otherwise + */ + public static boolean isConvertibleTo(Principal principal) { + if (principal instanceof NamePrincipal) { + return true; + } + if (principal != null) { + String name = principal.getName(); + if (name != null && ! name.isEmpty()) { + return true; + } + } + return false; + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/principal/NumericPrincipal.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/principal/NumericPrincipal.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/principal/NumericPrincipal.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,111 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2016 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.auth.principal; + +import java.io.Serializable; +import java.security.Principal; + +/** + * A principal which is represented by a numeric ID, such as what a database might use for a primary key. + * + * @author David M. Lloyd + */ +public final class NumericPrincipal implements Principal, Serializable { + private static final long serialVersionUID = 6679865697029801196L; + + /** + * @serial the principal ID + */ + private final long id; + + /** + * Construct a new instance. + * + * @param id the ID of the principal + */ + public NumericPrincipal(final long id) { + this.id = id; + } + + /** + * Construct a new instance from a decimal string. + * + * @param id the ID of the principal, as a string + * @throws NumberFormatException if the number is not a valid non-negative long integer + */ + public NumericPrincipal(final String id) throws NumberFormatException { + this(Long.parseUnsignedLong(id)); + } + + /** + * Get the ID of the principal. + * + * @return the ID of the principal + */ + public long getId() { + return id; + } + + /** + * Determine whether this principal is equal to the given object. + * + * @param obj the object + * @return {@code true} if they are equal, {@code false} otherwise + */ + public boolean equals(final Object obj) { + return obj instanceof NumericPrincipal && equals((NumericPrincipal) obj); + } + + /** + * Determine whether this principal is equal to the given object. + * + * @param obj the object + * @return {@code true} if they are equal, {@code false} otherwise + */ + public boolean equals(final NumericPrincipal obj) { + return obj != null && id == obj.id; + } + + /** + * Get the hash code of this principal. + * + * @return the hash code of this principal + */ + public int hashCode() { + return (int) id; + } + + /** + * Get this principal as a string. + * + * @return this principal as a string (not {@code null}) + */ + public String toString() { + return getName(); + } + + /** + * Returns the name of this principal, which is just the string representation of the ID. + * + * @return the name of this principal (not {@code null}) + */ + public String getName() { + return Long.toUnsignedString(id); + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/principal/RealmNestedPrincipal.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/principal/RealmNestedPrincipal.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/principal/RealmNestedPrincipal.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,142 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2016 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.auth.principal; + +import java.io.IOException; +import java.io.InvalidObjectException; +import java.io.ObjectInputStream; +import java.io.Serializable; +import java.security.Principal; + +import org.wildfly.common.Assert; + +/** + * A principal type which is used to find a specific identity in a specific realm. This principal can be used to locate + * an exact identity whose name may have changed or may be unknown, but which can be located another way (for example, + * by primary key). + * + * @author David M. Lloyd + */ +public final class RealmNestedPrincipal implements Principal, Serializable { + private static final long serialVersionUID = 3776427564561628331L; + + // names are short to facilitate serialization + /** + * @serial the realm name (must not be {@code null}) + */ + private final String r; + /** + * @serial the nested principal (must not be {@code null}) + */ + private final Principal p; + + /** + * Construct a new instance. + * + * @param realmName the realm name (must not be {@code null}) + * @param nestedPrincipal the nested principal (must not be {@code null}) + */ + public RealmNestedPrincipal(final String realmName, final Principal nestedPrincipal) { + Assert.checkNotNullParam("realmName", realmName); + Assert.checkNotNullParam("nestedPrincipal", nestedPrincipal); + this.r = realmName; + this.p = nestedPrincipal; + } + + /** + * Get the realm name. + * + * @return the realm name (not {@code null}) + */ + public String getRealmName() { + return r; + } + + /** + * Get the nested principal. + * + * @return the nested principal (not {@code null}) + */ + public Principal getNestedPrincipal() { + return p; + } + + /** + * Get the nested principal if it is of the given type class. + * + * @return the nested principal, or {@code null} if the nested principal is not of the given type + */ + public

P getNestedPrincipal(Class

principalClass) { + return principalClass.isInstance(p) ? principalClass.cast(p) : null; + } + + /** + * Returns the name of this principal, which is composed of the realm name and the name of the nested principal. + * + * @return the name of this principal + */ + public String getName() { + return r + "/" + p.getName(); + } + + /** + * Get the hash code of this principal. + * + * @return the hash code of this principal + */ + public int hashCode() { + return r.hashCode() * 17 + p.hashCode(); + } + + /** + * Determine whether this principal is equal to the given object. + * + * @param obj the object + * @return {@code true} if they are equal, {@code false} otherwise + */ + public boolean equals(final Object obj) { + return obj instanceof RealmNestedPrincipal && equals((RealmNestedPrincipal) obj); + } + + /** + * Determine whether this principal is equal to the given object. + * + * @param obj the object + * @return {@code true} if they are equal, {@code false} otherwise + */ + public boolean equals(final RealmNestedPrincipal obj) { + return this == obj || obj != null && r.equals(obj.r) && p.equals(obj.p); + } + + /** + * Get this principal as a string. + * + * @return this principal as a string (not {@code null}) + */ + public String toString() { + return getName(); + } + + private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { + ois.defaultReadObject(); + if (r == null || p == null) { + throw new InvalidObjectException("All fields must be non-null"); + } + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/principal/package-info.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/principal/package-info.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/principal/package-info.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,23 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2013 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. + */ + +/** + * The principal types used by this API. Note that, as a matter of policy, principal types are defined by + * format (e.g. plain string, X.500 string, etc.), not by usage (e.g. user name, host name). + */ +package org.wildfly.security.auth.principal; Index: 3rdParty_sources/elytron/org/wildfly/security/auth/realm/AggregateSecurityRealm.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/realm/AggregateSecurityRealm.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/realm/AggregateSecurityRealm.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,277 @@ +/* + * 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.auth.realm; + +import static org.wildfly.common.Assert.checkNotNullParam; +import static org.wildfly.security.auth.realm.ElytronMessages.log; + +import java.security.Principal; +import java.security.spec.AlgorithmParameterSpec; +import java.util.function.Function; + +import org.wildfly.security.auth.SupportLevel; +import org.wildfly.security.auth.server.RealmIdentity; +import org.wildfly.security.auth.server.RealmUnavailableException; +import org.wildfly.security.auth.server.SecurityRealm; +import org.wildfly.security.auth.server.event.RealmAuthenticationEvent; +import org.wildfly.security.auth.server.event.RealmAuthorizationEvent; +import org.wildfly.security.auth.server.event.RealmEvent; +import org.wildfly.security.authz.AggregateAttributes; +import org.wildfly.security.authz.Attributes; +import org.wildfly.security.authz.AuthorizationIdentity; +import org.wildfly.security.credential.Credential; +import org.wildfly.security.evidence.Evidence; + +/** + * A realm which directs authentication to one realm and authorization to another. The authentication realm need + * not provide any authorization information. Likewise the authorization realm need not provide any authentication + * credential acquisition or verification capabilities. + * + * @author David M. Lloyd + */ +public final class AggregateSecurityRealm implements SecurityRealm { + private final SecurityRealm authenticationRealm; + private final SecurityRealm[] authorizationRealms; + private final Function principalTransformer; + + /** + * Construct a new instance. + * + * @param authenticationRealm the realm to use for authentication + * @param authorizationRealm the realm to use for authorization + */ + public AggregateSecurityRealm(final SecurityRealm authenticationRealm, final SecurityRealm authorizationRealm) { + this.authenticationRealm = checkNotNullParam("authenticationRealm", authenticationRealm); + checkNotNullParam("authorizationRealm", authorizationRealm); + this.authorizationRealms = new SecurityRealm[] { authorizationRealm }; + this.principalTransformer = null; + } + + public AggregateSecurityRealm(final SecurityRealm authenticationRealm, final SecurityRealm... authorizationRealms) { + this.authenticationRealm = checkNotNullParam("authenticationRealm", authenticationRealm); + this.authorizationRealms = checkNotNullParam("authorizationRealms", authorizationRealms); + this.principalTransformer = null; + + } + public AggregateSecurityRealm(final SecurityRealm authenticationRealm, Function principalTransformer, final SecurityRealm... authorizationRealms) { + this.authenticationRealm = checkNotNullParam("authenticationRealm", authenticationRealm); + this.authorizationRealms = checkNotNullParam("authorizationRealms", authorizationRealms); + this.principalTransformer = principalTransformer; + } + + @Override + public RealmIdentity getRealmIdentity(final Evidence evidence) throws RealmUnavailableException { + boolean ok = false; + final RealmIdentity authenticationIdentity = authenticationRealm.getRealmIdentity(evidence); + if (authenticationIdentity.exists()) { + log.tracef("Authentication identity for principal [%s] found.", evidence.getDecodedPrincipal()); + } + final RealmIdentity[] authorizationIdentities = new RealmIdentity[authorizationRealms.length]; + try { + for (int i = 0; i < authorizationIdentities.length; i++) { + SecurityRealm authorizationRealm = authorizationRealms[i]; + authorizationIdentities[i] = (authorizationRealm == authenticationRealm) ? authenticationIdentity + : getAuthorizationIdentity(authorizationRealm, evidence, principalTransformer, authenticationIdentity); + if (authorizationIdentities[i].exists()) { + log.tracef("Authorization identity for principal [%s] found.", evidence.getDecodedPrincipal()); + } + } + + final Identity identity = new Identity(authenticationIdentity, authorizationIdentities); + ok = true; + return identity; + } finally { + if (!ok) { + authenticationIdentity.dispose(); + for (RealmIdentity current : authorizationIdentities) { + if (current != null) + current.dispose(); + } + } + } + } + + @Override + public RealmIdentity getRealmIdentity(final Principal principal) throws RealmUnavailableException { + boolean ok = false; + final RealmIdentity authenticationIdentity = authenticationRealm.getRealmIdentity(principal); + if (authenticationIdentity.exists()) { + log.tracef("Authentication identity for principal [%s] found.", principal); + } + Principal authorizationPrincipal = principal; + if (principalTransformer != null) { + authorizationPrincipal = principalTransformer.apply(authorizationPrincipal); + if (authorizationPrincipal == null) throw ElytronMessages.log.transformedPrincipalCannotBeNull(); + } + + final RealmIdentity[] authorizationIdentities = new RealmIdentity[authorizationRealms.length]; + try { + for (int i = 0; i < authorizationIdentities.length; i++) { + SecurityRealm authorizationRealm = authorizationRealms[i]; + authorizationIdentities[i] = (authorizationRealm == authenticationRealm) && (principalTransformer == null) ? authenticationIdentity : authorizationRealm.getRealmIdentity(authorizationPrincipal); + if (authorizationIdentities[i].exists()) { + log.tracef("Authorization identity for principal [%s] found.", principal); + } + } + + final Identity identity = new Identity(authenticationIdentity, authorizationIdentities); + ok = true; + return identity; + } finally { + if (!ok) { + authenticationIdentity.dispose(); + for (RealmIdentity current : authorizationIdentities) { + if (current != null) + current.dispose(); + } + } + } + } + + @Override + public SupportLevel getCredentialAcquireSupport(final Class credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException { + return authenticationRealm.getCredentialAcquireSupport(credentialType, algorithmName, parameterSpec); + } + + @Override + public SupportLevel getEvidenceVerifySupport(final Class evidenceType, final String algorithmName) throws RealmUnavailableException { + return authenticationRealm.getEvidenceVerifySupport(evidenceType, algorithmName); + } + + private RealmIdentity getAuthorizationIdentity(SecurityRealm authorizationRealm, Evidence evidence, Function principalTransformer, + RealmIdentity authenticationIdentity) throws RealmUnavailableException { + if (principalTransformer == null) { + if (evidence.getPrincipal() == null) { + return authorizationRealm.getRealmIdentity(authenticationIdentity.getRealmIdentityPrincipal()); + } else { + return authorizationRealm.getRealmIdentity(evidence); + } + } else { + if (evidence.getPrincipal() == null) { + Principal authorizationPrincipal = authenticationIdentity.getRealmIdentityPrincipal(); + authorizationPrincipal = principalTransformer.apply(authorizationPrincipal); + if (authorizationPrincipal == null) throw ElytronMessages.log.transformedPrincipalCannotBeNull(); + return authorizationRealm.getRealmIdentity(authorizationPrincipal); + } else { + return authorizationRealm.getRealmIdentity(evidence, principalTransformer); + } + } + } + + @Override + public void handleRealmEvent(final RealmEvent event) { + if (event instanceof RealmAuthenticationEvent) { + authenticationRealm.handleRealmEvent(event); + } else if (event instanceof RealmAuthorizationEvent) { + for (SecurityRealm current : authorizationRealms) { + SecurityRealm.safeHandleRealmEvent(current, event); + } + } else { + // use safe wrapper to ensure both are called + SecurityRealm.safeHandleRealmEvent(authenticationRealm, event); + for (SecurityRealm current : authorizationRealms) { + SecurityRealm.safeHandleRealmEvent(current, event); + } + } + } + + static final class Identity implements RealmIdentity { + + private final RealmIdentity authenticationIdentity; + private final RealmIdentity[] authorizationIdentities; + + Identity(final RealmIdentity authenticationIdentity, final RealmIdentity[] authorizationIdentities) { + this.authenticationIdentity = authenticationIdentity; + this.authorizationIdentities = authorizationIdentities; + } + + @Override + public Principal getRealmIdentityPrincipal() { + return authenticationIdentity.getRealmIdentityPrincipal(); + } + + @Override + public SupportLevel getCredentialAcquireSupport(final Class credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException { + return authenticationIdentity.getCredentialAcquireSupport(credentialType, algorithmName, parameterSpec); + } + + @Override + public SupportLevel getEvidenceVerifySupport(final Class evidenceType, final String algorithmName) throws RealmUnavailableException { + return authenticationIdentity.getEvidenceVerifySupport(evidenceType, algorithmName); + } + + @Override + public C getCredential(final Class credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException { + return authenticationIdentity.getCredential(credentialType, algorithmName, parameterSpec); + } + + @Override + public C getCredential(final Class credentialType, final String algorithmName) throws RealmUnavailableException { + return authenticationIdentity.getCredential(credentialType, algorithmName); + } + + @Override + public C getCredential(final Class credentialType) throws RealmUnavailableException { + return authenticationIdentity.getCredential(credentialType); + } + + @Override + public boolean verifyEvidence(final Evidence evidence) throws RealmUnavailableException { + return authenticationIdentity.verifyEvidence(evidence); + } + + @Override + public boolean exists() throws RealmUnavailableException { + return authenticationIdentity.exists(); + } + + @Override + public AuthorizationIdentity getAuthorizationIdentity() throws RealmUnavailableException { + if (authorizationIdentities.length == 1) { + return authorizationIdentities[0].getAuthorizationIdentity(); + } + + final AuthorizationIdentity[] authorizationIdentities = new AuthorizationIdentity[this.authorizationIdentities.length]; + for (int i = 0; i < authorizationIdentities.length; i++) { + authorizationIdentities[i] = this.authorizationIdentities[i].getAuthorizationIdentity(); + } + + // Use a Supplier here so we only load and aggregate the attributes if they are actually used. + return AuthorizationIdentity.basicIdentity(() -> combineAttributes(authorizationIdentities), "Aggregated"); + } + + private Attributes combineAttributes(AuthorizationIdentity[] authorizationIdentities) { + Attributes[] attributes = new Attributes[authorizationIdentities.length]; + for (int i = 0; i < attributes.length; i++) { + attributes[i] = authorizationIdentities[i].getAttributes(); + } + + return AggregateAttributes.aggregateOf(attributes); + } + + @Override + public void dispose() { + authenticationIdentity.dispose(); + for (RealmIdentity current : authorizationIdentities) { + current.dispose(); + } + } + } + +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/realm/CacheableSecurityRealm.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/realm/CacheableSecurityRealm.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/realm/CacheableSecurityRealm.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,40 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2016 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.auth.realm; + +import java.security.Principal; +import java.util.function.Consumer; + +import org.wildfly.security.auth.server.SecurityRealm; + +/** + * This interface defines a contract for a {@link SecurityRealm} that supports caching of {@link org.wildfly.security.auth.server.RealmIdentity} instances. + * + * @author Pedro Igor + * @see CachingSecurityRealm + */ +public interface CacheableSecurityRealm extends SecurityRealm { + + /** + * Register a listener that should be invoked by this realm in order to notify the caching layer about changes to a specific identity. + * + * @param listener the listener + */ + void registerIdentityChangeListener(Consumer listener); +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/realm/CachingModifiableSecurityRealm.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/realm/CachingModifiableSecurityRealm.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/realm/CachingModifiableSecurityRealm.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,202 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2016 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.auth.realm; + +import java.security.Principal; +import java.security.Provider; +import java.security.spec.AlgorithmParameterSpec; +import java.util.Collection; +import java.util.function.Function; +import java.util.function.Supplier; + +import org.wildfly.common.function.ExceptionConsumer; +import org.wildfly.security.auth.SupportLevel; +import org.wildfly.security.auth.server.ModifiableRealmIdentityIterator; +import org.wildfly.security.auth.server.ModifiableRealmIdentity; +import org.wildfly.security.auth.server.ModifiableSecurityRealm; +import org.wildfly.security.auth.server.RealmIdentity; +import org.wildfly.security.auth.server.RealmUnavailableException; +import org.wildfly.security.authz.Attributes; +import org.wildfly.security.authz.AuthorizationIdentity; +import org.wildfly.security.cache.RealmIdentityCache; +import org.wildfly.security.credential.Credential; +import org.wildfly.security.evidence.Evidence; + +/** + *

A wrapper class that provides caching capabilities for a {@link org.wildfly.security.auth.server.ModifiableSecurityRealm} and its identities. + * + * @author Pedro Igor + */ +public class CachingModifiableSecurityRealm extends CachingSecurityRealm implements ModifiableSecurityRealm { + + /** + * Creates a new instance. + * + * @param realm the {@link CacheableSecurityRealm} whose {@link RealmIdentity} should be cached. + * @param cache the {@link RealmIdentityCache} instance + */ + public CachingModifiableSecurityRealm(CacheableSecurityRealm realm, RealmIdentityCache cache) { + super(realm, cache); + } + + /** + * Creates a new instance. + * + * @param realm the {@link CacheableSecurityRealm} whose {@link RealmIdentity} should be cached. + * @param cache the {@link RealmIdentityCache} instance + * @param providerSupplier the provider supplier to use for verification purposes (must not be {@code null}) + */ + public CachingModifiableSecurityRealm(CacheableSecurityRealm realm, RealmIdentityCache cache, Supplier providerSupplier) { + super(realm, cache, providerSupplier); + } + + @Override + public ModifiableRealmIdentity getRealmIdentityForUpdate(Principal principal) throws RealmUnavailableException { + return wrap(getModifiableSecurityRealm().getRealmIdentityForUpdate(principal)); + } + + @Override + public ModifiableRealmIdentityIterator getRealmIdentityIterator() throws RealmUnavailableException { + ModifiableRealmIdentityIterator iterator = getModifiableSecurityRealm().getRealmIdentityIterator(); + return new ModifiableRealmIdentityIterator() { + @Override + public boolean hasNext() { + return iterator.hasNext(); + } + + @Override + public ModifiableRealmIdentity next() { + return wrap(iterator.next()); + } + }; + } + + private ModifiableRealmIdentity wrap(final ModifiableRealmIdentity modifiable) { + return new ModifiableRealmIdentity() { + @Override + public void delete() throws RealmUnavailableException { + executeAndInvalidate(modifiable -> { modifiable.delete(); }); + } + + @Override + public void create() throws RealmUnavailableException { + modifiable.create(); + } + + @Override + public void setCredentials(Collection credentials) throws RealmUnavailableException { + executeAndInvalidate(modifiable -> { modifiable.setCredentials(credentials); }); + } + + @Override + public void setAttributes(Attributes attributes) throws RealmUnavailableException { + executeAndInvalidate(modifiable -> { modifiable.setAttributes(attributes); }); + } + + @Override + public SupportLevel getCredentialAcquireSupport(Class credentialType, String algorithmName, final AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException { + return modifiable.getCredentialAcquireSupport(credentialType, algorithmName, parameterSpec); + } + + @Override + public C getCredential(Class credentialType) throws RealmUnavailableException { + return modifiable.getCredential(credentialType); + } + + @Override + public SupportLevel getEvidenceVerifySupport(Class evidenceType, String algorithmName) throws RealmUnavailableException { + return modifiable.getEvidenceVerifySupport(evidenceType, algorithmName); + } + + @Override + public boolean verifyEvidence(Evidence evidence) throws RealmUnavailableException { + return modifiable.verifyEvidence(evidence); + } + + @Override + public boolean exists() throws RealmUnavailableException { + return modifiable.exists(); + } + + @Override + public void updateCredential(Credential credential) throws RealmUnavailableException { + executeAndInvalidate(modifiable -> { modifiable.updateCredential(credential); }); + } + + @Override + public Principal getRealmIdentityPrincipal() { + return modifiable.getRealmIdentityPrincipal(); + } + + @Override + public C getCredential(Class credentialType, String algorithmName) throws RealmUnavailableException { + return modifiable.getCredential(credentialType, algorithmName); + } + + @Override + public C getCredential(final Class credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException { + return modifiable.getCredential(credentialType, algorithmName, parameterSpec); + } + + @Override + public R applyToCredential(Class credentialType, Function function) throws RealmUnavailableException { + return modifiable.applyToCredential(credentialType, function); + } + + @Override + public R applyToCredential(Class credentialType, String algorithmName, Function function) throws RealmUnavailableException { + return modifiable.applyToCredential(credentialType, algorithmName, function); + } + + @Override + public R applyToCredential(final Class credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec, final Function function) throws RealmUnavailableException { + return modifiable.applyToCredential(credentialType, algorithmName, parameterSpec, function); + } + + @Override + public void dispose() { + modifiable.dispose(); + } + + @Override + public AuthorizationIdentity getAuthorizationIdentity() throws RealmUnavailableException { + return modifiable.getAuthorizationIdentity(); + } + + @Override + public Attributes getAttributes() throws RealmUnavailableException { + return modifiable.getAttributes(); + } + + private void executeAndInvalidate(ExceptionConsumer operation) throws RealmUnavailableException { + try { + operation.accept(modifiable); + } catch (RealmUnavailableException rue) { + throw rue; + } finally { + removeFromCache(modifiable.getRealmIdentityPrincipal()); + } + } + }; + } + + private ModifiableSecurityRealm getModifiableSecurityRealm() { + return (ModifiableSecurityRealm) getCacheableRealm(); + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/realm/CachingSecurityRealm.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/realm/CachingSecurityRealm.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/realm/CachingSecurityRealm.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,327 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2016 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.auth.realm; + +import static org.wildfly.common.Assert.checkNotNullParam; +import static org.wildfly.security.auth.realm.ElytronMessages.log; +import static org.wildfly.security.provider.util.ProviderUtil.INSTALLED_PROVIDERS; + +import java.security.Principal; +import java.security.Provider; +import java.security.spec.AlgorithmParameterSpec; +import java.util.function.Supplier; + +import org.wildfly.security.auth.SupportLevel; +import org.wildfly.security.auth.server.IdentityCredentials; +import org.wildfly.security.auth.server.RealmIdentity; +import org.wildfly.security.auth.server.RealmUnavailableException; +import org.wildfly.security.auth.server.SecurityRealm; +import org.wildfly.security.auth.server.event.RealmEvent; +import org.wildfly.security.authz.Attributes; +import org.wildfly.security.authz.AuthorizationIdentity; +import org.wildfly.security.cache.RealmIdentityCache; +import org.wildfly.security.credential.Credential; +import org.wildfly.security.credential.PasswordCredential; +import org.wildfly.security.evidence.Evidence; +import org.wildfly.security.evidence.PasswordGuessEvidence; +import org.wildfly.security.password.Password; +import org.wildfly.security.password.interfaces.ClearPassword; + +/** + *

A wrapper class that provides caching capabilities for a {@link SecurityRealm} and its identities. + * + * @author Pedro Igor + */ +public class CachingSecurityRealm implements SecurityRealm { + + private final Supplier providerSupplier; + private final SecurityRealm realm; + private final RealmIdentityCache cache; + + /** + * Creates a new instance. + * + * @param realm the {@link SecurityRealm} whose {@link RealmIdentity} should be cached. + * @param cache the {@link RealmIdentityCache} instance + */ + public CachingSecurityRealm(SecurityRealm realm, RealmIdentityCache cache) { + this(realm, cache, INSTALLED_PROVIDERS); + } + + /** + * Creates a new instance. + * + * @param realm the {@link SecurityRealm} whose {@link RealmIdentity} should be cached. + * @param cache the {@link RealmIdentityCache} instance + * @param providerSupplier the provider supplier to use for verification purposes (must not be {@code null}) + */ + public CachingSecurityRealm(SecurityRealm realm, RealmIdentityCache cache, Supplier providerSupplier) { + this.realm = checkNotNullParam("realm", realm); + this.cache = checkNotNullParam("cache", cache); + this.providerSupplier = checkNotNullParam("providers", providerSupplier); + + if (realm instanceof CacheableSecurityRealm) { + CacheableSecurityRealm cacheable = CacheableSecurityRealm.class.cast(realm); + cacheable.registerIdentityChangeListener(this::removeFromCache); + } else { + throw ElytronMessages.log.realmCacheUnexpectedType(realm, CacheableSecurityRealm.class); + } + } + + @Override + public RealmIdentity getRealmIdentity(Principal principal) throws RealmUnavailableException { + RealmIdentity cached = cache.get(principal); + + if (cached != null) { + log.tracef("Returning cached RealmIdentity for '%s'", principal); + return cached; + } + + RealmIdentity realmIdentity = getCacheableRealm().getRealmIdentity(principal); + + if (!realmIdentity.exists()) { + log.tracef("RealmIdentity for '%s' does not exist, skipping cache.'", principal); + return realmIdentity; + } + + RealmIdentity cachedIdentity = new RealmIdentity() { + final RealmIdentity identity = realmIdentity; + + AuthorizationIdentity authorizationIdentity = null; + Attributes attributes = null; + IdentityCredentials credentials = IdentityCredentials.NONE; + + @Override + public Principal getRealmIdentityPrincipal() { + return identity.getRealmIdentityPrincipal(); + } + + @Override + public SupportLevel getCredentialAcquireSupport(Class credentialType, String algorithmName, final AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException { + if (credentials.contains(credentialType, algorithmName, parameterSpec)) { + if (log.isTraceEnabled()) { + log.tracef("getCredentialAcquireSupport credentialType='%s' with algorithmName='%' known for pincipal='%s'", credentialType.getName(), algorithmName, principal.getName()); + } + return credentials.getCredentialAcquireSupport(credentialType, algorithmName, parameterSpec); + } + Credential credential = identity.getCredential(credentialType, algorithmName, parameterSpec); + if (credential != null) { + if (log.isTraceEnabled()) { + log.tracef("getCredentialAcquireSupport Credential for credentialType='%s' with algorithmName='%' obtained from identity - caching for principal='%s'", + credentialType.getName(), algorithmName, principal.getName()); + } + credentials = credentials.withCredential(credential); + } + return credentials.getCredentialAcquireSupport(credentialType, algorithmName, parameterSpec); + } + + @Override + public C getCredential(Class credentialType) throws RealmUnavailableException { + if (credentials.contains(credentialType)) { + if (log.isTraceEnabled()) { + log.tracef("getCredential credentialType='%s' cached, returning cached credential for principal='%s'", credentialType.getName(), principal.getName()); + } + return credentials.getCredential(credentialType); + } + Credential credential = identity.getCredential(credentialType); + if (credential != null) { + if (log.isTraceEnabled()) { + log.tracef("getCredential credentialType='%s' obtained from identity - caching for principal='%s'", credentialType.getName(), principal.getName()); + } + credentials = credentials.withCredential(credential); + } + return credentials.getCredential(credentialType); + } + + @Override + public C getCredential(Class credentialType, String algorithmName) throws RealmUnavailableException { + if (credentials.contains(credentialType, algorithmName)) { + if (log.isTraceEnabled()) { + log.tracef("getCredential credentialType='%s' with algorithmName='%' cached, returning cached credential for principal='%s'", credentialType.getName(), algorithmName, principal.getName()); + } + return credentials.getCredential(credentialType, algorithmName); + } + Credential credential = identity.getCredential(credentialType, algorithmName); + if (credential != null) { + if (log.isTraceEnabled()) { + log.tracef("getCredential credentialType='%s' with algorithmName='%' obtained from identity - caching.", credentialType.getName(), algorithmName); + } + credentials = credentials.withCredential(credential); + } + return credentials.getCredential(credentialType, algorithmName); + } + + @Override + public C getCredential(final Class credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException { + if (credentials.contains(credentialType, algorithmName, parameterSpec)) { + if (log.isTraceEnabled()) { + log.tracef("getCredential credentialType='%s' with algorithmName='%' cached, returning cached credential for principal='%s'", credentialType.getName(), algorithmName, principal.getName()); + } + return credentials.getCredential(credentialType, algorithmName, parameterSpec); + } + Credential credential = identity.getCredential(credentialType, algorithmName, parameterSpec); + if (credential != null) { + if (log.isTraceEnabled()) { + log.tracef("getCredential credentialType='%s' with algorithmName='%' obtained from identity - caching for principal='%s'", credentialType.getName(), algorithmName, principal.getName()); + } + credentials = credentials.withCredential(credential); + } + return credentials.getCredential(credentialType, algorithmName, parameterSpec); + } + + @Override + public void updateCredential(Credential credential) throws RealmUnavailableException { + log.tracef("updateCredential For principal='%s'", principal); + try { + identity.updateCredential(credential); + } finally { + removeFromCache(identity.getRealmIdentityPrincipal()); + } + } + + @Override + public SupportLevel getEvidenceVerifySupport(Class evidenceType, String algorithmName) throws RealmUnavailableException { + if (PasswordGuessEvidence.class.isAssignableFrom(evidenceType)) { + if (credentials.canVerify(evidenceType, algorithmName)) { + if (log.isTraceEnabled()) { + log.tracef("getEvidenceVerifySupport evidenceType='%s' with algorithmName='%' can verify from cache for principal='%s'", evidenceType.getName(), algorithmName, principal.getName()); + } + return SupportLevel.SUPPORTED; + } + Credential credential = identity.getCredential(PasswordCredential.class); + if (credential != null) { + if (log.isTraceEnabled()) { + log.tracef("getEvidenceVerifySupport evidenceType='%s' with algorithmName='%' credential obtained from identity and cached for principal='%s'", + evidenceType.getName(), algorithmName, principal.getName()); + } + credentials = credentials.withCredential(credential); + if (credential.canVerify(evidenceType, algorithmName)) { + return SupportLevel.SUPPORTED; + } + } + } + if (log.isTraceEnabled()) { + log.tracef("getEvidenceVerifySupport evidenceType='%s' with algorithmName='%' falling back to direct support of identity for principal='%s'", + evidenceType.getName(), algorithmName, principal.getName()); + } + return identity.getEvidenceVerifySupport(evidenceType, algorithmName); + } + + @Override + public boolean verifyEvidence(Evidence evidence) throws RealmUnavailableException { + if (evidence instanceof PasswordGuessEvidence) { + if (credentials.canVerify(evidence)) { + log.tracef("verifyEvidence For principal='%s' using cached credential", principal); + return credentials.verify(providerSupplier, evidence); + } + Credential credential = identity.getCredential(PasswordCredential.class); + if (credential != null) { + log.tracef("verifyEvidence Credential obtained from identity and cached for principal='%s'", principal); + credentials = credentials.withCredential(credential); + if (credential.canVerify(evidence)) { + return credential.verify(providerSupplier, evidence); + } + } + char[] guess = ((PasswordGuessEvidence) evidence).getGuess(); + Password password = ClearPassword.createRaw(ClearPassword.ALGORITHM_CLEAR, guess); + log.tracef("verifyEvidence Falling back to direct support of identity for principal='%s'", principal); + if (identity.verifyEvidence(evidence)) { + credentials = credentials.withCredential(new PasswordCredential(password)); + return true; + } + return false; + } + return identity.verifyEvidence(evidence); + } + + @Override + public boolean exists() throws RealmUnavailableException { + return true; // non-existing identities will not be wrapped + } + + @Override + public AuthorizationIdentity getAuthorizationIdentity() throws RealmUnavailableException { + if (authorizationIdentity == null) { + log.tracef("getAuthorizationIdentity Caching AuthorizationIdentity for principal='%s'", principal); + authorizationIdentity = identity.getAuthorizationIdentity(); + } + return authorizationIdentity; + } + + @Override + public Attributes getAttributes() throws RealmUnavailableException { + if (attributes == null) { + log.tracef("getAttributes Caching Attributes for principal='%s'", principal); + attributes = identity.getAttributes(); + } + return attributes; + } + + @Override + public void dispose() { + identity.dispose(); + } + }; + + log.tracef("Created wrapper RealmIdentity for '%s' and placing in cache.", principal); + cache.put(principal, cachedIdentity); + + return cachedIdentity; + } + + @Override + public SupportLevel getCredentialAcquireSupport(Class credentialType, String algorithmName, final AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException { + return getCacheableRealm().getCredentialAcquireSupport(credentialType, algorithmName, parameterSpec); + } + + @Override + public SupportLevel getEvidenceVerifySupport(Class evidenceType, String algorithmName) throws RealmUnavailableException { + return getCacheableRealm().getEvidenceVerifySupport(evidenceType, algorithmName); + } + + @Override + public void handleRealmEvent(RealmEvent event) { + getCacheableRealm().handleRealmEvent(event); + } + + /** + * Removes a {@link RealmIdentity} referenced by the specified {@link Principal} from the cache. + * + * @param principal the {@link Principal} that references a previously cached realm identity + */ + public void removeFromCache(Principal principal) { + cache.remove(principal); + } + + /** + * Removes all cached identities from the cache. + */ + public void removeAllFromCache() { + cache.clear(); + } + + /** + * Gets wrapped backing realm. + * + * @return the wrapped backing realm + */ + protected SecurityRealm getCacheableRealm() { + return realm; + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/realm/DistributedSecurityRealm.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/realm/DistributedSecurityRealm.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/realm/DistributedSecurityRealm.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,293 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2020 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.auth.realm; + +import org.wildfly.common.Assert; +import org.wildfly.security.auth.SupportLevel; +import org.wildfly.security.auth.server.RealmIdentity; +import org.wildfly.security.auth.server.RealmUnavailableException; +import org.wildfly.security.auth.server.SecurityRealm; +import org.wildfly.security.authz.AuthorizationIdentity; +import org.wildfly.security.credential.Credential; +import org.wildfly.security.evidence.AlgorithmEvidence; +import org.wildfly.security.evidence.Evidence; + +import java.security.Principal; +import java.security.spec.AlgorithmParameterSpec; +import java.util.function.Consumer; + +/** + * A realm for authentication and authorization of identities distributed between multiple realms. + * + * @author Martin Mazanek + */ +public class DistributedSecurityRealm implements SecurityRealm { + private final boolean ignoreUnavailableRealms; + private final SecurityRealm[] securityRealms; + private final Consumer unavailableRealmCallback; + + public DistributedSecurityRealm(final SecurityRealm... securityRealms) { + this(false, null, securityRealms); + } + + /** + * Construct a new instance. + * + * @param ignoreUnavailableRealms allow to specify that the search should continue on to the next realm if a realm happens to be unavailable + * @param unavailableRealmCallback a callback that can be used to emit realm unavailability, can be {@code null} + * @param securityRealms references to one or more security realms for authentication and authorization + */ + public DistributedSecurityRealm( final boolean ignoreUnavailableRealms, final Consumer unavailableRealmCallback, final SecurityRealm... securityRealms) { + Assert.checkNotNullParam("securityRealms", securityRealms); + this.ignoreUnavailableRealms = ignoreUnavailableRealms; + this.unavailableRealmCallback = unavailableRealmCallback; + this.securityRealms = securityRealms; + } + + @Override + public RealmIdentity getRealmIdentity(final Evidence evidence) throws RealmUnavailableException { + return new EvidenceDistributedIdentity(evidence); + } + + @Override + public RealmIdentity getRealmIdentity(final Principal principal) throws RealmUnavailableException { + return new PrincipalDistributedIdentity(principal); + } + + @Override + public SupportLevel getCredentialAcquireSupport(final Class credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException { + SupportLevel max = SupportLevel.UNSUPPORTED; + for (SecurityRealm r : securityRealms) { + max = SupportLevel.max(max, r.getCredentialAcquireSupport(credentialType, algorithmName, parameterSpec)); + } + return max; + } + + @Override + public SupportLevel getEvidenceVerifySupport(final Class evidenceType, final String algorithmName) throws RealmUnavailableException { + SupportLevel max = SupportLevel.UNSUPPORTED; + for (SecurityRealm r : securityRealms) { + max = SupportLevel.max(max, r.getEvidenceVerifySupport(evidenceType, algorithmName)); + } + return max; + } + + final class EvidenceDistributedIdentity implements RealmIdentity { + private final Evidence evidence; + private final String evidenceAlgorithm; + private RealmIdentity currentIdentity = RealmIdentity.NON_EXISTENT; + private int nextRealm = 0; + + private EvidenceDistributedIdentity(Evidence evidence) throws RealmUnavailableException { + this.evidence = evidence; + if (evidence instanceof AlgorithmEvidence) { + evidenceAlgorithm = ((AlgorithmEvidence) evidence).getAlgorithm(); + } else { + evidenceAlgorithm = null; + } + nextIdentity(); + } + + private boolean nextIdentity() throws RealmUnavailableException { + currentIdentity.dispose(); + if (nextRealm >= securityRealms.length) { + currentIdentity = RealmIdentity.NON_EXISTENT; + return false; + } + if (securityRealms[nextRealm].getEvidenceVerifySupport(evidence.getClass(), evidenceAlgorithm).mayBeSupported()) { + currentIdentity = securityRealms[nextRealm].getRealmIdentity(evidence); + nextRealm++; + if (currentIdentity.getEvidenceVerifySupport(evidence.getClass(), evidenceAlgorithm).isNotSupported()) { + return nextIdentity(); + } + } else { + nextRealm++; + return nextIdentity(); + } + return true; + } + + @Override + public Principal getRealmIdentityPrincipal() { + return currentIdentity.getRealmIdentityPrincipal(); + } + + @Override + public SupportLevel getCredentialAcquireSupport(final Class credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException { + // Identity created from evidence will be verified using the evidence + return SupportLevel.UNSUPPORTED; + } + + @Override + public SupportLevel getEvidenceVerifySupport(final Class evidenceType, final String algorithmName) throws RealmUnavailableException { + //as server verifies evidence with same evidence used for creating realm identity, we dont have to look into remaining realms for support (currentIdentity will always support evidence verification, unless none of the possible does) + return currentIdentity.getEvidenceVerifySupport(evidenceType, algorithmName); + } + + @Override + public C getCredential(final Class credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException { + return null; + } + + @Override + public C getCredential(final Class credentialType, final String algorithmName) throws RealmUnavailableException { + return null; + } + + @Override + public C getCredential(final Class credentialType) throws RealmUnavailableException { + return null; + } + + @Override + public boolean verifyEvidence(final Evidence evidence) throws RealmUnavailableException { + do { + if (currentIdentity.verifyEvidence(evidence)) { + return true; + } + } while (nextIdentity()); + return false; + } + + @Override + public boolean exists() throws RealmUnavailableException { + return currentIdentity.exists(); + } + + @Override + public AuthorizationIdentity getAuthorizationIdentity() throws RealmUnavailableException { + return currentIdentity.getAuthorizationIdentity(); + } + + @Override + public void dispose() { + currentIdentity.dispose(); + } + } + + final class PrincipalDistributedIdentity implements RealmIdentity { + + private final Principal principal; + private RealmIdentity currentIdentity = RealmIdentity.NON_EXISTENT; + private int nextRealm = 0; + + PrincipalDistributedIdentity(Principal principal) throws RealmUnavailableException { + this.principal = principal; + nextIdentity(); + } + + private boolean nextIdentity() throws RealmUnavailableException { + currentIdentity.dispose(); + if (nextRealm >= securityRealms.length) { + currentIdentity = RealmIdentity.NON_EXISTENT; + return false; + } + + boolean doesIdentityExist = false; + try { + currentIdentity = securityRealms[nextRealm].getRealmIdentity(principal); + doesIdentityExist = currentIdentity.exists(); + } catch (RealmUnavailableException e) { + if (!ignoreUnavailableRealms) { + throw e; + } + ElytronMessages.log.realmIsNotAvailable(e); + if (unavailableRealmCallback != null) { + unavailableRealmCallback.accept(nextRealm); + } + } + nextRealm++; + if (!doesIdentityExist) { + return nextIdentity(); + } + return true; + } + + @Override + public Principal getRealmIdentityPrincipal() { + return currentIdentity.getRealmIdentityPrincipal(); + } + + @Override + public SupportLevel getCredentialAcquireSupport(final Class credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException { + return currentIdentity.getCredentialAcquireSupport(credentialType, algorithmName, parameterSpec); + } + + @Override + public SupportLevel getEvidenceVerifySupport(final Class evidenceType, final String algorithmName) throws RealmUnavailableException { + return currentIdentity.getEvidenceVerifySupport(evidenceType, algorithmName); + } + + @Override + public C getCredential(final Class credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException { + do { + C credential = currentIdentity.getCredential(credentialType, algorithmName, parameterSpec); + if (credential != null) { + return credential; + } + } while (nextIdentity()); + + return null; + } + + @Override + public C getCredential(final Class credentialType, final String algorithmName) throws RealmUnavailableException { + do { + C credential = currentIdentity.getCredential(credentialType, algorithmName); + if (credential != null) { + return credential; + } + } while (nextIdentity()); + + return null; + } + + @Override + public C getCredential(final Class credentialType) throws RealmUnavailableException { + do { + C credential = currentIdentity.getCredential(credentialType); + if (credential != null) { + return credential; + } + } while (nextIdentity()); + + return null; + } + + @Override + public boolean verifyEvidence(final Evidence evidence) throws RealmUnavailableException { + return currentIdentity.verifyEvidence(evidence); + } + + @Override + public boolean exists() throws RealmUnavailableException { + return currentIdentity.exists(); + } + + @Override + public AuthorizationIdentity getAuthorizationIdentity() throws RealmUnavailableException { + return currentIdentity.getAuthorizationIdentity(); + } + + @Override + public void dispose() { + currentIdentity.dispose(); + } + } + +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/realm/ElytronMessages.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/realm/ElytronMessages.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/realm/ElytronMessages.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,174 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2014 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.auth.realm; + +import java.io.IOException; +import java.nio.file.Path; +import java.security.KeyStoreException; +import java.security.Principal; +import java.util.NoSuchElementException; + +import org.jboss.logging.BasicLogger; +import org.jboss.logging.Logger; +import org.jboss.logging.annotations.Cause; +import org.jboss.logging.annotations.LogMessage; +import org.jboss.logging.annotations.Message; +import org.jboss.logging.annotations.MessageLogger; +import org.jboss.logging.annotations.Param; +import org.jboss.logging.annotations.ValidIdRange; +import org.jboss.logging.annotations.ValidIdRanges; +import org.wildfly.security.auth.server.RealmUnavailableException; +import org.wildfly.security.auth.server.SecurityRealm; + +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.UnsupportedCallbackException; + +/** + * Log messages and exceptions for Elytron. + * + * @author David M. Lloyd + * @author Darran Lofthouse + */ +@MessageLogger(projectCode = "ELY", length = 5) +@ValidIdRanges({ + @ValidIdRange(min = 1006, max = 1082), + @ValidIdRange(min = 1138, max = 1154), + @ValidIdRange(min = 11005, max = 11005), + @ValidIdRange(min = 13000, max = 13999) +}) +interface ElytronMessages extends BasicLogger { + ElytronMessages log = Logger.getMessageLogger(ElytronMessages.class, "org.wildfly.security"); + + @Message(id = 1006, value = "No realm name found in users property file - non-plain-text users file must contain \"#$REALM_NAME=RealmName$\" line") + RealmUnavailableException noRealmFoundInProperties(); + + @LogMessage(level = Logger.Level.DEBUG) + @Message(id = 1007, value = "JAAS authentication failed for principal %s") + void debugInfoJaasAuthenticationFailure(Principal principal, @Cause Throwable cause); + + @Message(id = 1008, value = "Failed to create login context") + RealmUnavailableException failedToCreateLoginContext(@Cause Throwable cause); + + @Message(id = 1009, value = "Failed to instantiate custom CallbackHandler") + RealmUnavailableException failedToInstantiateCustomHandler(@Cause Throwable cause); + + @Message(id = 1012, value = "Filesystem-backed realm unexpectedly failed to open path \"%s\" for identity name \"%s\"") + RealmUnavailableException fileSystemRealmFailedToOpen(Path path, String finalName, @Cause IOException cause); + + @Message(id = 1013, value = "Filesystem-backed realm unexpectedly failed to read path \"%s\" for identity name \"%s\"") + RealmUnavailableException fileSystemRealmFailedToRead(Path path, String finalName, @Cause Exception cause); + + @Message(id = 1015, value = "Filesystem-backed realm encountered invalid file content in path \"%s\" line %d for identity name \"%s\"") + RealmUnavailableException fileSystemRealmInvalidContent(Path path, int lineNumber, String name); + + @Message(id = 1016, value = "Filesystem-backed realm encountered missing required attribute \"%s\" in path \"%s\" line %d for identity name \"%s\"") + RealmUnavailableException fileSystemRealmMissingAttribute(String attribute, Path path, int lineNumber, String name); + + @Message(id = 1017, value = "Filesystem-backed realm encountered invalid password format \"%s\" in path \"%s\" line %d for identity name \"%s\"") + RealmUnavailableException fileSystemRealmInvalidPasswordFormat(String format, Path path, int lineNumber, String name); + + @Message(id = 1018, value = "Filesystem-backed realm encountered invalid password algorithm \"%s\" in path \"%s\" line %d for identity name \"%s\"") + RealmUnavailableException fileSystemRealmInvalidPasswordAlgorithm(String algorithm, Path path, int lineNumber, String name); + + @Message(id = 1020, value = "Filesystem-backed realm failed to update identity \"%s\"") + RealmUnavailableException fileSystemUpdatedFailed(String name, @Cause Throwable cause); + + @Message(id = 1021, value = "Filesystem-backed realm failed to delete identity \"%s\"") + RealmUnavailableException fileSystemRealmDeleteFailed(String name, @Cause IOException e); + + @Message(id = 1022, value = "Filesystem-backed realm failed to find identity \"%s\"") + RealmUnavailableException fileSystemRealmNotFound(String name); + + @Message(id = 1023, value = "Filesystem-backed realm failed to write to file \"%s\" for identity \"%s\"") + RealmUnavailableException fileSystemRealmFailedToWrite(Path tempPath, String name, @Cause Exception e); + + @Message(id = 1024, value = "Filesystem-backed realm cannot create duplicate identity for identity \"%s\"") + RealmUnavailableException fileSystemRealmAlreadyExists(String name, @Cause Throwable e); + + @Message(id = 1025, value = "Filesystem-backed realm encountered invalid certificate format \"%s\" in path \"%s\" line %d for identity name \"%s\"") + RealmUnavailableException fileSystemRealmCertificateReadError(String format, Path path, int lineNumber, String name); + + @Message(id = 1026, value = "Filesystem-backed realm encountered invalid key format \"%s\" in path \"%s\" line %d for identity name \"%s\"") + RealmUnavailableException fileSystemRealmUnsupportedKeyFormat(String format, Path path, int lineNumber, String name); + + @Message(id = 1027, value = "Filesystem-backed realm encountered invalid key algorithm for format \"%s\" in path \"%s\" line %d for identity name \"%s\"") + RealmUnavailableException fileSystemRealmUnsupportedKeyAlgorithm(String format, Path path, int lineNumber, String name, @Cause Exception e); + + @Message(id = 1064, value = "Invalid identity name") + IllegalArgumentException invalidName(); + + @Message(id = 1081, value = "Filesystem-backed realm encountered invalid OTP definition in path \"%s\" line %d for identity name \"%s\"") + RealmUnavailableException fileSystemRealmInvalidOtpDefinition(Path path, int lineNumber, String name, @Cause Throwable cause); + + @Message(id = 1082, value = "Filesystem-backed realm encountered invalid OTP algorithm \"%s\" in path \"%s\" line %d for identity name \"%s\"") + RealmUnavailableException fileSystemRealmInvalidOtpAlgorithm(String algorithm, Path path, int lineNumber, String name, @Cause Throwable cause); + + @Message(id = 1138, value = "Decoding hashed password from users property file failed - should not be set as plain-text property file?") + RealmUnavailableException decodingHashedPasswordFromPropertiesRealmFailed(@Cause Exception e); + + @Message(id = 1145, value = "Security realm [%s] must implement [%s]") + IllegalArgumentException realmCacheUnexpectedType(SecurityRealm realm, Class expectedType); + + @Message(id = 13000, value = "Authorization principal cannot be null after transformation") + IllegalStateException transformedPrincipalCannotBeNull(); + + @Message(id = 1154, value = "Failed to read key store") + RealmUnavailableException failedToReadKeyStore(@Cause KeyStoreException e); + + @Message(id = 11005, value = "Invalid unicode endoding, offending sequence: %s.") + IOException invalidUnicodeSequence(String s, @Cause NoSuchElementException nsee); + + @LogMessage(level = Logger.Level.WARN) + @Message(id = 13001, value = "Realm is failing over.") + void realmFailover(@Cause RealmUnavailableException rue); + + @Message(id = 13002, value = "%s does not handle a callback of type %s") + UnsupportedCallbackException unableToHandleCallback(@Param Callback callback, String callbackHandler, String callbackType); + + @Message(id = 13003, value = "Failed to load JAAS configuration file.") + RealmUnavailableException failedToLoadJaasConfigFile(); + + @LogMessage(level = Logger.Level.DEBUG) + @Message(id = 13004, value = "JAAS logout failed for principal %s") + void debugInfoJaasLogoutFailure(Principal principal, @Cause Throwable cause); + + @Message(id = 13005, value = "Filesystem-backed realm unable to decrypt identity") + RealmUnavailableException fileSystemRealmDecryptionFailed(@Cause Throwable cause); + + @Message(id = 13006, value = "Filesystem-backed realm unable to encrypt identity") + RealmUnavailableException fileSystemRealmEncryptionFailed(@Cause Throwable cause); + + @Message(id = 13007, value = "Signature for the following identity is invalid: %s.") + IntegrityException invalidIdentitySignature(String s); + + @Message(id = 13008, value = "Unable to create a signature for the file: %s.") + RealmUnavailableException unableToGenerateSignature(String s); + + @Message(id = 13009, value = "Unable to locate the signature element for the file: %s") + RealmUnavailableException cannotFindSignature(String s); + + @Message(id = 13010, value = "Both PrivateKey and PublicKey must be defined for realm at: %s") + IllegalArgumentException invalidKeyPairArgument(String s); + + @Message(id = 13011, value = "Integrity has not been enabled for the realm at: %s") + IllegalArgumentException integrityNotEnabled(String s); + + @LogMessage(level = Logger.Level.WARN) + @Message(id = 13012, value = "A realm within the distributed realm is unavailable. This realm will be ignored.") + void realmIsNotAvailable(@Cause Exception e); +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/realm/ElytronMessages_$logger.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/realm/ElytronMessages_$logger.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/realm/ElytronMessages_$logger.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,383 @@ +package org.wildfly.security.auth.realm; + +import java.util.Locale; +import java.nio.file.Path; +import java.lang.IllegalStateException; +import java.io.Serializable; +import javax.annotation.Generated; +import org.jboss.logging.DelegatingBasicLogger; +import org.wildfly.security.auth.server.SecurityRealm; +import org.wildfly.security.auth.realm.IntegrityException; +import org.wildfly.security.auth.server.RealmUnavailableException; +import java.lang.String; +import java.io.IOException; +import java.security.KeyStoreException; +import org.jboss.logging.Logger; +import java.lang.Exception; +import javax.security.auth.callback.Callback; +import org.jboss.logging.BasicLogger; +import java.lang.Throwable; +import java.lang.Class; +import javax.security.auth.callback.UnsupportedCallbackException; +import java.util.Arrays; +import java.security.Principal; +import java.lang.IllegalArgumentException; +import java.util.NoSuchElementException; + + +import static org.jboss.logging.Logger.Level.DEBUG; +import static org.jboss.logging.Logger.Level.WARN; + +/** + * Warning this class consists of generated code. + */ +@Generated(value = "org.jboss.logging.processor.generator.model.MessageLoggerImplementor", date = "2024-01-15T21:46:10+0100") +public class ElytronMessages_$logger extends DelegatingBasicLogger implements ElytronMessages, BasicLogger, Serializable { + private static final long serialVersionUID = 1L; + private static final String FQCN = ElytronMessages_$logger.class.getName(); + public ElytronMessages_$logger(final Logger log) { + super(log); + } + private static final Locale LOCALE = Locale.ROOT; + protected Locale getLoggingLocale() { + return LOCALE; + } + protected String noRealmFoundInProperties$str() { + return "ELY01006: No realm name found in users property file - non-plain-text users file must contain \"#$REALM_NAME=RealmName$\" line"; + } + @Override + public final RealmUnavailableException noRealmFoundInProperties() { + final RealmUnavailableException result = new RealmUnavailableException(String.format(getLoggingLocale(), noRealmFoundInProperties$str())); + _copyStackTraceMinusOne(result); + return result; + } + private static void _copyStackTraceMinusOne(final Throwable e) { + final StackTraceElement[] st = e.getStackTrace(); + e.setStackTrace(Arrays.copyOfRange(st, 1, st.length)); + } + @Override + public final void debugInfoJaasAuthenticationFailure(final Principal principal, final Throwable cause) { + super.log.logf(FQCN, DEBUG, cause, debugInfoJaasAuthenticationFailure$str(), principal); + } + protected String debugInfoJaasAuthenticationFailure$str() { + return "ELY01007: JAAS authentication failed for principal %s"; + } + protected String failedToCreateLoginContext$str() { + return "ELY01008: Failed to create login context"; + } + @Override + public final RealmUnavailableException failedToCreateLoginContext(final Throwable cause) { + final RealmUnavailableException result = new RealmUnavailableException(String.format(getLoggingLocale(), failedToCreateLoginContext$str()), cause); + _copyStackTraceMinusOne(result); + return result; + } + protected String failedToInstantiateCustomHandler$str() { + return "ELY01009: Failed to instantiate custom CallbackHandler"; + } + @Override + public final RealmUnavailableException failedToInstantiateCustomHandler(final Throwable cause) { + final RealmUnavailableException result = new RealmUnavailableException(String.format(getLoggingLocale(), failedToInstantiateCustomHandler$str()), cause); + _copyStackTraceMinusOne(result); + return result; + } + protected String fileSystemRealmFailedToOpen$str() { + return "ELY01012: Filesystem-backed realm unexpectedly failed to open path \"%s\" for identity name \"%s\""; + } + @Override + public final RealmUnavailableException fileSystemRealmFailedToOpen(final Path path, final String finalName, final IOException cause) { + final RealmUnavailableException result = new RealmUnavailableException(String.format(getLoggingLocale(), fileSystemRealmFailedToOpen$str(), path, finalName), cause); + _copyStackTraceMinusOne(result); + return result; + } + protected String fileSystemRealmFailedToRead$str() { + return "ELY01013: Filesystem-backed realm unexpectedly failed to read path \"%s\" for identity name \"%s\""; + } + @Override + public final RealmUnavailableException fileSystemRealmFailedToRead(final Path path, final String finalName, final Exception cause) { + final RealmUnavailableException result = new RealmUnavailableException(String.format(getLoggingLocale(), fileSystemRealmFailedToRead$str(), path, finalName), cause); + _copyStackTraceMinusOne(result); + return result; + } + protected String fileSystemRealmInvalidContent$str() { + return "ELY01015: Filesystem-backed realm encountered invalid file content in path \"%s\" line %d for identity name \"%s\""; + } + @Override + public final RealmUnavailableException fileSystemRealmInvalidContent(final Path path, final int lineNumber, final String name) { + final RealmUnavailableException result = new RealmUnavailableException(String.format(getLoggingLocale(), fileSystemRealmInvalidContent$str(), path, lineNumber, name)); + _copyStackTraceMinusOne(result); + return result; + } + protected String fileSystemRealmMissingAttribute$str() { + return "ELY01016: Filesystem-backed realm encountered missing required attribute \"%s\" in path \"%s\" line %d for identity name \"%s\""; + } + @Override + public final RealmUnavailableException fileSystemRealmMissingAttribute(final String attribute, final Path path, final int lineNumber, final String name) { + final RealmUnavailableException result = new RealmUnavailableException(String.format(getLoggingLocale(), fileSystemRealmMissingAttribute$str(), attribute, path, lineNumber, name)); + _copyStackTraceMinusOne(result); + return result; + } + protected String fileSystemRealmInvalidPasswordFormat$str() { + return "ELY01017: Filesystem-backed realm encountered invalid password format \"%s\" in path \"%s\" line %d for identity name \"%s\""; + } + @Override + public final RealmUnavailableException fileSystemRealmInvalidPasswordFormat(final String format, final Path path, final int lineNumber, final String name) { + final RealmUnavailableException result = new RealmUnavailableException(String.format(getLoggingLocale(), fileSystemRealmInvalidPasswordFormat$str(), format, path, lineNumber, name)); + _copyStackTraceMinusOne(result); + return result; + } + protected String fileSystemRealmInvalidPasswordAlgorithm$str() { + return "ELY01018: Filesystem-backed realm encountered invalid password algorithm \"%s\" in path \"%s\" line %d for identity name \"%s\""; + } + @Override + public final RealmUnavailableException fileSystemRealmInvalidPasswordAlgorithm(final String algorithm, final Path path, final int lineNumber, final String name) { + final RealmUnavailableException result = new RealmUnavailableException(String.format(getLoggingLocale(), fileSystemRealmInvalidPasswordAlgorithm$str(), algorithm, path, lineNumber, name)); + _copyStackTraceMinusOne(result); + return result; + } + protected String fileSystemUpdatedFailed$str() { + return "ELY01020: Filesystem-backed realm failed to update identity \"%s\""; + } + @Override + public final RealmUnavailableException fileSystemUpdatedFailed(final String name, final Throwable cause) { + final RealmUnavailableException result = new RealmUnavailableException(String.format(getLoggingLocale(), fileSystemUpdatedFailed$str(), name), cause); + _copyStackTraceMinusOne(result); + return result; + } + protected String fileSystemRealmDeleteFailed$str() { + return "ELY01021: Filesystem-backed realm failed to delete identity \"%s\""; + } + @Override + public final RealmUnavailableException fileSystemRealmDeleteFailed(final String name, final IOException e) { + final RealmUnavailableException result = new RealmUnavailableException(String.format(getLoggingLocale(), fileSystemRealmDeleteFailed$str(), name), e); + _copyStackTraceMinusOne(result); + return result; + } + protected String fileSystemRealmNotFound$str() { + return "ELY01022: Filesystem-backed realm failed to find identity \"%s\""; + } + @Override + public final RealmUnavailableException fileSystemRealmNotFound(final String name) { + final RealmUnavailableException result = new RealmUnavailableException(String.format(getLoggingLocale(), fileSystemRealmNotFound$str(), name)); + _copyStackTraceMinusOne(result); + return result; + } + protected String fileSystemRealmFailedToWrite$str() { + return "ELY01023: Filesystem-backed realm failed to write to file \"%s\" for identity \"%s\""; + } + @Override + public final RealmUnavailableException fileSystemRealmFailedToWrite(final Path tempPath, final String name, final Exception e) { + final RealmUnavailableException result = new RealmUnavailableException(String.format(getLoggingLocale(), fileSystemRealmFailedToWrite$str(), tempPath, name), e); + _copyStackTraceMinusOne(result); + return result; + } + protected String fileSystemRealmAlreadyExists$str() { + return "ELY01024: Filesystem-backed realm cannot create duplicate identity for identity \"%s\""; + } + @Override + public final RealmUnavailableException fileSystemRealmAlreadyExists(final String name, final Throwable e) { + final RealmUnavailableException result = new RealmUnavailableException(String.format(getLoggingLocale(), fileSystemRealmAlreadyExists$str(), name), e); + _copyStackTraceMinusOne(result); + return result; + } + protected String fileSystemRealmCertificateReadError$str() { + return "ELY01025: Filesystem-backed realm encountered invalid certificate format \"%s\" in path \"%s\" line %d for identity name \"%s\""; + } + @Override + public final RealmUnavailableException fileSystemRealmCertificateReadError(final String format, final Path path, final int lineNumber, final String name) { + final RealmUnavailableException result = new RealmUnavailableException(String.format(getLoggingLocale(), fileSystemRealmCertificateReadError$str(), format, path, lineNumber, name)); + _copyStackTraceMinusOne(result); + return result; + } + protected String fileSystemRealmUnsupportedKeyFormat$str() { + return "ELY01026: Filesystem-backed realm encountered invalid key format \"%s\" in path \"%s\" line %d for identity name \"%s\""; + } + @Override + public final RealmUnavailableException fileSystemRealmUnsupportedKeyFormat(final String format, final Path path, final int lineNumber, final String name) { + final RealmUnavailableException result = new RealmUnavailableException(String.format(getLoggingLocale(), fileSystemRealmUnsupportedKeyFormat$str(), format, path, lineNumber, name)); + _copyStackTraceMinusOne(result); + return result; + } + protected String fileSystemRealmUnsupportedKeyAlgorithm$str() { + return "ELY01027: Filesystem-backed realm encountered invalid key algorithm for format \"%s\" in path \"%s\" line %d for identity name \"%s\""; + } + @Override + public final RealmUnavailableException fileSystemRealmUnsupportedKeyAlgorithm(final String format, final Path path, final int lineNumber, final String name, final Exception e) { + final RealmUnavailableException result = new RealmUnavailableException(String.format(getLoggingLocale(), fileSystemRealmUnsupportedKeyAlgorithm$str(), format, path, lineNumber, name), e); + _copyStackTraceMinusOne(result); + return result; + } + protected String invalidName$str() { + return "ELY01064: Invalid identity name"; + } + @Override + public final IllegalArgumentException invalidName() { + final IllegalArgumentException result = new IllegalArgumentException(String.format(getLoggingLocale(), invalidName$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String fileSystemRealmInvalidOtpDefinition$str() { + return "ELY01081: Filesystem-backed realm encountered invalid OTP definition in path \"%s\" line %d for identity name \"%s\""; + } + @Override + public final RealmUnavailableException fileSystemRealmInvalidOtpDefinition(final Path path, final int lineNumber, final String name, final Throwable cause) { + final RealmUnavailableException result = new RealmUnavailableException(String.format(getLoggingLocale(), fileSystemRealmInvalidOtpDefinition$str(), path, lineNumber, name), cause); + _copyStackTraceMinusOne(result); + return result; + } + protected String fileSystemRealmInvalidOtpAlgorithm$str() { + return "ELY01082: Filesystem-backed realm encountered invalid OTP algorithm \"%s\" in path \"%s\" line %d for identity name \"%s\""; + } + @Override + public final RealmUnavailableException fileSystemRealmInvalidOtpAlgorithm(final String algorithm, final Path path, final int lineNumber, final String name, final Throwable cause) { + final RealmUnavailableException result = new RealmUnavailableException(String.format(getLoggingLocale(), fileSystemRealmInvalidOtpAlgorithm$str(), algorithm, path, lineNumber, name), cause); + _copyStackTraceMinusOne(result); + return result; + } + protected String decodingHashedPasswordFromPropertiesRealmFailed$str() { + return "ELY01138: Decoding hashed password from users property file failed - should not be set as plain-text property file?"; + } + @Override + public final RealmUnavailableException decodingHashedPasswordFromPropertiesRealmFailed(final Exception e) { + final RealmUnavailableException result = new RealmUnavailableException(String.format(getLoggingLocale(), decodingHashedPasswordFromPropertiesRealmFailed$str()), e); + _copyStackTraceMinusOne(result); + return result; + } + protected String realmCacheUnexpectedType$str() { + return "ELY01145: Security realm [%s] must implement [%s]"; + } + @Override + public final IllegalArgumentException realmCacheUnexpectedType(final SecurityRealm realm, final Class expectedType) { + final IllegalArgumentException result = new IllegalArgumentException(String.format(getLoggingLocale(), realmCacheUnexpectedType$str(), realm, expectedType)); + _copyStackTraceMinusOne(result); + return result; + } + protected String transformedPrincipalCannotBeNull$str() { + return "ELY13000: Authorization principal cannot be null after transformation"; + } + @Override + public final IllegalStateException transformedPrincipalCannotBeNull() { + final IllegalStateException result = new IllegalStateException(String.format(getLoggingLocale(), transformedPrincipalCannotBeNull$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String failedToReadKeyStore$str() { + return "ELY01154: Failed to read key store"; + } + @Override + public final RealmUnavailableException failedToReadKeyStore(final KeyStoreException e) { + final RealmUnavailableException result = new RealmUnavailableException(String.format(getLoggingLocale(), failedToReadKeyStore$str()), e); + _copyStackTraceMinusOne(result); + return result; + } + protected String invalidUnicodeSequence$str() { + return "ELY11005: Invalid unicode endoding, offending sequence: %s."; + } + @Override + public final IOException invalidUnicodeSequence(final String s, final NoSuchElementException nsee) { + final IOException result = new IOException(String.format(getLoggingLocale(), invalidUnicodeSequence$str(), s), nsee); + _copyStackTraceMinusOne(result); + return result; + } + @Override + public final void realmFailover(final RealmUnavailableException rue) { + super.log.logf(FQCN, WARN, rue, realmFailover$str()); + } + protected String realmFailover$str() { + return "ELY13001: Realm is failing over."; + } + protected String unableToHandleCallback$str() { + return "ELY13002: %s does not handle a callback of type %s"; + } + @Override + public final UnsupportedCallbackException unableToHandleCallback(final Callback callback, final String callbackHandler, final String callbackType) { + final UnsupportedCallbackException result = new UnsupportedCallbackException(callback, String.format(getLoggingLocale(), unableToHandleCallback$str(), callbackHandler, callbackType)); + _copyStackTraceMinusOne(result); + return result; + } + protected String failedToLoadJaasConfigFile$str() { + return "ELY13003: Failed to load JAAS configuration file."; + } + @Override + public final RealmUnavailableException failedToLoadJaasConfigFile() { + final RealmUnavailableException result = new RealmUnavailableException(String.format(getLoggingLocale(), failedToLoadJaasConfigFile$str())); + _copyStackTraceMinusOne(result); + return result; + } + @Override + public final void debugInfoJaasLogoutFailure(final Principal principal, final Throwable cause) { + super.log.logf(FQCN, DEBUG, cause, debugInfoJaasLogoutFailure$str(), principal); + } + protected String debugInfoJaasLogoutFailure$str() { + return "ELY13004: JAAS logout failed for principal %s"; + } + protected String fileSystemRealmDecryptionFailed$str() { + return "ELY13005: Filesystem-backed realm unable to decrypt identity"; + } + @Override + public final RealmUnavailableException fileSystemRealmDecryptionFailed(final Throwable cause) { + final RealmUnavailableException result = new RealmUnavailableException(String.format(getLoggingLocale(), fileSystemRealmDecryptionFailed$str()), cause); + _copyStackTraceMinusOne(result); + return result; + } + protected String fileSystemRealmEncryptionFailed$str() { + return "ELY13006: Filesystem-backed realm unable to encrypt identity"; + } + @Override + public final RealmUnavailableException fileSystemRealmEncryptionFailed(final Throwable cause) { + final RealmUnavailableException result = new RealmUnavailableException(String.format(getLoggingLocale(), fileSystemRealmEncryptionFailed$str()), cause); + _copyStackTraceMinusOne(result); + return result; + } + protected String invalidIdentitySignature$str() { + return "ELY13007: Signature for the following identity is invalid: %s."; + } + @Override + public final org.wildfly.security.auth.realm.IntegrityException invalidIdentitySignature(final String s) { + final org.wildfly.security.auth.realm.IntegrityException result = new org.wildfly.security.auth.realm.IntegrityException(String.format(getLoggingLocale(), invalidIdentitySignature$str(), s)); + _copyStackTraceMinusOne(result); + return result; + } + protected String unableToGenerateSignature$str() { + return "ELY13008: Unable to create a signature for the file: %s."; + } + @Override + public final RealmUnavailableException unableToGenerateSignature(final String s) { + final RealmUnavailableException result = new RealmUnavailableException(String.format(getLoggingLocale(), unableToGenerateSignature$str(), s)); + _copyStackTraceMinusOne(result); + return result; + } + protected String cannotFindSignature$str() { + return "ELY13009: Unable to locate the signature element for the file: %s"; + } + @Override + public final RealmUnavailableException cannotFindSignature(final String s) { + final RealmUnavailableException result = new RealmUnavailableException(String.format(getLoggingLocale(), cannotFindSignature$str(), s)); + _copyStackTraceMinusOne(result); + return result; + } + protected String invalidKeyPairArgument$str() { + return "ELY13010: Both PrivateKey and PublicKey must be defined for realm at: %s"; + } + @Override + public final IllegalArgumentException invalidKeyPairArgument(final String s) { + final IllegalArgumentException result = new IllegalArgumentException(String.format(getLoggingLocale(), invalidKeyPairArgument$str(), s)); + _copyStackTraceMinusOne(result); + return result; + } + protected String integrityNotEnabled$str() { + return "ELY13011: Integrity has not been enabled for the realm at: %s"; + } + @Override + public final IllegalArgumentException integrityNotEnabled(final String s) { + final IllegalArgumentException result = new IllegalArgumentException(String.format(getLoggingLocale(), integrityNotEnabled$str(), s)); + _copyStackTraceMinusOne(result); + return result; + } + @Override + public final void realmIsNotAvailable(final Exception e) { + super.log.logf(FQCN, WARN, e, realmIsNotAvailable$str()); + } + protected String realmIsNotAvailable$str() { + return "ELY13012: A realm within the distributed realm is unavailable. This realm will be ignored."; + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/realm/FailoverSecurityRealm.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/realm/FailoverSecurityRealm.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/realm/FailoverSecurityRealm.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,305 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2019 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.auth.realm; + +import static org.wildfly.security.auth.realm.ElytronMessages.log; + +import org.wildfly.common.Assert; +import org.wildfly.security.auth.SupportLevel; +import org.wildfly.security.auth.server.RealmIdentity; +import org.wildfly.security.auth.server.RealmUnavailableException; +import org.wildfly.security.auth.server.SecurityRealm; +import org.wildfly.security.authz.Attributes; +import org.wildfly.security.authz.AuthorizationIdentity; +import org.wildfly.security.credential.Credential; +import org.wildfly.security.evidence.Evidence; + +import java.security.Principal; +import java.security.spec.AlgorithmParameterSpec; +import java.util.function.Consumer; +import java.util.function.Function; + +/** + * A realm which wraps one realm and fails over to another in case the first is unavailable. + * + * @author Martin Mazanek + */ +public class FailoverSecurityRealm implements SecurityRealm { + protected final SecurityRealm delegateRealm; + protected final SecurityRealm failoverRealm; + protected final Consumer failoverCallback; + + /** + * Construct a new instance. + * + * @param delegateRealm the wrapped realm + * @param failoverRealm the realm to use in case delegateRealm is unavailable + * @param failoverCallback callback function that gets called in case delegateRealm is unavailable + */ + public FailoverSecurityRealm(final SecurityRealm delegateRealm, final SecurityRealm failoverRealm, final Consumer failoverCallback) { + Assert.checkNotNullParam("delegateRealm", delegateRealm); + Assert.checkNotNullParam("failoverRealm", failoverRealm); + + this.delegateRealm = delegateRealm; + this.failoverRealm = failoverRealm; + this.failoverCallback = failoverCallback; + } + + @Override + public RealmIdentity getRealmIdentity(final Evidence evidence) throws RealmUnavailableException { + try { + return createFailoverIdentity(delegateRealm.getRealmIdentity(evidence), evidence); + } catch (RealmUnavailableException e) { + log.realmFailover(e); + if (failoverCallback != null) { + failoverCallback.accept(e); + } + return failoverRealm.getRealmIdentity(evidence); + } + } + + @Override + public RealmIdentity getRealmIdentity(final Principal principal) throws RealmUnavailableException { + try { + return createFailoverIdentity(delegateRealm.getRealmIdentity(principal), principal); + } catch (RealmUnavailableException e) { + log.realmFailover(e); + if (failoverCallback != null) { + failoverCallback.accept(e); + } + return failoverRealm.getRealmIdentity(principal); + } + } + + @Override + public SupportLevel getCredentialAcquireSupport(final Class credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException { + try { + return delegateRealm.getCredentialAcquireSupport(credentialType, algorithmName, parameterSpec); + } catch (RealmUnavailableException rue) { + log.realmFailover(rue); + if (failoverCallback != null) { + failoverCallback.accept(rue); + } + return SupportLevel.POSSIBLY_SUPPORTED; + } + } + + @Override + public SupportLevel getEvidenceVerifySupport(final Class evidenceType, final String algorithmName) throws RealmUnavailableException { + try { + return delegateRealm.getEvidenceVerifySupport(evidenceType, algorithmName); + } catch (RealmUnavailableException rue) { + log.realmFailover(rue); + if (failoverCallback != null) { + failoverCallback.accept(rue); + } + return SupportLevel.POSSIBLY_SUPPORTED; + } + } + + protected RealmIdentity createFailoverIdentity(final RealmIdentity identity, final Evidence evidence) { + return new FailoverRealmIdentity(identity) { + @Override + protected RealmIdentity getFailoverIdentity() throws RealmUnavailableException { + return failoverRealm.getRealmIdentity(evidence); + } + }; + } + + protected RealmIdentity createFailoverIdentity(final RealmIdentity identity, final Principal principal) { + return new FailoverRealmIdentity(identity) { + @Override + protected RealmIdentity getFailoverIdentity() throws RealmUnavailableException { + return failoverRealm.getRealmIdentity(principal); + } + }; + } + + protected abstract class FailoverRealmIdentity implements RealmIdentity { + protected RealmIdentity delegate; + protected boolean failed = false; + + public FailoverRealmIdentity(final RealmIdentity identity) { + this.delegate = identity; + } + + protected abstract RealmIdentity getFailoverIdentity() throws RealmUnavailableException; + + @Override + public SupportLevel getCredentialAcquireSupport(Class credentialType, String algorithmName, final AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException { + try { + return delegate.getCredentialAcquireSupport(credentialType, algorithmName, parameterSpec); + } catch (RealmUnavailableException rue) { + return failover(rue).getCredentialAcquireSupport(credentialType, algorithmName, parameterSpec); + } + } + + @Override + public C getCredential(Class credentialType) throws RealmUnavailableException { + try { + return delegate.getCredential(credentialType); + } catch (RealmUnavailableException rue) { + return failover(rue).getCredential(credentialType); + } finally { + disableFailover(); + } + } + + @Override + public SupportLevel getEvidenceVerifySupport(Class evidenceType, String algorithmName) throws RealmUnavailableException { + try { + return delegate.getEvidenceVerifySupport(evidenceType, algorithmName); + } catch (RealmUnavailableException rue) { + return failover(rue).getEvidenceVerifySupport(evidenceType, algorithmName); + } + } + + @Override + public boolean verifyEvidence(Evidence evidence) throws RealmUnavailableException { + try { + return delegate.verifyEvidence(evidence); + } catch (RealmUnavailableException rue) { + return failover(rue).verifyEvidence(evidence); + } finally { + disableFailover(); + } + } + + @Override + public boolean exists() throws RealmUnavailableException { + try { + return delegate.exists(); + } catch (RealmUnavailableException rue) { + return failover(rue).exists(); + } finally { + disableFailover(); + } + } + + @Override + public void updateCredential(Credential credential) throws RealmUnavailableException { + try { + delegate.updateCredential(credential); + } catch (RealmUnavailableException rue) { + failover(rue).updateCredential(credential); + } finally { + disableFailover(); + } + } + + @Override + public Principal getRealmIdentityPrincipal() { + return delegate.getRealmIdentityPrincipal(); + } + + @Override + public C getCredential(Class credentialType, String algorithmName) throws RealmUnavailableException { + try { + return delegate.getCredential(credentialType, algorithmName); + } catch (RealmUnavailableException rue) { + return failover(rue).getCredential(credentialType, algorithmName); + } finally { + disableFailover(); + } + } + + @Override + public C getCredential(final Class credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException { + try { + return delegate.getCredential(credentialType, algorithmName, parameterSpec); + } catch (RealmUnavailableException rue) { + return failover(rue).getCredential(credentialType, algorithmName, parameterSpec); + } finally { + disableFailover(); + } + } + + @Override + public R applyToCredential(Class credentialType, Function function) throws RealmUnavailableException { + try { + return delegate.applyToCredential(credentialType, function); + } catch (RealmUnavailableException rue) { + return failover(rue).applyToCredential(credentialType, function); + } finally { + disableFailover(); + } + } + + @Override + public R applyToCredential(Class credentialType, String algorithmName, Function function) throws RealmUnavailableException { + try { + return delegate.applyToCredential(credentialType, algorithmName, function); + } catch (RealmUnavailableException rue) { + return failover(rue).applyToCredential(credentialType, algorithmName, function); + } finally { + disableFailover(); + } + } + + @Override + public R applyToCredential(final Class credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec, final Function function) throws RealmUnavailableException { + try { + return delegate.applyToCredential(credentialType, algorithmName, parameterSpec, function); + } catch (RealmUnavailableException rue) { + return failover(rue).applyToCredential(credentialType, algorithmName, parameterSpec, function); + } finally { + disableFailover(); + } + } + + @Override + public void dispose() { + delegate.dispose(); + } + + @Override + public AuthorizationIdentity getAuthorizationIdentity() throws RealmUnavailableException { + try { + return delegate.getAuthorizationIdentity(); + } catch (RealmUnavailableException rue) { + return failover(rue).getAuthorizationIdentity(); + } finally { + disableFailover(); + } + } + + @Override + public Attributes getAttributes() throws RealmUnavailableException { + return delegate.getAttributes(); + } + + protected RealmIdentity failover(RealmUnavailableException rue) throws RealmUnavailableException { + if (failed) { + throw rue; + } + log.realmFailover(rue); + if (FailoverSecurityRealm.this.failoverCallback != null) { + FailoverSecurityRealm.this.failoverCallback.accept(rue); + } + failed = true; + delegate.dispose(); + delegate = getFailoverIdentity(); + return delegate; + } + + // Used to make sure that failover cannot happen in the middle of authentication. + protected void disableFailover() { + failed = true; + } + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/realm/FileSystemRealmUtil.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/realm/FileSystemRealmUtil.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/realm/FileSystemRealmUtil.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,65 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2021 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.auth.realm; + + +import java.util.List; + +import org.wildfly.common.Assert; +import org.wildfly.security.auth.principal.NamePrincipal; +import org.wildfly.security.auth.server.ModifiableRealmIdentity; +import org.wildfly.security.auth.server.ModifiableRealmIdentityIterator; +import org.wildfly.security.auth.server.RealmUnavailableException; +import org.wildfly.security.authz.Attributes; +import org.wildfly.security.credential.Credential; + +/** + * A utility class to utilize methods from the {@code FileSystemSecurityRealm} class for the Elytron Tool. + * + * @author Ashpan Raskar + * @author Cameron Rodriguez + */ +public class FileSystemRealmUtil { + + /** + * Copies identities from an existing {@code FileSystemSecurityRealm} to a new one. + * + * @param oldRealm the existing {@code FileSystemSecurityRealm} with the identities + * @param newRealm the new {@code FileSystemSecurityRealm} + * @throws RealmUnavailableException if either realm is unavailable or an operation fails + */ + public static void cloneIdentitiesToNewRealm(FileSystemSecurityRealm oldRealm, FileSystemSecurityRealm newRealm) throws RealmUnavailableException { + Assert.checkNotNullParam("Old FileSystem Realm", oldRealm); + Assert.checkNotNullParam("New FileSystem Realm", newRealm); + + ModifiableRealmIdentityIterator realmIterator = oldRealm.getRealmIdentityIterator(); + + while (realmIterator.hasNext()) { + ModifiableRealmIdentity oldIdentity = realmIterator.next(); + List credentials = ((FileSystemSecurityRealm.Identity) oldIdentity).loadCredentials(); + Attributes attributes = oldIdentity.getAttributes(); + + ModifiableRealmIdentity newIdentity = newRealm.getRealmIdentityForUpdate(new NamePrincipal(oldIdentity.getRealmIdentityPrincipal().getName())); + newIdentity.create(); + newIdentity.setCredentials(credentials); + newIdentity.setAttributes(attributes); + newIdentity.dispose(); + } + realmIterator.close(); + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/realm/FileSystemSecurityRealm.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/realm/FileSystemSecurityRealm.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/realm/FileSystemSecurityRealm.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,1696 @@ +/* + * 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.auth.realm; + +import static java.nio.file.StandardOpenOption.CREATE_NEW; +import static java.nio.file.StandardOpenOption.DSYNC; +import static java.nio.file.StandardOpenOption.READ; +import static java.nio.file.StandardOpenOption.WRITE; +import static javax.xml.stream.XMLStreamConstants.END_ELEMENT; +import static javax.xml.stream.XMLStreamConstants.START_ELEMENT; +import static org.wildfly.security.provider.util.ProviderUtil.INSTALLED_PROVIDERS; + +import java.io.BufferedOutputStream; +import java.io.File; +import java.io.FileNotFoundException; +import java.io.FileWriter; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.nio.file.DirectoryStream; +import java.nio.file.FileAlreadyExistsException; +import java.nio.file.Files; +import java.nio.file.NoSuchFileException; +import java.nio.file.Path; +import java.security.AccessController; +import java.security.GeneralSecurityException; +import java.security.InvalidAlgorithmParameterException; +import java.security.KeyException; +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.Principal; +import java.security.PrivateKey; +import java.security.PrivilegedAction; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; +import java.security.Provider; +import java.security.PublicKey; +import java.security.cert.CertificateEncodingException; +import java.security.cert.CertificateException; +import java.security.cert.CertificateFactory; +import java.security.cert.X509Certificate; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.text.Normalizer; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ThreadLocalRandom; +import java.util.function.Consumer; +import java.util.function.Supplier; +import javax.crypto.SecretKey; +import javax.xml.crypto.MarshalException; +import javax.xml.crypto.dsig.CanonicalizationMethod; +import javax.xml.crypto.dsig.DigestMethod; +import javax.xml.crypto.dsig.Reference; +import javax.xml.crypto.dsig.SignedInfo; +import javax.xml.crypto.dsig.Transform; +import javax.xml.crypto.dsig.XMLSignature; +import javax.xml.crypto.dsig.XMLSignatureException; +import javax.xml.crypto.dsig.XMLSignatureFactory; +import javax.xml.crypto.dsig.dom.DOMSignContext; +import javax.xml.crypto.dsig.dom.DOMValidateContext; +import javax.xml.crypto.dsig.keyinfo.KeyInfo; +import javax.xml.crypto.dsig.keyinfo.KeyInfoFactory; +import javax.xml.crypto.dsig.keyinfo.KeyValue; +import javax.xml.crypto.dsig.spec.C14NMethodParameterSpec; +import javax.xml.crypto.dsig.spec.TransformParameterSpec; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; +import javax.xml.stream.XMLInputFactory; +import javax.xml.stream.XMLOutputFactory; +import javax.xml.stream.XMLStreamException; +import javax.xml.stream.XMLStreamReader; +import javax.xml.stream.XMLStreamWriter; +import javax.xml.transform.Transformer; +import javax.xml.transform.TransformerException; +import javax.xml.transform.TransformerFactory; +import javax.xml.transform.dom.DOMSource; +import javax.xml.transform.stream.StreamResult; +import org.w3c.dom.Document; +import org.w3c.dom.Element; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.wildfly.common.Assert; +import org.wildfly.common.bytes.ByteStringBuilder; +import org.wildfly.common.codec.Base32Alphabet; +import org.wildfly.common.codec.Base64Alphabet; +import org.wildfly.common.iteration.ByteIterator; +import org.wildfly.common.iteration.CodePointIterator; +import org.wildfly.security.auth.SupportLevel; +import org.wildfly.security.auth.principal.NamePrincipal; +import org.wildfly.security.auth.realm.IdentitySharedExclusiveLock.IdentityLock; +import org.wildfly.security.auth.server.ModifiableRealmIdentity; +import org.wildfly.security.auth.server.ModifiableRealmIdentityIterator; +import org.wildfly.security.auth.server.ModifiableSecurityRealm; +import org.wildfly.security.auth.server.NameRewriter; +import org.wildfly.security.auth.server.RealmIdentity; +import org.wildfly.security.auth.server.RealmUnavailableException; +import org.wildfly.security.authz.Attributes; +import org.wildfly.security.authz.AuthorizationIdentity; +import org.wildfly.security.authz.MapAttributes; +import org.wildfly.security.credential.Credential; +import org.wildfly.security.credential.PasswordCredential; +import org.wildfly.security.credential.PublicKeyCredential; +import org.wildfly.security.credential.X509CertificateChainPublicCredential; +import org.wildfly.security.encryption.CipherUtil; +import org.wildfly.security.evidence.Evidence; +import org.wildfly.security.password.Password; +import org.wildfly.security.password.PasswordFactory; +import org.wildfly.security.password.interfaces.OneTimePassword; +import org.wildfly.security.password.spec.BasicPasswordSpecEncoding; +import org.wildfly.security.password.spec.Encoding; +import org.wildfly.security.password.spec.OneTimePasswordSpec; +import org.wildfly.security.password.spec.PasswordSpec; +import org.wildfly.security.password.util.ModularCrypt; +import org.wildfly.security.permission.ElytronPermission; +import org.xml.sax.SAXException; + +/** + * A simple filesystem-backed security realm. + * + * @author David M. Lloyd + */ +public final class FileSystemSecurityRealm implements ModifiableSecurityRealm, CacheableSecurityRealm { + + static final ElytronPermission CREATE_SECURITY_REALM = ElytronPermission.forName("createSecurityRealm"); + + private final Supplier providers; + static final Map KNOWN_NAMESPACES; + + private enum Version { + + VERSION_1_0("urn:elytron:1.0", null), + VERSION_1_0_1("urn:elytron:1.0.1", VERSION_1_0), + VERSION_1_1("urn:elytron:identity:1.1", VERSION_1_0_1), + VERSION_1_2("urn:elytron:identity:1.2", VERSION_1_1); + + final String namespace; + + /* + * In the future we could support multiple parents but wait until that becomes a reality before adding it. + */ + final Version parent; + + Version(String namespace, Version parent) { + this.namespace = namespace; + this.parent = parent; + } + + String getNamespace() { + return namespace; + } + + boolean isAtLeast(Version version) { + return this.equals(version) || (parent != null ? parent.isAtLeast(version) : false); + } + + } + + static { + Map knownNamespaces = new HashMap<>(); + for (Version version : Version.values()) { + knownNamespaces.put(version.namespace, version); + } + KNOWN_NAMESPACES = Collections.unmodifiableMap(knownNamespaces); + } + + private final Path root; + private final NameRewriter nameRewriter; + private final int levels; + private final boolean encoded; + private final Charset hashCharset; + private final Encoding hashEncoding; + private final SecretKey secretKey; + private final PrivateKey privateKey; + private final PublicKey publicKey; + private final ConcurrentHashMap realmIdentityLocks = new ConcurrentHashMap<>(); + + /** + * Construct a new instance of the FileSystemSecurityRealmBuilder. + * + * @return the new FileSystemSecurityRealmBuilder instance + */ + public static FileSystemSecurityRealmBuilder builder() { + return new FileSystemSecurityRealmBuilder(); + } + /** + * Construct a new instance. + * + * Construction with enabled security manager requires {@code createSecurityRealm} {@link ElytronPermission}. + * + * @param root the root path of the identity store + * @param nameRewriter the name rewriter to apply to looked up names + * @param levels the number of levels of directory hashing to apply + * @param encoded whether identity names should be BASE32 encoded before using as filename (only applies if the security realm is unencrypted) + * @param hashCharset the character set to use when converting password strings to a byte array. Uses UTF-8 by default. + * @param hashEncoding the string format for the hashed passwords. Uses Base64 by default. + * @param providers The providers supplier + * @param secretKey the SecretKey used to encrypt and decrypt the security realm (if {@code null}, the security realm will be unencrypted) + * @param privateKey the PrivateKey used to verify the integrity of the security realm (if {@code null}, the security realm will not verify integrity) + * @param publicKey the PublicKey used to verify the integrity of the security realm (if {@code null}, the security realm will not verify integrity) + * + */ + public FileSystemSecurityRealm(final Path root, final NameRewriter nameRewriter, final int levels, final boolean encoded, final Encoding hashEncoding, final Charset hashCharset, final Supplier providers, final SecretKey secretKey, final PrivateKey privateKey, final PublicKey publicKey) { + final SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission(CREATE_SECURITY_REALM); + } + this.root = root; + this.nameRewriter = nameRewriter; + this.levels = levels; + this.encoded = secretKey == null && encoded; + this.hashCharset = hashCharset != null ? hashCharset : StandardCharsets.UTF_8; + this.hashEncoding = hashEncoding != null ? hashEncoding : Encoding.BASE64; + this.providers = providers != null ? providers : INSTALLED_PROVIDERS; + this.secretKey = secretKey; + this.privateKey = privateKey; + this.publicKey = publicKey; + + } + + /** + * Construct a new instance. + * + * Construction with enabled security manager requires {@code createSecurityRealm} {@link ElytronPermission}. + * + * @param root the root path of the identity store + * @param nameRewriter the name rewriter to apply to looked up names + * @param levels the number of levels of directory hashing to apply + * @param encoded whether identity names should be BASE32 encoded before using as filename + * @param hashCharset the character set to use when converting password strings to a byte array. Uses UTF-8 by default. + * @param hashEncoding the string format for the hashed passwords. Uses Base64 by default. + * @param secretKey the SecretKey used to encrypt and decrypt the security realm (if {@code null}, the security realm will be unencrypted) + */ + public FileSystemSecurityRealm(final Path root, final NameRewriter nameRewriter, final int levels, final boolean encoded, final Encoding hashEncoding, final Charset hashCharset, final SecretKey secretKey) { + this(root, nameRewriter, levels, encoded, hashEncoding, hashCharset, INSTALLED_PROVIDERS, secretKey, null, null); + } + + /** + * Construct a new instance. + * + * Construction with enabled security manager requires {@code createSecurityRealm} {@link ElytronPermission}. + * + * @param root the root path of the identity store + * @param nameRewriter the name rewriter to apply to looked up names + * @param levels the number of levels of directory hashing to apply + * @param encoded whether identity names should be BASE32 encoded before using as filename + * @param hashCharset the character set to use when converting password strings to a byte array. Uses UTF-8 by default. + * @param hashEncoding the string format for the hashed passwords. Uses Base64 by default. + */ + public FileSystemSecurityRealm(final Path root, final NameRewriter nameRewriter, final int levels, final boolean encoded, final Encoding hashEncoding, final Charset hashCharset) { + this(root, nameRewriter, levels, encoded, hashEncoding, hashCharset, INSTALLED_PROVIDERS, null, null, null); + } + + /** + * Construct a new instance. + * + * Construction with enabled security manager requires {@code createSecurityRealm} {@link ElytronPermission}. + * + * @param root the root path of the identity store + * @param nameRewriter the name rewriter to apply to looked up names + * @param levels the number of levels of directory hashing to apply + * @param encoded whether identity names should by BASE32 encoded before using as filename + */ + public FileSystemSecurityRealm(final Path root, final NameRewriter nameRewriter, final int levels, final boolean encoded) { + this(root, nameRewriter, levels, encoded, Encoding.BASE64, StandardCharsets.UTF_8, INSTALLED_PROVIDERS, null, null, null); + } + + /** + * Construct a new instance. + * + * @param root the root path of the identity store + * @param nameRewriter the name rewriter to apply to looked up names + * @param levels the number of levels of directory hashing to apply + */ + public FileSystemSecurityRealm(final Path root, final NameRewriter nameRewriter, final int levels) { + this(root, nameRewriter, levels, true); + } + + /** + * Construct a new instance. + * + * @param root the root path of the identity store + * @param nameRewriter the name rewriter to apply to looked up names + * @param levels the number of levels of directory hashing to apply + * @param hashEncoding the string format for hashed passwords. Uses Base64 by default. + * @param hashCharset the character set to use when converting password strings to a byte array. Uses UTF-8 by default and must not be {@code null}. + */ + public FileSystemSecurityRealm(final Path root, final NameRewriter nameRewriter, final int levels, final Encoding hashEncoding, final Charset hashCharset) { + this(root, nameRewriter, levels, true, hashEncoding, hashCharset, INSTALLED_PROVIDERS, null, null, null); + } + + + + /** + * Construct a new instance. + * + * @param root the root path of the identity store + * @param levels the number of levels of directory hashing to apply + */ + public FileSystemSecurityRealm(final Path root, final int levels) { + this(root, NameRewriter.IDENTITY_REWRITER, levels, true); + } + + + /** + * Construct a new instance. + * + * @param root the root path of the identity store + * @param levels the number of levels of directory hashing to apply + * @param hashEncoding the string format for hashed passwords. Uses Base64 by default. + * @param hashCharset the character set to use when converting password strings to a byte array. Uses UTF-8 by default and must not be {@code null}. + */ + public FileSystemSecurityRealm(final Path root, final int levels, final Encoding hashEncoding, final Charset hashCharset) { + this(root, NameRewriter.IDENTITY_REWRITER, levels, true, hashEncoding, hashCharset, INSTALLED_PROVIDERS, null, null, null); + } + + /** + * Construct a new instance with 2 levels of hashing. + * + * @param root the root path of the identity store + */ + public FileSystemSecurityRealm(final Path root) { + this(root, NameRewriter.IDENTITY_REWRITER, 2, true); + } + + /** + * Construct a new instance with 2 levels of hashing. + * + * @param root the root path of the identity store + * @param hashEncoding the string format for hashed passwords. Uses Base64 by default. + * @param hashCharset the character set to use when converting password strings to a byte array. Uses UTF-8 by default and must not be {@code null} + */ + public FileSystemSecurityRealm(final Path root, final Encoding hashEncoding, final Charset hashCharset) { + this(root, NameRewriter.IDENTITY_REWRITER, 2, true, hashEncoding, hashCharset, INSTALLED_PROVIDERS, null, null, null); + } + + public FileSystemSecurityRealm(Path root, int levels, Supplier providers) { + this(root, NameRewriter.IDENTITY_REWRITER, levels, true, Encoding.BASE64, StandardCharsets.UTF_8, providers, null, null, null); + } + + /** + * Checks if the FileSystemSecurityRealm has Integrity checking enabled + * @return {@code true} if Integrity checking is enabled, and {@code false} otherwise + */ + public boolean hasIntegrityEnabled() { + return privateKey != null && publicKey != null; + } + private Path pathFor(String name) { + assert name.codePointCount(0, name.length()) > 0; + String normalizedName = name; + + if (encoded) { + normalizedName = Normalizer.normalize(name, Normalizer.Form.NFKC) + .toLowerCase(Locale.ROOT) + .replaceAll("[^a-z0-9]", "_"); + } + if (secretKey != null || encoded) { + String base32 = ByteIterator.ofBytes(new ByteStringBuilder().append(name).toArray()) + .base32Encode(Base32Alphabet.STANDARD, false).drainToString(); + normalizedName = secretKey != null ? base32 : normalizedName + "-" + base32; + } + + Path path = root; + int idx = 0; + for (int level = 0; level < levels; level ++) { + int newIdx = normalizedName.offsetByCodePoints(idx, 1); + path = path.resolve(normalizedName.substring(idx, newIdx)); + idx = newIdx; + if (idx == normalizedName.length()) { + break; + } + } + + return path.resolve(normalizedName + ".xml"); + } + + public Charset getHashCharset() { + return this.hashCharset; + } + + private String nameFor(Path path) { + String fileName = path.toString(); + fileName = fileName.substring(0, fileName.length() - 4); // remove ".xml" + + if (secretKey != null) { + CodePointIterator it = CodePointIterator.ofString(fileName); + fileName = it.base32Decode(Base32Alphabet.STANDARD, false) + .asUtf8String().drainToString(); + } else if (encoded) { + CodePointIterator it = CodePointIterator.ofString(fileName); + it.delimitedBy('-').skipAll(); + it.next(); // skip '-' + fileName = it.base32Decode(Base32Alphabet.STANDARD, false) + .asUtf8String().drainToString(); + } + return fileName; + } + + public RealmIdentity getRealmIdentity(final Principal principal) { + return NamePrincipal.isConvertibleTo(principal) ? getRealmIdentity(principal.getName(), false) : RealmIdentity.NON_EXISTENT; + } + + @Override + public ModifiableRealmIdentity getRealmIdentityForUpdate(final Principal principal) { + return NamePrincipal.isConvertibleTo(principal) ? getRealmIdentity(principal.getName(), true) : ModifiableRealmIdentity.NON_EXISTENT; + } + + @Override + public void registerIdentityChangeListener(Consumer listener) { + // no need to register the listener given that changes to identities are done through the realm + } + + private ModifiableRealmIdentity getRealmIdentity(final String name, final boolean exclusive) { + final String finalName = nameRewriter.rewriteName(name); + if (finalName == null) { + throw ElytronMessages.log.invalidName(); + } + + // Acquire the appropriate lock for the realm identity + IdentitySharedExclusiveLock realmIdentityLock = getRealmIdentityLockForName(finalName); + IdentityLock lock; + if (exclusive) { + lock = realmIdentityLock.lockExclusive(); + } else { + lock = realmIdentityLock.lockShared(); + } + return new Identity(finalName, pathFor(finalName), lock, hashCharset, hashEncoding, providers, secretKey, privateKey, publicKey, hasIntegrityEnabled()); + } + + @Override + public ModifiableRealmIdentityIterator getRealmIdentityIterator() throws RealmUnavailableException { + return subIterator(root, levels); + } + + private ModifiableRealmIdentityIterator subIterator(final Path root, final int levels) { + final DirectoryStream stream; + final Iterator iterator; + if (levels == 0) { + try { + stream = Files.newDirectoryStream(root, "*.xml"); + iterator = stream.iterator(); + } catch (IOException e) { + ElytronMessages.log.debug("Unable to open directory", e); + return ModifiableRealmIdentityIterator.emptyIterator(); + } + return new ModifiableRealmIdentityIterator() { + + public boolean hasNext() { + if ( ! iterator.hasNext()) { + try { + close(); + } catch (IOException e) { + ElytronMessages.log.debug("Unable to close the stream", e); + } + } + return iterator.hasNext(); + } + + public ModifiableRealmIdentity next() { + final Path path = iterator.next(); + final String name = nameFor(path.getFileName()); + return getRealmIdentityForUpdate(new NamePrincipal(name)); + } + + public void close() throws RealmUnavailableException { + try { + stream.close(); + } catch (IOException e) { + ElytronMessages.log.debug("Unable to close the stream", e); + } + } + }; + } else { + try { + stream = Files.newDirectoryStream(root, entry -> { + final String fileName = entry.getFileName().toString(); + return fileName.length() == 1 && !fileName.equals(".") && Files.isDirectory(entry); + }); + iterator = stream.iterator(); + } catch (IOException e) { + ElytronMessages.log.debug("Unable to open directory", e); + return ModifiableRealmIdentityIterator.emptyIterator(); + } + return new ModifiableRealmIdentityIterator() { + private ModifiableRealmIdentityIterator subIterator; + + public boolean hasNext() { + for (;;) { + if (subIterator == null) { + if (! iterator.hasNext()) { + try { + close(); + } catch (IOException e) { + ElytronMessages.log.debug("Unable to close the stream", e); + } + return false; + } + final Path path = iterator.next(); + subIterator = subIterator(path, levels - 1); + } else if (subIterator.hasNext()) { + return true; + } else { + subIterator = null; + } + } + } + + public ModifiableRealmIdentity next() { + if (! hasNext()) { + throw new NoSuchElementException(); + } + return subIterator.next(); + } + + public void close() throws RealmUnavailableException { + try { + if (subIterator != null) subIterator.close(); + } finally { + try { + stream.close(); + } catch (IOException e) { + ElytronMessages.log.debug("Unable to close the stream", e); + } + } + } + }; + } + } + + public SupportLevel getCredentialAcquireSupport(final Class credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException { + return SupportLevel.POSSIBLY_SUPPORTED; + } + + public SupportLevel getEvidenceVerifySupport(final Class evidenceType, final String algorithmName) throws RealmUnavailableException { + return SupportLevel.POSSIBLY_SUPPORTED; + } + + private IdentitySharedExclusiveLock getRealmIdentityLockForName(final String name) { + IdentitySharedExclusiveLock realmIdentityLock = realmIdentityLocks.get(name); + if (realmIdentityLock == null) { + final IdentitySharedExclusiveLock newRealmIdentityLock = new IdentitySharedExclusiveLock(); + realmIdentityLock = realmIdentityLocks.putIfAbsent(name, newRealmIdentityLock); + if (realmIdentityLock == null) { + realmIdentityLock = newRealmIdentityLock; + } + } + return realmIdentityLock; + } + + @FunctionalInterface + interface CredentialParseFunction { + void parseCredential(String algorithm, String format, String body) throws RealmUnavailableException, XMLStreamException; + } + + /** + * Re-generate the signatures for all the identities in this realm. + * This method is intended to be called after updating the key pair used by this realm. + * + * @throws RealmUnavailableException if the realm is not able to handle requests for any reason + */ + public void updateRealmKeyPair() throws RealmUnavailableException { + if (! hasIntegrityEnabled()) { + throw ElytronMessages.log.integrityNotEnabled(root.toString()); + } + ModifiableRealmIdentityIterator realmIterator = this.getRealmIdentityIterator(); + while (realmIterator.hasNext()) { + Identity identity = (Identity) realmIterator.next(); + try{ + identity.writeDigitalSignature(identity.path, identity.name); + } finally { + identity.dispose(); + } + } + realmIterator.close(); + } + + /** + * Verify the integrity of each identity file in this realm. + * @return {@code true} if the integrity of all the identity files in the realm is successfully verified and {@code false} otherwise + * + */ + public IntegrityResult verifyRealmIntegrity() throws RealmUnavailableException { + if (! hasIntegrityEnabled()) { + throw ElytronMessages.log.integrityNotEnabled(root.toString()); + } + ArrayList failedIdentities = new ArrayList<>(); + ModifiableRealmIdentityIterator realmIterator = this.getRealmIdentityIterator(); + while (realmIterator.hasNext()) { + Identity identity = (Identity) realmIterator.next(); + if(! identity.isIntegrityValid()) { + failedIdentities.add(identity.name); + } + identity.dispose(); + } + realmIterator.close(); + return new IntegrityResult(failedIdentities.isEmpty(), failedIdentities); + } + + static class Identity implements ModifiableRealmIdentity { + + private static final String ENCRYPTION_FORMAT = "enc_base64"; + private static final String BASE64_FORMAT = "base64"; + private static final String MCF_FORMAT = "crypt"; + private static final String X509_FORMAT = "X.509"; + private static final String HEX = "hex"; + + private final String name; + private final Path path; + private final Supplier providers; + private IdentityLock lock; + private final Charset hashCharset; + private final Encoding hashEncoding; + private final SecretKey secretKey; + private final PrivateKey privateKey; + private final PublicKey publicKey; + private final boolean integrityEnabled; + + Identity(final String name, final Path path, final IdentityLock lock, final Charset hashCharset, final Encoding hashEncoding, Supplier providers, final SecretKey secretKey, final PrivateKey privateKey, final PublicKey publicKey, final boolean integrityEnabled) { + this.name = name; + this.path = path; + this.lock = lock; + this.hashCharset = hashCharset; + this.hashEncoding = hashEncoding; + this.providers = providers; + this.secretKey = secretKey; + this.privateKey = privateKey; + this.publicKey = publicKey; + this.integrityEnabled = integrityEnabled; + } + + public Principal getRealmIdentityPrincipal() { + return new NamePrincipal(name); + } + + public SupportLevel getCredentialAcquireSupport(final Class credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException { + Assert.checkNotNullParam("credentialType", credentialType); + List credentials = loadCredentials(); + for (Credential credential : credentials) { + if (credential.matches(credentialType, algorithmName, parameterSpec)) { + return SupportLevel.SUPPORTED; + } + } + return SupportLevel.UNSUPPORTED; + } + + @Override + public C getCredential(final Class credentialType) throws RealmUnavailableException { + return getCredential(credentialType, null); + } + + public C getCredential(final Class credentialType, final String algorithmName) throws RealmUnavailableException { + return getCredential(credentialType, algorithmName, null); + } + + public C getCredential(final Class credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException { + Assert.checkNotNullParam("credentialType", credentialType); + List credentials = loadCredentials(); + for (Credential credential : credentials) { + if (credential.matches(credentialType, algorithmName, parameterSpec)) { + return credentialType.cast(credential.clone()); + } + } + return null; + } + + public SupportLevel getEvidenceVerifySupport(final Class evidenceType, final String algorithmName) throws RealmUnavailableException { + Assert.checkNotNullParam("evidenceType", evidenceType); + List credentials = loadCredentials(); + for (Credential credential : credentials) { + if (credential.canVerify(evidenceType, algorithmName)) { + ElytronMessages.log.tracef("FileSystemSecurityRealm - evidence verification SUPPORTED: type = [%s] algorithm = [%s] credentials = [%d]", evidenceType, algorithmName, credentials.size()); + return SupportLevel.SUPPORTED; + } + } + ElytronMessages.log.tracef("FileSystemSecurityRealm - evidence verification UNSUPPORTED: type = [%s] algorithm = [%s] credentials = [%d]", evidenceType, algorithmName, credentials.size()); + return SupportLevel.UNSUPPORTED; + } + + public boolean verifyEvidence(final Evidence evidence) throws RealmUnavailableException { + Assert.checkNotNullParam("evidence", evidence); + + if (ElytronMessages.log.isTraceEnabled()) { + try { + final LoadedIdentity loadedIdentity = loadIdentity(false, true); + ElytronMessages.log.tracef("Trying to authenticate identity %s using FileSystemSecurityRealm", (loadedIdentity != null) ? loadedIdentity.getName() : "null"); + } catch (RealmUnavailableException e) { + if (e.getCause() instanceof IntegrityException) { + return false; + } + throw e; + } + } + List credentials = null; + try { + credentials = loadCredentials(); + } catch (RealmUnavailableException e) { + if (e.getCause() instanceof IntegrityException) { + return false; + } + throw e; + } + ElytronMessages.log.tracef("FileSystemSecurityRealm - verification evidence [%s] against [%d] credentials...", evidence, credentials.size()); + for (Credential credential : credentials) { + if (credential.canVerify(evidence)) { + boolean verified = false; + if (credential instanceof PasswordCredential) { + verified = ((PasswordCredential )credential).verify(providers, evidence, hashCharset); + } else { + verified = credential.verify(providers, evidence); + } + ElytronMessages.log.tracef("FileSystemSecurityRealm - verification against credential [%s] = %b", credential, verified); + return verified; + } + } + ElytronMessages.log.tracef("FileSystemSecurityRealm - no credential able to verify evidence [%s]", evidence); + return false; + } + + List loadCredentials() throws RealmUnavailableException { + final LoadedIdentity loadedIdentity = loadIdentity(false, true); + return loadedIdentity == null ? Collections.emptyList() : loadedIdentity.getCredentials(); + } + + public boolean exists() throws RealmUnavailableException { + if (System.getSecurityManager() == null) { + return Files.exists(path); + } + return AccessController.doPrivileged((PrivilegedAction) () -> Files.exists(path)); + } + + public void delete() throws RealmUnavailableException { + if (System.getSecurityManager() == null) { + deletePrivileged(); + return; + } + try { + AccessController.doPrivileged((PrivilegedExceptionAction) this::deletePrivileged); + } catch (PrivilegedActionException e) { + if (e.getException() instanceof RealmUnavailableException) { + throw (RealmUnavailableException) e.getException(); + } + throw new RuntimeException(e.getException()); + } + } + + private Void deletePrivileged() throws RealmUnavailableException { + try { + Files.delete(path); + return null; + } catch (NoSuchFileException e) { + throw ElytronMessages.log.fileSystemRealmNotFound(name); + } catch (IOException e) { + throw ElytronMessages.log.fileSystemRealmDeleteFailed(name, e); + } + } + + private String tempSuffix() { + final ThreadLocalRandom random = ThreadLocalRandom.current(); + char[] array = new char[12]; + for (int i = 0; i < array.length; i ++) { + int idx = random.nextInt(36); + if (idx < 26) { + array[i] = (char) ('A' + idx); + } else { + array[i] = (char) ('0' + idx - 26); + } + } + return new String(array); + } + + private Path tempPath() { + Path parent = path.getParent(); + File file = parent.toFile(); + if (!file.exists()) { + file.mkdirs(); + } + return parent.resolve(path.getFileName().toString() + '.' + tempSuffix()); + } + + public void create() throws RealmUnavailableException { + if (System.getSecurityManager() == null) { + createPrivileged(); + return; + } + try { + AccessController.doPrivileged((PrivilegedExceptionAction) this::createPrivileged); + } catch (PrivilegedActionException e) { + if (e.getException() instanceof RealmUnavailableException) { + throw (RealmUnavailableException) e.getException(); + } + throw new RuntimeException(e.getException()); + } + } + + private Void createPrivileged() throws RealmUnavailableException { + for (;;) { + final Path tempPath = tempPath(); + final XMLOutputFactory xmlOutputFactory = XMLOutputFactory.newFactory(); + try (OutputStream outputStream = new BufferedOutputStream(Files.newOutputStream(tempPath, WRITE, CREATE_NEW, DSYNC))) { + try (AutoCloseableXMLStreamWriterHolder holder = new AutoCloseableXMLStreamWriterHolder(xmlOutputFactory.createXMLStreamWriter(outputStream))) { + String namespace = ""; + if (integrityEnabled) { + namespace = Version.VERSION_1_2.getNamespace(); + } else if (secretKey != null) { + namespace = Version.VERSION_1_1.getNamespace(); + } else { + namespace = Version.VERSION_1_0.getNamespace(); + } + final XMLStreamWriter streamWriter = holder.getXmlStreamWriter(); + // create empty identity + streamWriter.writeStartDocument(); + streamWriter.writeCharacters("\n"); + streamWriter.writeStartElement("identity"); + streamWriter.writeDefaultNamespace(namespace); + if (integrityEnabled) { + streamWriter.writeCharacters("\n "); + streamWriter.writeStartElement("principal"); + streamWriter.writeAttribute("name", secretKey != null ? CipherUtil.encrypt(name, secretKey) : name); + streamWriter.writeEndElement(); + streamWriter.writeCharacters("\n "); + } + streamWriter.writeEndElement(); + streamWriter.writeEndDocument(); + } catch (XMLStreamException | GeneralSecurityException e) { + throw ElytronMessages.log.fileSystemRealmFailedToWrite(tempPath, name, e); + } + if(integrityEnabled) { + try { + writeDigitalSignature(tempPath, this.name); + } catch (RealmUnavailableException e) { + throw ElytronMessages.log.unableToGenerateSignature(path.toString()); + } + } + } catch (FileAlreadyExistsException ignored) { + // try a new name + continue; + } catch (IOException e) { + throw ElytronMessages.log.fileSystemRealmFailedToOpen(tempPath, name, e); + } + try { + Files.createLink(path, tempPath); + } catch (FileAlreadyExistsException e) { + try { + Files.delete(tempPath); + } catch (IOException e2) { + e.addSuppressed(e2); + } + throw ElytronMessages.log.fileSystemRealmAlreadyExists(name, e); + } catch (IOException e) { + throw ElytronMessages.log.fileSystemRealmFailedToWrite(tempPath, name, e); + } + try { + Files.delete(tempPath); + } catch (IOException ignored) { + // nothing we can do + } + return null; + } + } + + public void setCredentials(final Collection credentials) throws RealmUnavailableException { + Assert.checkNotNullParam("credential", credentials); + final LoadedIdentity loadedIdentity = loadIdentity(false, false); + if (loadedIdentity == null) { + throw ElytronMessages.log.fileSystemRealmNotFound(name); + } + + final LoadedIdentity newIdentity = new LoadedIdentity(name, new ArrayList<>(credentials), loadedIdentity.getAttributes(), hashEncoding); + replaceIdentity(newIdentity); + } + + public void setAttributes(final Attributes attributes) throws RealmUnavailableException { + Assert.checkNotNullParam("attributes", attributes); + final LoadedIdentity loadedIdentity = loadIdentity(false, true); + if (loadedIdentity == null) { + throw ElytronMessages.log.fileSystemRealmNotFound(name); + } + final LoadedIdentity newIdentity = new LoadedIdentity(name, loadedIdentity.getCredentials(), attributes, hashEncoding); + replaceIdentity(newIdentity); + } + + @Override + public Attributes getAttributes() throws RealmUnavailableException { + final LoadedIdentity loadedIdentity = loadIdentity(true, false); + if (loadedIdentity == null) { + throw ElytronMessages.log.fileSystemRealmNotFound(name); + } + return loadedIdentity.getAttributes().asReadOnly(); + } + + private void replaceIdentity(final LoadedIdentity newIdentity) throws RealmUnavailableException { + if (System.getSecurityManager() == null) { + replaceIdentityPrivileged(newIdentity); + return; + } + try { + AccessController.doPrivileged((PrivilegedExceptionAction) () -> replaceIdentityPrivileged(newIdentity)); + } catch (PrivilegedActionException e) { + if (e.getException() instanceof RealmUnavailableException) { + throw (RealmUnavailableException) e.getException(); + } + throw new RuntimeException(e.getException()); + } + } + + private Void replaceIdentityPrivileged(final LoadedIdentity newIdentity) throws RealmUnavailableException { + if (!isIntegrityValid()) { + throw new RealmUnavailableException(ElytronMessages.log.invalidIdentitySignature(name)); + } + for (;;) { + final Path tempPath = tempPath(); + try { + final XMLOutputFactory xmlOutputFactory = XMLOutputFactory.newFactory(); + try (OutputStream outputStream = new BufferedOutputStream(Files.newOutputStream(tempPath, WRITE, CREATE_NEW, DSYNC))) { + try (AutoCloseableXMLStreamWriterHolder holder = new AutoCloseableXMLStreamWriterHolder(xmlOutputFactory.createXMLStreamWriter(outputStream))) { + writeIdentity(holder.getXmlStreamWriter(), newIdentity); + } catch (XMLStreamException | InvalidKeySpecException | NoSuchAlgorithmException | CertificateEncodingException e) { + throw ElytronMessages.log.fileSystemRealmFailedToWrite(tempPath, name, e); + } catch (GeneralSecurityException e) { + throw ElytronMessages.log.fileSystemRealmEncryptionFailed(e); + } + if (integrityEnabled) { + try { + writeDigitalSignature(tempPath, name); + } catch (RealmUnavailableException e) { + throw ElytronMessages.log.unableToGenerateSignature(path.toString()); + } + } + } catch (FileAlreadyExistsException ignored) { + // try a new name + continue; + } catch (IOException e) { + try { + Files.deleteIfExists(tempPath); + } catch (IOException e2) { + e.addSuppressed(e2); + } + throw ElytronMessages.log.fileSystemRealmFailedToOpen(tempPath, name, e); + } + try { + Files.delete(path); + } catch (IOException e) { + throw ElytronMessages.log.fileSystemUpdatedFailed(path.toAbsolutePath().toString(), e); + } + try { + Files.createLink(path, tempPath); + } catch (FileAlreadyExistsException e) { + try { + Files.deleteIfExists(tempPath); + } catch (IOException e2) { + e.addSuppressed(e2); + } + throw ElytronMessages.log.fileSystemRealmAlreadyExists(name, e); + } catch (IOException e) { + throw ElytronMessages.log.fileSystemRealmFailedToWrite(tempPath, name, e); + } + try { + Files.delete(tempPath); + } catch (IOException ignored) { + // nothing we can do + } + return null; + } catch (Throwable t) { + try { + Files.delete(tempPath); + } catch (IOException e) { + t.addSuppressed(e); + } + throw t; + } + } + } + + private Version requiredVersion(final LoadedIdentity identityToWrite) { + // As new functionality is added we will identify if we need to use a later version + // if new functionality is used then use the required schema version otherwise fallback + // to an older version. + + if (integrityEnabled) { + return Version.VERSION_1_2; + } else if (secretKey != null) { + return Version.VERSION_1_1; + } else { + return Version.VERSION_1_0; + } + } + + private void writeIdentity(final XMLStreamWriter streamWriter, final LoadedIdentity newIdentity) throws XMLStreamException, InvalidKeySpecException, NoSuchAlgorithmException, GeneralSecurityException { + streamWriter.writeStartDocument(); + streamWriter.writeCharacters("\n"); + streamWriter.writeStartElement("identity"); + streamWriter.writeDefaultNamespace(requiredVersion(newIdentity).getNamespace()); + + if (integrityEnabled) { + streamWriter.writeCharacters("\n "); + streamWriter.writeStartElement("principal"); + streamWriter.writeAttribute("name", secretKey != null ? CipherUtil.encrypt(name, secretKey) : name); + streamWriter.writeEndElement(); + } + + if (newIdentity.getCredentials().size() > 0) { + streamWriter.writeCharacters("\n "); + streamWriter.writeStartElement("credentials"); + for (Credential credential : newIdentity.getCredentials()) { + streamWriter.writeCharacters("\n "); + if (credential instanceof PasswordCredential) { + Password password = ((PasswordCredential) credential).getPassword(); + if (password instanceof OneTimePassword) { + final OneTimePassword otp = (OneTimePassword) password; + streamWriter.writeStartElement("otp"); + streamWriter.writeAttribute("algorithm", otp.getAlgorithm()); + streamWriter.writeAttribute("hash", ByteIterator.ofBytes(otp.getHash()).base64Encode().drainToString()); + streamWriter.writeAttribute("seed", ByteIterator.ofBytes(otp.getSeed().getBytes(StandardCharsets.US_ASCII)).base64Encode().drainToString()); + streamWriter.writeAttribute("sequence", Integer.toString(otp.getSequenceNumber())); + streamWriter.writeEndElement(); + } else { + streamWriter.writeStartElement("password"); + String format; + String algorithm = password.getAlgorithm(); + String passwordString; + byte[] encoded = BasicPasswordSpecEncoding.encode(password, providers); + + if (secretKey != null) { + format = ENCRYPTION_FORMAT; + passwordString = ByteIterator.ofBytes(CipherUtil.encrypt(encoded, secretKey)).base64Encode().drainToString(); + } else if (encoded != null) { + if (newIdentity.getHashEncoding() == Encoding.HEX) { + format = HEX; + passwordString = ByteIterator.ofBytes(encoded).hexEncode().drainToString(); + } else { + // default to base64 + format = BASE64_FORMAT; + passwordString = ByteIterator.ofBytes(encoded).base64Encode().drainToString(); + } + } else { + format = MCF_FORMAT; + passwordString = ModularCrypt.encodeAsString(password); + } + + streamWriter.writeAttribute("algorithm", algorithm); + streamWriter.writeAttribute("format", format); + streamWriter.writeCharacters(passwordString); + streamWriter.writeEndElement(); + } + } + } + streamWriter.writeCharacters("\n "); + streamWriter.writeEndElement(); + } + final Iterator entryIter = newIdentity.getAttributes().entries().iterator(); + if (entryIter.hasNext()) { + streamWriter.writeCharacters("\n "); + streamWriter.writeStartElement("attributes"); + do { + final Attributes.Entry entry = entryIter.next(); + for (String value : entry) { + streamWriter.writeCharacters("\n "); + streamWriter.writeStartElement("attribute"); + streamWriter.writeAttribute("name", secretKey != null ? CipherUtil.encrypt(entry.getKey(), secretKey) : entry.getKey()); + streamWriter.writeAttribute("value", secretKey != null ? CipherUtil.encrypt(value, secretKey) : value); + streamWriter.writeEndElement(); + } + } while (entryIter.hasNext()); + streamWriter.writeCharacters("\n "); + streamWriter.writeEndElement(); + streamWriter.writeCharacters("\n"); + } + streamWriter.writeCharacters("\n "); + streamWriter.writeEndElement(); + streamWriter.writeEndDocument(); + } + + public void dispose() { + // Release the lock for this realm identity + IdentityLock identityLock = lock; + lock = null; + if (identityLock != null) { + identityLock.release(); + } + } + + @Override + public AuthorizationIdentity getAuthorizationIdentity() throws RealmUnavailableException { + final LoadedIdentity loadedIdentity = loadIdentity(true, false); + return loadedIdentity == null ? AuthorizationIdentity.EMPTY : AuthorizationIdentity.basicIdentity(loadedIdentity.getAttributes()); + } + + private LoadedIdentity loadIdentity(final boolean skipCredentials, final boolean skipAttributes) throws RealmUnavailableException { + if (System.getSecurityManager() == null) { + return loadIdentityPrivileged(skipCredentials, skipAttributes); + } + try { + return AccessController.doPrivileged((PrivilegedExceptionAction) () -> loadIdentityPrivileged(skipCredentials, skipAttributes)); + } catch (PrivilegedActionException e) { + if (e.getException() instanceof RealmUnavailableException) { + throw (RealmUnavailableException) e.getException(); + } + throw new RuntimeException(e.getException()); + } + } + + protected LoadedIdentity loadIdentityPrivileged(final boolean skipCredentials, final boolean skipAttributes) throws RealmUnavailableException { + if (!isIntegrityValid()) { + throw new RealmUnavailableException(ElytronMessages.log.invalidIdentitySignature(name)); + } + try (InputStream inputStream = Files.newInputStream(path, READ)) { + final XMLInputFactory inputFactory = XMLInputFactory.newFactory(); + inputFactory.setProperty(XMLInputFactory.IS_VALIDATING, Boolean.FALSE); + inputFactory.setProperty(XMLInputFactory.SUPPORT_DTD, Boolean.FALSE); + inputFactory.setProperty(XMLInputFactory.IS_SUPPORTING_EXTERNAL_ENTITIES, Boolean.FALSE); + inputFactory.setProperty(XMLInputFactory.IS_COALESCING, Boolean.TRUE); + try (final AutoCloseableXMLStreamReaderHolder holder = new AutoCloseableXMLStreamReaderHolder(inputFactory.createXMLStreamReader(inputStream, "UTF-8"))) { + final XMLStreamReader streamReader = holder.getXmlStreamReader(); + return parseIdentity(streamReader, skipCredentials, skipAttributes); + } catch (XMLStreamException e) { + throw ElytronMessages.log.fileSystemRealmFailedToRead(path, name, e); + } + } catch (NoSuchFileException | FileNotFoundException ignored) { + return null; + } catch (IOException e) { + throw ElytronMessages.log.fileSystemRealmFailedToOpen(path, name, e); + } + } + + private LoadedIdentity parseIdentity(final XMLStreamReader streamReader, final boolean skipCredentials, final boolean skipAttributes) throws RealmUnavailableException, XMLStreamException { + final int tag = streamReader.nextTag(); + Version version; + if (tag != START_ELEMENT || ((version = identifyVersion(streamReader)) == null) || ! "identity".equals(streamReader.getLocalName())) { + throw ElytronMessages.log.fileSystemRealmInvalidContent(path, streamReader.getLocation().getLineNumber(), name); + } + return parseIdentityContents(streamReader, version, skipCredentials, skipAttributes); + } + + private Version identifyVersion(final XMLStreamReader streamReader) { + return KNOWN_NAMESPACES.get(streamReader.getNamespaceURI()); + } + + private LoadedIdentity parseIdentityContents(final XMLStreamReader streamReader, final Version version, final boolean skipCredentials, final boolean skipAttributes) throws RealmUnavailableException, XMLStreamException { + final int attributeCount = streamReader.getAttributeCount(); + if (attributeCount > 0) { + throw ElytronMessages.log.fileSystemRealmInvalidContent(path, streamReader.getLocation().getLineNumber(), name); + } + List credentials = Collections.emptyList(); + Attributes attributes = Attributes.EMPTY; + boolean gotCredentials = false; + boolean gotAttributes = false; + for (;;) { + if (streamReader.isEndElement()) { + if (attributes == Attributes.EMPTY && !skipAttributes) { + // Since this could be a use-case wanting to modify the attributes, make sure that we have a + // modifiable version of Attributes; + attributes = new MapAttributes(); + } + return new LoadedIdentity(name, credentials, attributes, hashEncoding); + } + if (!(version.getNamespace().equals(streamReader.getNamespaceURI())) && !(XMLSignature.XMLNS.equals(streamReader.getNamespaceURI()))) { + // Mixed versions unsupported. + throw ElytronMessages.log.fileSystemRealmInvalidContent(path, streamReader.getLocation().getLineNumber(), name); + } + + if ("principal".equals(streamReader.getLocalName())) { + if (version.isAtLeast(Version.VERSION_1_2)) { + consumeContent(streamReader); + } else { + throw ElytronMessages.log.fileSystemRealmInvalidContent(path, streamReader.getLocation().getLineNumber(), name); + } + } + + if (! gotCredentials && "credentials".equals(streamReader.getLocalName())) { + gotCredentials = true; + if (skipCredentials) { + consumeContent(streamReader); + } else { + credentials = parseCredentials(streamReader, version); + } + } else if (! gotAttributes && "attributes".equals(streamReader.getLocalName())) { + gotAttributes = true; + if (skipAttributes) { + consumeContent(streamReader); + } else { + attributes = parseAttributes(streamReader, version); + } + } + streamReader.nextTag(); + } + } + + private List parseCredentials(final XMLStreamReader streamReader, final Version version) throws RealmUnavailableException, XMLStreamException { + final int attributeCount = streamReader.getAttributeCount(); + if (attributeCount > 0) { + throw ElytronMessages.log.fileSystemRealmInvalidContent(path, streamReader.getLocation().getLineNumber(), name); + } + if (streamReader.nextTag() == END_ELEMENT) { + return Collections.emptyList(); + } + List credentials = new ArrayList<>(); + do { + if (! version.getNamespace().equals(streamReader.getNamespaceURI()) ) { + // Mixed versions unsupported. + throw ElytronMessages.log.fileSystemRealmInvalidContent(path, streamReader.getLocation().getLineNumber(), name); + } + if ("password".equals(streamReader.getLocalName())) { + parsePassword(credentials, streamReader, version); + } else if ("public-key".equals(streamReader.getLocalName())) { + parsePublicKey(credentials, streamReader); + } else if ("certificate".equals(streamReader.getLocalName())) { + parseCertificate(credentials, streamReader); + } else if ("otp".equals(streamReader.getLocalName())) { + parseOtp(credentials, streamReader); + } else { + throw ElytronMessages.log.fileSystemRealmInvalidContent(path, streamReader.getLocation().getLineNumber(), name); + } + } while (streamReader.nextTag() != END_ELEMENT); + return credentials; + } + + private void parseCredential(final XMLStreamReader streamReader, CredentialParseFunction function) throws RealmUnavailableException, XMLStreamException { + final int attributeCount = streamReader.getAttributeCount(); + String name = null; + String algorithm = null; + String format = null; + for (int i = 0; i < attributeCount; i ++) { + String namespace = streamReader.getAttributeNamespace(i); + if (namespace != null && !namespace.equals("")) { + throw ElytronMessages.log.fileSystemRealmInvalidContent(path, streamReader.getLocation().getLineNumber(), name); + } + final String localName = streamReader.getAttributeLocalName(i); + if ("name".equals(localName)) { + name = streamReader.getAttributeValue(i); + } else if ("algorithm".equals(localName)) { + algorithm = streamReader.getAttributeValue(i); + } else if ("format".equals(localName)) { + format = streamReader.getAttributeValue(i); + } else { + throw ElytronMessages.log.fileSystemRealmInvalidContent(path, streamReader.getLocation().getLineNumber(), name); + } + } + final String text = streamReader.getElementText().trim(); + function.parseCredential(algorithm, format, text); + } + + private void parseCertificate(final List credentials, final XMLStreamReader streamReader) throws RealmUnavailableException, XMLStreamException { + parseCredential(streamReader, (algorithm, format, text) -> { + if (algorithm == null) algorithm = X509_FORMAT; + if (format == null) format = X509_FORMAT; + try { + final CertificateFactory certificateFactory = CertificateFactory.getInstance(algorithm); + credentials.add(new X509CertificateChainPublicCredential((X509Certificate) certificateFactory.generateCertificate( + CodePointIterator.ofString(text).base64Decode().asInputStream()))); + } catch (CertificateException | ClassCastException e) { + throw ElytronMessages.log.fileSystemRealmCertificateReadError(format, path, streamReader.getLocation().getLineNumber(), name); + } + }); + } + + private void parsePublicKey(final List credentials, final XMLStreamReader streamReader) throws RealmUnavailableException, XMLStreamException { + parseCredential(streamReader, (algorithm, format, text) -> { + if (algorithm == null) { + throw ElytronMessages.log.fileSystemRealmMissingAttribute("algorithm", path, streamReader.getLocation().getLineNumber(), name); + } + if (format == null) { + format = X509_FORMAT; + } else if (!X509_FORMAT.equals(format)) { + throw ElytronMessages.log.fileSystemRealmUnsupportedKeyFormat(format, path, streamReader.getLocation().getLineNumber(), name); + } + try { + KeyFactory keyFactory = KeyFactory.getInstance(algorithm); + credentials.add(new PublicKeyCredential(keyFactory.generatePublic(new PKCS8EncodedKeySpec(CodePointIterator.ofString(text).base64Decode().drain())))); + } catch (NoSuchAlgorithmException e) { + throw ElytronMessages.log.fileSystemRealmUnsupportedKeyAlgorithm(format, path, streamReader.getLocation().getLineNumber(), name, e); + } catch (InvalidKeySpecException e) { + throw ElytronMessages.log.fileSystemRealmUnsupportedKeyFormat(format, path, streamReader.getLocation().getLineNumber(), name); + } + }); + } + + private void parsePassword(final List credentials, final XMLStreamReader streamReader, final Version version) throws XMLStreamException, RealmUnavailableException { + parseCredential(streamReader, (algorithm, format, text) -> { + try { + if (ENCRYPTION_FORMAT.equals(format)) { + if (! version.isAtLeast(Version.VERSION_1_1)) { + throw ElytronMessages.log.fileSystemRealmInvalidContent(path, streamReader.getLocation().getLineNumber(), name); + } + if (algorithm == null) { + throw ElytronMessages.log.fileSystemRealmMissingAttribute("algorithm", path, streamReader.getLocation().getLineNumber(), name); + } + PasswordFactory passwordFactory = PasswordFactory.getInstance(algorithm, providers); + byte[] encryptedPasswordBytes = CodePointIterator.ofChars(text.toCharArray()).base64Decode().drain(); + byte[] decryptedPasswordBytes; + try { + decryptedPasswordBytes = CipherUtil.decrypt(encryptedPasswordBytes, secretKey); + } catch (GeneralSecurityException e) { + throw ElytronMessages.log.fileSystemRealmDecryptionFailed(e); + } + PasswordSpec passwordSpec = BasicPasswordSpecEncoding.decode(decryptedPasswordBytes); + + if (passwordSpec != null) { + credentials.add(new PasswordCredential(passwordFactory.generatePassword(passwordSpec))); + } else { + throw ElytronMessages.log.fileSystemRealmInvalidPasswordAlgorithm(algorithm, path, streamReader.getLocation().getLineNumber(), name); + } + } else if (BASE64_FORMAT.equals(format) || HEX.equals(format)) { + if (algorithm == null) { + throw ElytronMessages.log.fileSystemRealmMissingAttribute("algorithm", path, streamReader.getLocation().getLineNumber(), name); + } + PasswordFactory passwordFactory = PasswordFactory.getInstance(algorithm, providers); + byte[] passwordBytes; + if (BASE64_FORMAT.equals(format)) { + passwordBytes = CodePointIterator.ofChars(text.toCharArray()).base64Decode().drain(); + } else { + passwordBytes = CodePointIterator.ofChars(text.toCharArray()).hexDecode().drain(); + } + PasswordSpec passwordSpec = BasicPasswordSpecEncoding.decode(passwordBytes); + + if (passwordSpec != null) { + credentials.add(new PasswordCredential(passwordFactory.generatePassword(passwordSpec))); + } else { + throw ElytronMessages.log.fileSystemRealmInvalidPasswordAlgorithm(algorithm, path, streamReader.getLocation().getLineNumber(), name); + } + } else if (MCF_FORMAT.equals(format)) { + credentials.add(new PasswordCredential(ModularCrypt.decode(text))); + } else { + throw ElytronMessages.log.fileSystemRealmInvalidPasswordFormat(format, path, streamReader.getLocation().getLineNumber(), name); + } + } catch (InvalidKeySpecException | NoSuchAlgorithmException e) { + throw ElytronMessages.log.fileSystemRealmInvalidContent(path, streamReader.getLocation().getLineNumber(), name); + } + }); + } + + private void parseOtp(final List credentials, final XMLStreamReader streamReader) throws XMLStreamException, RealmUnavailableException { + String name = null; + String algorithm = null; + byte[] hash = null; + String seed = null; + int sequenceNumber = 0; + + final int attributeCount = streamReader.getAttributeCount(); + for (int i = 0; i < attributeCount; i ++) { + String namespace = streamReader.getAttributeNamespace(i); + if (namespace != null && !namespace.equals("")) { + throw ElytronMessages.log.fileSystemRealmInvalidContent(path, streamReader.getLocation().getLineNumber(), name); + } + final String localName = streamReader.getAttributeLocalName(i); + if ("name".equals(localName)) { + name = streamReader.getAttributeValue(i); + } else if ("algorithm".equals(localName)) { + algorithm = streamReader.getAttributeValue(i); + } else if ("hash".equals(localName)) { + hash = CodePointIterator.ofString(streamReader.getAttributeValue(i)).base64Decode(Base64Alphabet.STANDARD, false).drain(); + } else if ("seed".equals(localName)) { + seed = new String(CodePointIterator.ofString(streamReader.getAttributeValue(i)).base64Decode(Base64Alphabet.STANDARD, false).drain(), StandardCharsets.US_ASCII); + } else if ("sequence".equals(localName)) { + sequenceNumber = Integer.parseInt(streamReader.getAttributeValue(i)); + } else { + throw ElytronMessages.log.fileSystemRealmInvalidContent(path, streamReader.getLocation().getLineNumber(), name); + } + } + + if (streamReader.nextTag() != END_ELEMENT) { + throw ElytronMessages.log.fileSystemRealmInvalidContent(path, streamReader.getLocation().getLineNumber(), name); + } + + try { + if (algorithm == null) { + throw ElytronMessages.log.fileSystemRealmMissingAttribute("algorithm", path, streamReader.getLocation().getLineNumber(), name); + } + PasswordFactory passwordFactory = PasswordFactory.getInstance(algorithm, providers); + Password password = passwordFactory.generatePassword(new OneTimePasswordSpec(hash, seed, sequenceNumber)); + credentials.add(new PasswordCredential(password)); + } catch (InvalidKeySpecException e) { + throw ElytronMessages.log.fileSystemRealmInvalidOtpDefinition(path, streamReader.getLocation().getLineNumber(), name, e); + } catch (NoSuchAlgorithmException e) { + throw ElytronMessages.log.fileSystemRealmInvalidOtpAlgorithm(algorithm, path, streamReader.getLocation().getLineNumber(), name, e); + } + } + + private Attributes parseAttributes(final XMLStreamReader streamReader, final Version version) throws RealmUnavailableException, XMLStreamException { + final int attributeCount = streamReader.getAttributeCount(); + if (attributeCount > 0) { + throw ElytronMessages.log.fileSystemRealmInvalidContent(path, streamReader.getLocation().getLineNumber(), name); + } + int tag = streamReader.nextTag(); + if (tag == END_ELEMENT) { + return Attributes.EMPTY; + } + Attributes attributes = new MapAttributes(); + do { + if (! version.getNamespace().equals(streamReader.getNamespaceURI()) ) { + + // Mixed versions unsupported. + throw ElytronMessages.log.fileSystemRealmInvalidContent(path, streamReader.getLocation().getLineNumber(), name); + } + if ("attribute".equals(streamReader.getLocalName())) { + parseAttribute(streamReader, attributes); + } else { + throw ElytronMessages.log.fileSystemRealmInvalidContent(path, streamReader.getLocation().getLineNumber(), name); + } + } while (streamReader.nextTag() == START_ELEMENT); + return attributes; + } + + private void parseAttribute(final XMLStreamReader streamReader, final Attributes attributes) throws XMLStreamException, RealmUnavailableException { + String name = null; + String value = null; + final int attributeCount = streamReader.getAttributeCount(); + for (int i = 0; i < attributeCount; i++) { + String namespace = streamReader.getAttributeNamespace(i); + if (namespace != null && !namespace.equals("")) { + throw ElytronMessages.log.fileSystemRealmInvalidContent(path, streamReader.getLocation().getLineNumber(), this.name); + } + if ("name".equals(streamReader.getAttributeLocalName(i))) { + name = streamReader.getAttributeValue(i); + } else if ("value".equals(streamReader.getAttributeLocalName(i))) { + value = streamReader.getAttributeValue(i); + } else { + throw ElytronMessages.log.fileSystemRealmInvalidContent(path, streamReader.getLocation().getLineNumber(), this.name); + } + } + if (name == null) { + throw ElytronMessages.log.fileSystemRealmMissingAttribute("name", path, streamReader.getLocation().getLineNumber(), this.name); + } + if (value == null) { + throw ElytronMessages.log.fileSystemRealmMissingAttribute("value", path, streamReader.getLocation().getLineNumber(), this.name); + } + if (secretKey != null) { + try { + attributes.addLast(CipherUtil.decrypt(name, secretKey), CipherUtil.decrypt(value, secretKey)); + } catch (GeneralSecurityException e){ + throw ElytronMessages.log.fileSystemRealmDecryptionFailed(e); + } + } else { + attributes.addLast(name, value); + } + if (streamReader.nextTag() != END_ELEMENT) { + throw ElytronMessages.log.fileSystemRealmInvalidContent(path, streamReader.getLocation().getLineNumber(), this.name); + } + } + + private void consumeContent(final XMLStreamReader reader) throws XMLStreamException { + while (reader.hasNext()) { + switch (reader.next()) { + case START_ELEMENT: { + consumeContent(reader); + break; + } + case END_ELEMENT: { + return; + } + } + } + } + + private boolean isIntegrityValid() { + if (this.publicKey != null) { + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + dbf.setNamespaceAware(true); + Document doc; + try { + doc = dbf.newDocumentBuilder().parse(path.toString()); + } catch (SAXException | IOException | ParserConfigurationException e) { + return false; + } + return (validatePrincipalName(doc) && validateDigitalSignature(doc)); + } + return true; + } + + // Process for updating identity: + // 1. Validate current identity digital signature + // 2. Update identity with new data + // 3. Create new digital signature + private boolean validateDigitalSignature(Document doc) { + try { + NodeList nl = doc.getElementsByTagNameNS(XMLSignature.XMLNS, "Signature"); + if (nl.getLength() == 0) { + throw ElytronMessages.log.cannotFindSignature(path.toString()); + } + XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM"); + DOMValidateContext valContext = new DOMValidateContext(publicKey, nl.item(0)); + XMLSignature signature = fac.unmarshalXMLSignature(valContext); + boolean coreValidity = signature.validate(valContext); + ElytronMessages.log.tracef("FileSystemSecurityRealm - verification against signature for credential [%s] = %b", name, coreValidity); + return coreValidity; + } catch (IOException | MarshalException | XMLSignatureException e) { + ElytronMessages.log.tracef("FileSystemSecurityRealm - Error during verification. Signature for credential [%s] failed", name); + return false; + } + } + + private boolean validatePrincipalName(Document doc) { + NodeList nl = doc.getElementsByTagName("principal"); + if (nl.getLength() == 0) { + ElytronMessages.log.tracef("FileSystemSecurityRealm - verification against principal for credential [%s] = %b", name, false); + return false; + } + String principalName = nl.item(0).getAttributes().getNamedItem("name").getNodeValue(); + if (secretKey != null) { + try { + principalName = CipherUtil.decrypt(principalName, secretKey); + } catch (GeneralSecurityException e) { + ElytronMessages.log.tracef("FileSystemSecurityRealm - verification against principal for credential [%s] = %b", name, false); + return false; + } + } + boolean validity = Objects.equals(principalName, name); + ElytronMessages.log.tracef("FileSystemSecurityRealm - verification against principal for credential [%s] = %b", name, validity); + return validity; + } + + private void writeDigitalSignature(Path path, String name) throws RealmUnavailableException { + try { + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + dbf.setNamespaceAware(true); + DocumentBuilder builder = dbf.newDocumentBuilder(); + Document doc = builder.parse(Files.newInputStream(path)); + Element elem = doc.getDocumentElement(); + NodeList signatureNode = doc.getElementsByTagName("Signature"); + if (signatureNode.getLength() > 0) { + Node sig = signatureNode.item(0); + elem.removeChild(sig); + } + DOMSignContext dsc = new DOMSignContext(this.privateKey, elem); + XMLSignatureFactory fac = XMLSignatureFactory.getInstance("DOM"); + Reference ref = fac.newReference + ("", fac.newDigestMethod(DigestMethod.SHA256, null), + Collections.singletonList + (fac.newTransform(Transform.ENVELOPED, + (TransformParameterSpec) null)), null, null); + String signatureMethod = ""; + // https://issues.redhat.com/browse/ELY-2346 + // Once JDK 8 support is removed use the javax.xml.crypto.dsig.SignatureMethod to set these signatureMethods + switch (this.publicKey.getAlgorithm()) { + case "DSA": + signatureMethod = "http://www.w3.org/2009/xmldsig11#dsa-sha256"; + break; + case "RSA": + signatureMethod = "http://www.w3.org/2001/04/xmldsig-more#rsa-sha256"; + break; + case "HMAC": + signatureMethod = "http://www.w3.org/2001/04/xmldsig-more#hmac-sha256"; + break; + case "EC": + signatureMethod = "http://www.w3.org/2001/04/xmldsig-more#ecdsa-sha256"; + break; + } + SignedInfo si = fac.newSignedInfo + (fac.newCanonicalizationMethod + (CanonicalizationMethod.INCLUSIVE, + (C14NMethodParameterSpec) null), + fac.newSignatureMethod(signatureMethod, null), + Collections.singletonList(ref)); + KeyInfoFactory kif = fac.getKeyInfoFactory(); + KeyValue kv = kif.newKeyValue(this.publicKey); + KeyInfo ki = kif.newKeyInfo(Collections.singletonList(kv)); + XMLSignature signature = fac.newXMLSignature(si, ki); + signature.sign(dsc); + TransformerFactory transformerFactory = TransformerFactory.newInstance(); + Transformer transformer = transformerFactory.newTransformer(); + DOMSource source = new DOMSource(doc); + FileWriter writer = new FileWriter(String.valueOf(path)); + StreamResult result = new StreamResult(writer); + transformer.transform(source, result); + ElytronMessages.log.tracef("FileSystemSecurityRealm - signature against file updated [%s]", name); + writer.close(); + } catch (ParserConfigurationException | IOException | NoSuchAlgorithmException | InvalidAlgorithmParameterException | + KeyException | XMLSignatureException | MarshalException | TransformerException | SAXException e) { + ElytronMessages.log.tracef("FileSystemSecurityRealm - Error during signature generation against identity [%s]", name); + throw ElytronMessages.log.unableToGenerateSignature(String.valueOf(this.path)); + } + } + } + + protected static final class LoadedIdentity { + private final String name; + private final List credentials; + private final Attributes attributes; + private final Encoding hashEncoding; + + LoadedIdentity(final String name, final List credentials, final Attributes attributes, final Encoding hashEncoding) { + this.name = name; + this.credentials = credentials; + this.attributes = attributes; + this.hashEncoding = hashEncoding; + } + + public String getName() { + return name; + } + + public Attributes getAttributes() { + return attributes; + } + + List getCredentials() { + return credentials; + } + + public Encoding getHashEncoding() { + return hashEncoding; + } + + } + + static class AutoCloseableXMLStreamReaderHolder implements AutoCloseable { + private final XMLStreamReader xmlStreamReader; + + AutoCloseableXMLStreamReaderHolder(final XMLStreamReader xmlStreamReader) { + this.xmlStreamReader = xmlStreamReader; + } + + public void close() throws XMLStreamException { + xmlStreamReader.close(); + } + + public XMLStreamReader getXmlStreamReader() { + return xmlStreamReader; + } + } + + static class AutoCloseableXMLStreamWriterHolder implements AutoCloseable { + private final XMLStreamWriter xmlStreamWriter; + + AutoCloseableXMLStreamWriterHolder(final XMLStreamWriter xmlStreamWriter) { + this.xmlStreamWriter = xmlStreamWriter; + } + + public void close() throws XMLStreamException { + xmlStreamWriter.close(); + } + + public XMLStreamWriter getXmlStreamWriter() { + return xmlStreamWriter; + } + } + + public static class IntegrityResult { + /** + * The result of the integrity check and invalid identities. + */ + private final boolean valid; + private final ArrayList identityNames; + + IntegrityResult(final boolean valid, final ArrayList identityNames) { + this.valid = valid; + this.identityNames = identityNames; + } + + /** + * The validity of the integrity check. + * + * @return {@code true} if the integrity check was successful. + */ + public boolean isValid() { + return valid; + } + + /** + * Returns a string of the identities that were found to be invalid. + * + * @return the list of identities that were found to be invalid. + */ + public String getIdentityNames() { + return identityNames.toString(); + } + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/realm/FileSystemSecurityRealmBuilder.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/realm/FileSystemSecurityRealmBuilder.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/realm/FileSystemSecurityRealmBuilder.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,197 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2021 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.auth.realm; + +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.nio.file.Path; +import java.security.PrivateKey; +import java.security.Provider; +import java.security.PublicKey; +import java.util.function.Supplier; + +import javax.crypto.SecretKey; + +import org.wildfly.common.Assert; +import org.wildfly.security.auth.server.NameRewriter; +import org.wildfly.security.password.spec.Encoding; + + +/** + * A builder class that creates {@link FileSystemSecurityRealm} instances. + * + * @author Ashpan Raskar + */ +public class FileSystemSecurityRealmBuilder { + + private Path root; + private NameRewriter nameRewriter; + private int levels = 2; + private boolean encoded = true; + private Charset hashCharset; + private Encoding hashEncoding; + private SecretKey secretKey; + private PrivateKey privateKey; + private PublicKey publicKey; + private Supplier providers; + + FileSystemSecurityRealmBuilder() { + } + + /** + * Set the root path to be used by the realm. + * + * @param root the root path of the identity store (must not be {@code null}) + * @return this builder.enc + */ + public FileSystemSecurityRealmBuilder setRoot(final Path root) { + Assert.checkNotNullParam("root", root); + this.root = root; + return this; + } + + /** + * Set the name rewriter to be used by the realm. + * + * @param nameRewriter the name rewriter to apply to looked up names (must not be {@code null}) + * @return this builder. + */ + public FileSystemSecurityRealmBuilder setNameRewriter(final NameRewriter nameRewriter) { + Assert.checkNotNullParam("nameRewriter", nameRewriter); + this.nameRewriter = nameRewriter; + return this; + } + + /** + * Set the number of levels to be used by the realm. + * + * @param levels the number of levels of directory hashing to apply + * @return this builder. + */ + public FileSystemSecurityRealmBuilder setLevels(final int levels) { + Assert.checkMinimumParameter("levels", 0, levels); + this.levels = levels; + return this; + } + + /** + * Set whether the identity name should be encoded for the filename in the realm. + * + * @param encoded whether identity names should be BASE32 encoded before using as filename (only applies if the security realm is unencrypted) + * @return this builder. + */ + public FileSystemSecurityRealmBuilder setEncoded(final boolean encoded) { + this.encoded = encoded; + return this; + } + + /** + * Set the character set to be used by the realm. + * + * @param hashCharset the character set to use when converting password strings to a byte array. Uses UTF-8 by default. (must not be {@code null}) + * @return this builder. + */ + public FileSystemSecurityRealmBuilder setHashCharset(final Charset hashCharset) { + Assert.checkNotNullParam("hashCharset", hashCharset); + this.hashCharset = hashCharset; + return this; + } + + /** + * Set the string format for hashed passwords to be used by the realm. + * + * @param hashEncoding the string format for the hashed passwords. Uses Base64 by default. (must not be {@code null}) + * @return this builder. + */ + public FileSystemSecurityRealmBuilder setHashEncoding(final Encoding hashEncoding) { + Assert.checkNotNullParam("hashEncoding", hashEncoding); + this.hashEncoding = hashEncoding; + return this; + } + + /** + * Set the SecretKey to be used by the realm. + * + * @param secretKey the symmetric SecretKey used to encrypt and decrypt the Security Realm (must not be {@code null}) + * @return this builder. + */ + public FileSystemSecurityRealmBuilder setSecretKey(final SecretKey secretKey) { + Assert.checkNotNullParam("secretKey", secretKey); + this.secretKey = secretKey; + return this; + } + + /** + * Set the providers to be used by the realm. + * + * @param providers the provider to be used (must not be {@code null}) + * @return this builder. + */ + public FileSystemSecurityRealmBuilder setProviders(final Supplier providers) { + Assert.checkNotNullParam("providers", providers); + this.providers = providers; + return this; + } + + /** + * Set the PrivateKey to be used by the realm. + * + * @param privateKey the asymmetric PrivateKey used to sign the identity files used for file integrity (must not be {@code null}) + * @return this builder. + */ + public FileSystemSecurityRealmBuilder setPrivateKey(final PrivateKey privateKey) { + Assert.checkNotNullParam("privateKey", privateKey); + this.privateKey = privateKey; + return this; + } + + /** + * Set the PublicKey to be used by the realm. + * + * @param publicKey the asymmetric PublicKey used to verify the identity files used for file integrity (must not be {@code null}) + * @return this builder. + */ + public FileSystemSecurityRealmBuilder setPublicKey(final PublicKey publicKey) { + Assert.checkNotNullParam("publicKey", publicKey); + this.publicKey = publicKey; + return this; + } + + /** + * Builds a new {@link FileSystemSecurityRealm} instance based on configuration defined for this {@link FileSystemSecurityRealmBuilder} instance. + * + * @return the built realm + */ + public FileSystemSecurityRealm build() { + encoded = secretKey == null && encoded; + if (nameRewriter == null) { + nameRewriter = NameRewriter.IDENTITY_REWRITER; + } + if (hashEncoding == null) { + hashEncoding = Encoding.BASE64; + } + if (hashCharset == null) { + hashCharset = StandardCharsets.UTF_8; + } + if (privateKey == null ^ publicKey == null) { + throw ElytronMessages.log.invalidKeyPairArgument(root.toString()); + } + + return new FileSystemSecurityRealm(root, nameRewriter, levels, encoded, hashEncoding, hashCharset, providers, secretKey, privateKey, publicKey); + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/realm/IdentitySharedExclusiveLock.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/realm/IdentitySharedExclusiveLock.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/realm/IdentitySharedExclusiveLock.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,148 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2016 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.auth.realm; + +/** + * A simple shared/exclusive lock for a realm identity. + * + * @author Farah Juma + */ +public class IdentitySharedExclusiveLock { + + private int sharedHoldCount; + private boolean isExclusiveLocked; + private int exclusiveRequests; + + /** + * Acquire the exclusive lock. An invocation of this method will block until the lock can be acquired. + * + * @return a lock object representing the newly acquired lock + */ + public synchronized IdentityLock lockExclusive() { + boolean interrupted = false; + try { + exclusiveRequests++; + while ((sharedHoldCount > 0) || isExclusiveLocked) { + try { + wait(); + } catch (InterruptedException e) { + interrupted = true; + } + } + isExclusiveLocked = true; + exclusiveRequests--; + return new IdentityLock(true); + } finally { + if (interrupted) { + Thread.currentThread().interrupt(); + } + } + + } + + /** + * Acquire a shared lock. An invocation of this method will block until the lock can be acquired. + * + * @return a lock object representing the newly acquired lock + */ + public synchronized IdentityLock lockShared() { + boolean interrupted = false; + try { + while (isExclusiveLocked || (exclusiveRequests > 0)) { + try { + wait(); + } catch (InterruptedException e) { + interrupted = true; + } + } + sharedHoldCount++; + return new IdentityLock(false); + } finally { + if (interrupted) { + Thread.currentThread().interrupt(); + } + } + } + + private synchronized void release(IdentityLock identityLock) { + if (identityLock.isExclusive()) { + isExclusiveLocked = false; + notifyAll(); + } else { + if (--sharedHoldCount == 0) { + notifyAll(); + } + } + + } + + /** + * Class that represents a lock on a realm identity. A lock object is created each time a lock is + * acquired on a realm identity via {@link IdentitySharedExclusiveLock#lockExclusive()} or + * {@link IdentitySharedExclusiveLock#lockShared()}. + */ + public class IdentityLock implements AutoCloseable { + + private final boolean exclusive; + private volatile boolean valid = true; + + /** + * Construct a new instance. + * + * @param exclusive {@code true} if this lock is exclusive, {@code false} if this lock is shared + */ + public IdentityLock(final boolean exclusive) { + this.exclusive = exclusive; + } + + /** + * Release this lock. Invoking this method has no effect if this lock is invalid. + */ + public synchronized void release() { + if (valid) { + IdentitySharedExclusiveLock.this.release(this); + valid = false; + } + } + + @Override + public void close() { + release(); + } + + /** + * Determine whether this lock is exclusive or shared. + * + * @return {@code true} if this lock is exclusive, {@code false} if this lock is shared + */ + public boolean isExclusive() { + return exclusive; + } + + /** + * Determine whether this lock is valid. A lock starts out valid and becomes invalid when it + * is released via {@link #release()} or {@link #close()}. + * + * @return {@code true} if this lock is valid, {@code false} otherwise + */ + public boolean isValid() { + return valid; + } + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/realm/IntegrityException.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/realm/IntegrityException.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/realm/IntegrityException.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,71 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2022 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.auth.realm; + +import java.io.IOException; + +/** + * Exception to indicate a general failure related to the Integrity Verification of the Filesystem Realm. + * + * @author Ashpan Raskar + */ +public class IntegrityException extends IOException { + + + private static final long serialVersionUID = 8889252552074803941L; + + /** + * Constructs a new {@code IntegrityException} instance. The message is left blank ({@code null}), and no + * cause is specified. + */ + public IntegrityException() { + } + + /** + * Constructs a new {@code IntegrityException} instance with an initial message. No cause is specified. + * + * @param msg the message + */ + public IntegrityException(final String msg) { + super(msg); + } + + /** + * Constructs a new {@code IntegrityException} instance with an initial cause. If a non-{@code null} cause + * is specified, its message is used to initialize the message of this {@code IntegrityException}; otherwise + * the message is left blank ({@code null}). + * + * @param cause the cause + */ + public IntegrityException(final Throwable cause) { + super(cause); + } + + /** + * Constructs a new {@code IntegrityException} instance with an initial message and cause. + * + * @param msg the message + * @param cause the cause + */ + public IntegrityException(final String msg, final Throwable cause) { + super(msg, cause); + } + +} + Index: 3rdParty_sources/elytron/org/wildfly/security/auth/realm/JaasSecurityRealm.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/realm/JaasSecurityRealm.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/realm/JaasSecurityRealm.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,418 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2014 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.auth.realm; + +import static org.wildfly.security.auth.realm.ElytronMessages.log; + +import javax.security.auth.Subject; +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.NameCallback; +import javax.security.auth.callback.PasswordCallback; +import javax.security.auth.callback.UnsupportedCallbackException; +import javax.security.auth.login.Configuration; +import javax.security.auth.login.LoginContext; +import javax.security.auth.login.LoginException; + +import java.io.File; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.net.URI; +import java.nio.file.Paths; +import java.security.NoSuchAlgorithmException; +import java.security.Principal; +import java.security.Security; +import java.security.URIParameter; +import java.security.spec.AlgorithmParameterSpec; + +import org.wildfly.common.Assert; +import org.wildfly.security.auth.callback.CredentialCallback; +import org.wildfly.security.auth.principal.NamePrincipal; +import org.wildfly.security.authz.Attributes; +import org.wildfly.security.authz.AuthorizationIdentity; +import org.wildfly.security.auth.server.RealmIdentity; +import org.wildfly.security.auth.server.RealmUnavailableException; +import org.wildfly.security.auth.server.SecurityRealm; +import org.wildfly.security.auth.SupportLevel; +import org.wildfly.security.authz.MapAttributes; +import org.wildfly.security.credential.Credential; +import org.wildfly.security.evidence.Evidence; +import org.wildfly.security.evidence.PasswordGuessEvidence; + +/** + * A JAAS based {@link SecurityRealm} implementation. + * + * @author Stefan Guilhen + */ +public class JaasSecurityRealm implements SecurityRealm { + + private static final String DEFAULT_CONFIGURATION_POLICY_TYPE = "JavaLoginConfig"; + private final URI jaasConfigFilePath; + private final String entry; + private final CallbackHandler handler; + private final ClassLoader classLoader; + + /** + * Construct a new instance. + * + * @param entry JAAS configuration file entry (must not be {@code null}) + */ + public JaasSecurityRealm(final String entry) { + this(entry, (String) null); + } + + /** + * Construct a new instance. + * + * @param entry JAAS configuration file entry (must not be {@code null}) + * @param classLoader classLoader to use with LoginContext, this class loader must contain LoginModule CallbackHandler classes + */ + public JaasSecurityRealm(final String entry, final ClassLoader classLoader) { + this(entry, null, classLoader); + } + + /** + * Construct a new instance. + * + * @param entry JAAS configuration file entry (must not be {@code null}) + * @param jaasConfigFilePath path to JAAS configuration file + */ + public JaasSecurityRealm(final String entry, final String jaasConfigFilePath) { + this(entry, jaasConfigFilePath, null); + } + + /** + * Construct a new instance. + * + * @param entry JAAS configuration file entry (must not be {@code null}) + * @param jaasConfigFilePath path to JAAS configuration file + * @param classLoader classLoader to use with LoginContext, this class loader must contain LoginModule CallbackHandler classes + */ + public JaasSecurityRealm(final String entry, final String jaasConfigFilePath, final ClassLoader classLoader) { + this(entry, jaasConfigFilePath, classLoader, null); + } + + /** + * Construct a new instance. + * + * @param entry JAAS configuration file entry (must not be {@code null}) + * @param jaasConfigFilePath path to JAAS configuration file + * @param callbackHandler callbackHandler to pass to LoginContext + * @param classLoader classLoader to use with LoginContext, this class loader must contain LoginModule CallbackHandler classes + */ + public JaasSecurityRealm(final String entry, final String jaasConfigFilePath, final ClassLoader classLoader, final CallbackHandler callbackHandler) { + Assert.checkNotNullParam("entry", entry); + if (jaasConfigFilePath != null) { + this.jaasConfigFilePath = Paths.get(jaasConfigFilePath).toUri(); + } else { + this.jaasConfigFilePath = null; + } + this.entry = entry; + this.handler = callbackHandler; + if (classLoader != null) { + this.classLoader = classLoader; + } else { + this.classLoader = Thread.currentThread().getContextClassLoader(); + } + } + + @Override + public RealmIdentity getRealmIdentity(final Principal principal) { + if (principal instanceof NamePrincipal) { + return new JaasRealmIdentity(principal); + } else { + NamePrincipal namePrincipal = NamePrincipal.from(principal); + return namePrincipal != null ? new JaasRealmIdentity(namePrincipal) : RealmIdentity.NON_EXISTENT; + } + } + + @Override + public SupportLevel getCredentialAcquireSupport(final Class credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException { + Assert.checkNotNullParam("credentialType", credentialType); + return SupportLevel.UNSUPPORTED; + } + + @Override + public SupportLevel getEvidenceVerifySupport(final Class evidenceType, final String algorithmName) throws RealmUnavailableException { + Assert.checkNotNullParam("evidenceType", evidenceType); + return SupportLevel.POSSIBLY_SUPPORTED; + } + + private class JaasRealmIdentity implements RealmIdentity { + + private final Principal principal; + private LoginContext loginContext; + private Subject subject; + + private JaasRealmIdentity(final Principal principal) { + this.principal = principal; + } + + public Principal getRealmIdentityPrincipal() { + return principal; + } + + public Subject getSubject() { + return subject; + } + + @Override + public SupportLevel getCredentialAcquireSupport(final Class credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException { + return JaasSecurityRealm.this.getCredentialAcquireSupport(credentialType, algorithmName, parameterSpec); + } + + @Override + public C getCredential(final Class credentialType) throws RealmUnavailableException { + return getCredential(credentialType, null); + } + + @Override + public C getCredential(final Class credentialType, final String algorithmName) throws RealmUnavailableException { + return getCredential(credentialType, algorithmName, null); + } + + @Override + public C getCredential(final Class credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException { + return null; + } + + @Override + public SupportLevel getEvidenceVerifySupport(final Class evidenceType, final String algorithmName) throws RealmUnavailableException { + Assert.checkNotNullParam("evidenceType", evidenceType); + return JaasSecurityRealm.this.getEvidenceVerifySupport(evidenceType, algorithmName); + } + + @Override + public boolean verifyEvidence(final Evidence evidence) throws RealmUnavailableException { + Assert.checkNotNullParam("evidence", evidence); + this.subject = null; + boolean successfulLogin; + ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader(); + try { + if (classLoader != null) { + Thread.currentThread().setContextClassLoader(classLoader); + } + final CallbackHandler callbackHandler = createCallbackHandler(principal, evidence); + final Subject subject = new Subject(); + loginContext = createLoginContext(entry, subject, callbackHandler); + log.tracef("Trying to authenticate subject %s using LoginContext %s using JaasSecurityRealm", principal, loginContext); + try { + loginContext.login(); + successfulLogin = true; + this.subject = loginContext.getSubject(); + } catch (LoginException loginException) { + successfulLogin = false; + ElytronMessages.log.debugInfoJaasAuthenticationFailure(principal, loginException); + } + } finally { + Thread.currentThread().setContextClassLoader(oldClassLoader); + } + return successfulLogin; + } + + public boolean exists() { + /* we don't really know that the identity exists, but we know that there is always + * an authorization identity so that's as good as {@code true} + */ + return true; + } + + @Override + public AuthorizationIdentity getAuthorizationIdentity() throws RealmUnavailableException { + return JaasAuthorizationIdentity.fromSubject(subject); + } + + @Override + public void dispose() { + // call logout in order to empty the subject + ClassLoader oldClassLoader = Thread.currentThread().getContextClassLoader(); + try { + try { + if (classLoader != null) { + Thread.currentThread().setContextClassLoader(classLoader); + } + if (loginContext != null) { + loginContext.logout(); + } + } catch (LoginException e) { + ElytronMessages.log.debugInfoJaasLogoutFailure(this.principal, e); + } + } finally { + Thread.currentThread().setContextClassLoader(oldClassLoader); + } + } + + /** + * @param entry login configuration file entry + * @param subject classLoader to use with LoginContext, this class loader must contain LoginModule CallbackHandler classes + * @param callbackHandler callbackHandler to pass to LoginContext + * @return the instance of LoginContext + * @throws RealmUnavailableException + */ + private LoginContext createLoginContext(final String entry, final Subject subject, final CallbackHandler callbackHandler) throws RealmUnavailableException { + if (jaasConfigFilePath != null) { + File file = new File(jaasConfigFilePath); + if (!file.exists() && !file.isDirectory()) { + throw ElytronMessages.log.failedToLoadJaasConfigFile(); + } + } + try { + if (jaasConfigFilePath == null) { + return new LoginContext(entry, subject, callbackHandler); + } else { + return new LoginContext(entry, subject, callbackHandler, Configuration.getInstance(DEFAULT_CONFIGURATION_POLICY_TYPE, new URIParameter(jaasConfigFilePath))); + } + } catch (LoginException | NoSuchAlgorithmException le) { + throw ElytronMessages.log.failedToCreateLoginContext(le); + } + } + + private CallbackHandler createCallbackHandler(final Principal principal, final Evidence evidence) { + if (handler != null) { + try { + final CallbackHandler callbackHandler = handler.getClass().getConstructor().newInstance(); + // custom handlers were allowed in the past as long as they had a public setSecurityInfo method. Use this method if it exists + final Method setSecurityInfo = handler.getClass().getMethod("setSecurityInfo", Principal.class, Object.class); + setSecurityInfo.invoke(callbackHandler, principal, evidence); + return callbackHandler; + } catch (InstantiationException | IllegalAccessException | NoSuchMethodException | InvocationTargetException e) { + // ignore if this method does not exist + return handler; + } + } else if (Security.getProperty("auth.login.defaultCallbackHandler") != null) { + // security property "auth.login.defaultCallbackHandler" is not null so LoginContext will initialize it itself + return null; + } else { + return new JaasSecurityRealmDefaultCallbackHandler(principal, evidence); + } + } + + } + + /** + * Default CallbackHandler passed to the LoginContext when none is provided to JAAS security realm and none is configured in the "auth.login.defaultCallbackHandler" security property. + */ + private static class JaasSecurityRealmDefaultCallbackHandler implements CallbackHandler { + + private final Principal principal; + private final Object evidence; + + private JaasSecurityRealmDefaultCallbackHandler(final Principal principal, final Evidence evidence) { + this.principal = principal; + this.evidence = evidence; + } + + @Override + public void handle(Callback[] callbacks) throws UnsupportedCallbackException { + Assert.checkNotNullParam("callbacks", callbacks); + for (Callback callback : callbacks) { + if (callback instanceof NameCallback) { + NameCallback nc = (NameCallback) callback; + if (principal != null) + nc.setName(principal.getName()); + } else if (callback instanceof PasswordCallback) { + if (evidence instanceof PasswordGuessEvidence) { + ((PasswordCallback) callback).setPassword(((PasswordGuessEvidence) evidence).getGuess()); + } else { + PasswordCallback pc = (PasswordCallback) callback; + char[] password = getPassword(); + if (password != null) + pc.setPassword(password); + } + } else if (callback instanceof CredentialCallback && evidence instanceof Credential) { + final CredentialCallback credentialCallback = (CredentialCallback) callback; + Credential credential = (Credential) evidence; + if (credentialCallback.isCredentialSupported(credential)) { + credentialCallback.setCredential(credential); + } + } else { + throw ElytronMessages.log.unableToHandleCallback(callback, this.getClass().getName(), callback.getClass().getCanonicalName()); + } + } + } + + /** + * Source: A utility method for obtaining of password taken from + * https://github.com/picketbox/picketbox/blob/master/security-jboss-sx/jbosssx/src/main/java/org/jboss/security/auth/callback/JBossCallbackHandler.java + * on November 2021 + *

+ * Try to convert the credential value into a char[] using the + * first of the following attempts which succeeds: + *

+ * 1. Check for instanceof char[] + * 2. Check for instanceof String and then use toCharArray() + * 3. See if credential has a toCharArray() method and use it + * 4. Use toString() followed by toCharArray(). + * + * @return a char[] representation of the credential. + */ + private char[] getPassword() { + char[] password = null; + if (evidence instanceof char[]) { + password = (char[]) evidence; + } else if (evidence instanceof String) { + String s = (String) evidence; + password = s.toCharArray(); + } else { + try { + Class[] types = {}; + Method m = evidence.getClass().getMethod("toCharArray", types); + Object[] args = {}; + password = (char[]) m.invoke(evidence, args); + } catch (Exception e) { + if (evidence != null) { + String s = evidence.toString(); + password = s.toCharArray(); + } + } + } + return password; + } + } + + /** + * A JAAS realm's authorization identity. Roles are mapped from all Subject's principals with the following rule: + * key of the attribute is principal's simple classname and the value is principal's name + */ + private static class JaasAuthorizationIdentity implements AuthorizationIdentity { + + private MapAttributes attributes; + + private static JaasAuthorizationIdentity fromSubject(final Subject subject) { + MapAttributes attributes = new MapAttributes(); + // map all subject's principals to attributes with the following rule: + // key of the attribute is principal's simple classname and the value is principal's name + if (subject != null) { + for (Principal principal : subject.getPrincipals()) { + attributes.addLast(principal.getClass().getSimpleName(), principal.getName()); + } + } + return new JaasAuthorizationIdentity(attributes); + } + + private JaasAuthorizationIdentity(MapAttributes attributes) { + this.attributes = attributes; + } + + @Override + public Attributes getAttributes() { + return attributes; + } + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/realm/KeyStoreBackedSecurityRealm.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/realm/KeyStoreBackedSecurityRealm.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/realm/KeyStoreBackedSecurityRealm.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,213 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2014 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.auth.realm; + +import static org.wildfly.security.auth.realm.ElytronMessages.log; +import static org.wildfly.security.provider.util.ProviderUtil.INSTALLED_PROVIDERS; + +import java.security.KeyStore; +import java.security.KeyStoreException; +import java.security.NoSuchAlgorithmException; +import java.security.Principal; +import java.security.Provider; +import java.security.UnrecoverableEntryException; +import java.security.cert.Certificate; +import java.security.cert.X509Certificate; +import java.security.spec.AlgorithmParameterSpec; +import java.util.Enumeration; +import java.util.function.Supplier; + +import javax.security.auth.x500.X500Principal; + +import org.wildfly.common.Assert; +import org.wildfly.security.auth.principal.NamePrincipal; +import org.wildfly.security.authz.AuthorizationIdentity; +import org.wildfly.security.auth.server.RealmIdentity; +import org.wildfly.security.auth.server.RealmUnavailableException; +import org.wildfly.security.auth.server.SecurityRealm; +import org.wildfly.security.auth.SupportLevel; +import org.wildfly.security.credential.Credential; +import org.wildfly.security.evidence.Evidence; +import org.wildfly.security.x500.util.X500PrincipalUtil; + +/** + * A {@link KeyStore} backed {@link SecurityRealm} implementation. + * + * @author David M. Lloyd + * @author Darran Lofthouse + */ +public class KeyStoreBackedSecurityRealm implements SecurityRealm { + + private final Supplier providers; + private final KeyStore keyStore; + + /** + * Construct a new instance. + * + * @param keyStore the keystore to use to back this realm + */ + public KeyStoreBackedSecurityRealm(final KeyStore keyStore) { + this(keyStore, INSTALLED_PROVIDERS); + } + + /** + * Construct a new instance. + * + * @param keyStore the keystore to use to back this realm + * @param providers A supplier of providers for use by this realm + */ + public KeyStoreBackedSecurityRealm(final KeyStore keyStore, final Supplier providers) { + Assert.checkNotNullParam("keyStore", keyStore); + this.keyStore = keyStore; + this.providers = providers; + } + + @Override + public RealmIdentity getRealmIdentity(final Principal principal) throws RealmUnavailableException { + final X500Principal x500Principal = X500PrincipalUtil.asX500Principal(principal); + if (x500Principal != null) { + log.tracef("KeyStoreRealm: obtaining certificate by X500Principal [%s]", x500Principal); + final KeyStore keyStore = this.keyStore; + try { + final Enumeration aliases = keyStore.aliases(); + while (aliases.hasMoreElements()) { + final String alias = aliases.nextElement(); + if (keyStore.isCertificateEntry(alias)) { + final Certificate certificate = keyStore.getCertificate(alias); + if (certificate instanceof X509Certificate && x500Principal.equals(X500PrincipalUtil.asX500Principal(((X509Certificate) certificate).getSubjectX500Principal()))) { + log.tracef("KeyStoreRealm: certificate found by X500Principal in alias [%s]", alias); + return new KeyStoreRealmIdentity(alias); + } + } + } + } catch (KeyStoreException e) { + throw log.failedToReadKeyStore(e); + } + log.tracef("KeyStoreRealm: certificate not found by X500Principal"); + return RealmIdentity.NON_EXISTENT; + } else if (NamePrincipal.isConvertibleTo(principal)) { + String name = principal.getName(); + log.tracef("KeyStoreRealm: obtaining certificate by alias [%s]", name); + return new KeyStoreRealmIdentity(name); + } else { + log.tracef("KeyStoreRealm: conversion of principal [%s] to X500Principal failed", principal); + return RealmIdentity.NON_EXISTENT; + } + } + + @Override + public SupportLevel getCredentialAcquireSupport(final Class credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException { + Assert.checkNotNullParam("credentialType", credentialType); + return SupportLevel.POSSIBLY_SUPPORTED; + } + + @Override + public SupportLevel getEvidenceVerifySupport(final Class evidenceType, final String algorithmName) throws RealmUnavailableException { + Assert.checkNotNullParam("evidenceType", evidenceType); + return SupportLevel.POSSIBLY_SUPPORTED; + } + + private class KeyStoreRealmIdentity implements RealmIdentity { + + private final String name; + + private KeyStoreRealmIdentity(final String name) { + this.name = name; + } + + public Principal getRealmIdentityPrincipal() { + return new NamePrincipal(name); + } + + @Override + public SupportLevel getCredentialAcquireSupport(final Class credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException { + final KeyStore.Entry entry = getEntry(name); + if (entry == null) return SupportLevel.UNSUPPORTED; + final Credential credential = Credential.fromKeyStoreEntry(entry); + return credential != null && credential.matches(credentialType, algorithmName, parameterSpec) ? SupportLevel.SUPPORTED : SupportLevel.UNSUPPORTED; + } + + @Override + public C getCredential(final Class credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException { + Assert.checkNotNullParam("credentialType", credentialType); + final KeyStore.Entry entry = getEntry(name); + if (entry == null) return null; + final Credential credential = Credential.fromKeyStoreEntry(entry); + return credential != null ? credential.castAs(credentialType, algorithmName, parameterSpec) : null; + } + + @Override + public C getCredential(final Class credentialType, final String algorithmName) throws RealmUnavailableException { + return getCredential(credentialType, algorithmName, null); + } + + @Override + public C getCredential(final Class credentialType) throws RealmUnavailableException { + return getCredential(credentialType, null); + } + + @Override + public AuthorizationIdentity getAuthorizationIdentity() { + return AuthorizationIdentity.EMPTY; + } + + @Override + public SupportLevel getEvidenceVerifySupport(final Class evidenceType, final String algorithmName) throws RealmUnavailableException { + final KeyStore.Entry entry = getEntry(name); + if (entry == null) return SupportLevel.UNSUPPORTED; + final Credential credential = Credential.fromKeyStoreEntry(entry); + if (credential != null && credential.canVerify(evidenceType, algorithmName)) { + log.tracef("KeyStoreRealm: verification supported using alias [%s]", name); + return SupportLevel.SUPPORTED; + } + log.tracef("KeyStoreRealm: verification unsupported - unsupported entry type of alias [%s]", name); + return SupportLevel.UNSUPPORTED; + } + + @Override + public boolean verifyEvidence(final Evidence evidence) throws RealmUnavailableException { + final KeyStore.Entry entry = getEntry(name); + if (entry == null) return false; + final Credential credential = Credential.fromKeyStoreEntry(entry); + if (credential != null && credential.canVerify(evidence) && credential.verify(providers, evidence)) { + log.tracef("KeyStoreRealm: verification succeed for alias [%s]", name); + return true; + } + log.tracef("KeyStoreRealm: verification failed - rejected by credential from alias [%s]", name); + return false; + } + + public boolean exists() throws RealmUnavailableException { + return getEntry(name) != null; + } + + private KeyStore.Entry getEntry(String name) { + try { + KeyStore.Entry entry = keyStore.getEntry(name, null); + if (entry == null) { + log.tracef("KeyStoreRealm: alias [%s] does not exist in KeyStore", name); + } + return entry; + } catch (NoSuchAlgorithmException | UnrecoverableEntryException | KeyStoreException e) { + log.tracef(e, "KeyStoreRealm: Obtaining entry [%s] from KeyStore failed", name); + return null; + } + } + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/realm/LegacyPropertiesSecurityRealm.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/realm/LegacyPropertiesSecurityRealm.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/realm/LegacyPropertiesSecurityRealm.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,592 @@ +/* + * 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.auth.realm; + +import static org.wildfly.security.auth.realm.ElytronMessages.log; +import static org.wildfly.security.password.interfaces.ClearPassword.ALGORITHM_CLEAR; +import static org.wildfly.security.password.interfaces.DigestPassword.ALGORITHM_DIGEST_MD5; +import static org.wildfly.security.provider.util.ProviderUtil.INSTALLED_PROVIDERS; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.Principal; +import java.security.Provider; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.InvalidKeySpecException; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Properties; +import java.util.Set; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Supplier; + +import org.wildfly.common.Assert; +import org.wildfly.common.codec.DecodeException; +import org.wildfly.common.iteration.ByteIterator; +import org.wildfly.common.iteration.CodePointIterator; +import org.wildfly.security.auth.principal.NamePrincipal; +import org.wildfly.security.auth.server.RealmIdentity; +import org.wildfly.security.auth.server.RealmUnavailableException; +import org.wildfly.security.auth.server.SecurityRealm; +import org.wildfly.security.auth.SupportLevel; +import org.wildfly.security.authz.AuthorizationIdentity; +import org.wildfly.security.authz.MapAttributes; +import org.wildfly.security.credential.Credential; +import org.wildfly.security.credential.PasswordCredential; +import org.wildfly.security.evidence.Evidence; +import org.wildfly.security.evidence.PasswordGuessEvidence; +import org.wildfly.security.password.Password; +import org.wildfly.security.password.PasswordFactory; +import org.wildfly.security.password.spec.ClearPasswordSpec; +import org.wildfly.security.password.spec.DigestPasswordAlgorithmSpec; +import org.wildfly.security.password.spec.DigestPasswordSpec; +import org.wildfly.security.password.spec.Encoding; +import org.wildfly.security.password.spec.EncryptablePasswordSpec; +import org.wildfly.security.password.spec.PasswordSpec; + +/** + * A {@link SecurityRealm} implementation that makes use of the legacy properties files. + * + * @author Darran Lofthouse + */ +public class LegacyPropertiesSecurityRealm implements SecurityRealm { + + private static final String COMMENT_PREFIX1 = "#"; + private static final String COMMENT_PREFIX2 = "!"; + private static final String REALM_COMMENT_PREFIX = "$REALM_NAME="; + private static final String REALM_COMMENT_SUFFIX = "$"; + + private final Supplier providers; + private final String defaultRealm; + private final boolean plainText; + private final Encoding hashEncoding; + private final Charset hashCharset; + + private final String groupsAttribute; + + private final AtomicReference loadedState = new AtomicReference<>(); + + private LegacyPropertiesSecurityRealm(Builder builder) throws IOException { + plainText = builder.plainText; + groupsAttribute = builder.groupsAttribute; + providers = builder.providers; + defaultRealm = builder.defaultRealm; + hashEncoding = builder.hashEncoding; + hashCharset = builder.hashCharset; + } + + @Override + public RealmIdentity getRealmIdentity(final Principal principal) throws RealmUnavailableException { + NamePrincipal namePrincipal = NamePrincipal.from(principal); + if (namePrincipal == null) { + log.tracef("PropertiesRealm: unsupported principal type: [%s]", principal); + return RealmIdentity.NON_EXISTENT; + } + final LoadedState loadedState = this.loadedState.get(); + + final AccountEntry accountEntry = loadedState.getAccounts().get(namePrincipal.getName()); + + if (accountEntry == null) { + log.tracef("PropertiesRealm: identity [%s] does not exist", namePrincipal); + return RealmIdentity.NON_EXISTENT; + } + + return new RealmIdentity() { + + public Principal getRealmIdentityPrincipal() { + return namePrincipal; + } + + @Override + public SupportLevel getCredentialAcquireSupport(final Class credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException { + return LegacyPropertiesSecurityRealm.this.getCredentialAcquireSupport(credentialType, algorithmName, parameterSpec); + } + + @Override + public SupportLevel getEvidenceVerifySupport(final Class evidenceType, final String algorithmName) throws RealmUnavailableException { + return LegacyPropertiesSecurityRealm.this.getEvidenceVerifySupport(evidenceType, algorithmName); + } + + @Override + public C getCredential(final Class credentialType) throws RealmUnavailableException { + return getCredential(credentialType, null, null); + } + + @Override + public C getCredential(final Class credentialType, final String algorithmName) throws RealmUnavailableException { + return getCredential(credentialType, algorithmName, null); + } + + @Override + public C getCredential(final Class credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException { + if (accountEntry.getPasswordRepresentation() == null || LegacyPropertiesSecurityRealm.this.getCredentialAcquireSupport(credentialType, algorithmName, parameterSpec) == SupportLevel.UNSUPPORTED) { + log.tracef("PropertiesRealm: Unable to obtain credential for identity [%s]", namePrincipal); + return null; + } + + boolean clear; // whether should be clear or digested credential returned + if (algorithmName == null) { + clear = plainText; + } else if (ALGORITHM_CLEAR.equals(algorithmName)) { + clear = true; + } else if (ALGORITHM_DIGEST_MD5.equals(algorithmName)) { + clear = false; + } else { + log.tracef("PropertiesRealm: Unable to obtain credential for identity [%s]: unsupported algorithm [%s]", namePrincipal, algorithmName); + return null; + } + + final PasswordFactory passwordFactory; + final PasswordSpec passwordSpec; + + if (clear) { + passwordFactory = getPasswordFactory(ALGORITHM_CLEAR); + passwordSpec = new ClearPasswordSpec(accountEntry.getPasswordRepresentation().toCharArray()); + } else { + passwordFactory = getPasswordFactory(ALGORITHM_DIGEST_MD5); + if (plainText) { // file contains clear passwords - needs to be digested + AlgorithmParameterSpec spec = parameterSpec != null ? parameterSpec : new DigestPasswordAlgorithmSpec(accountEntry.getName(), loadedState.getRealmName()); + passwordSpec = new EncryptablePasswordSpec(accountEntry.getPasswordRepresentation().toCharArray(), spec); + } else { // already digested file - need to check realm name + if (parameterSpec != null) { // when not null, type already checked in acquire support check + DigestPasswordAlgorithmSpec spec = (DigestPasswordAlgorithmSpec) parameterSpec; + if (! loadedState.getRealmName().equals(spec.getRealm()) || ! accountEntry.getName().equals(spec.getUsername())) { + if (log.isTraceEnabled()) { + log.tracef("PropertiesRealm: Unable to obtain credential for username [%s] (available [%s]) and realm [%s] (available [%s])", + spec.getUsername(), accountEntry.getName(), spec.getRealm(), loadedState.getRealmName()); + } + return null; // no digest for given username+realm + } + } + byte[] hashed; + if (hashEncoding.equals(Encoding.BASE64)) { + hashed = ByteIterator.ofBytes(accountEntry.getPasswordRepresentation().getBytes(hashCharset)).asUtf8String().base64Decode().drain(); + } else { + // use hex by default otherwise + hashed = ByteIterator.ofBytes(accountEntry.getPasswordRepresentation().getBytes(hashCharset)).asUtf8String().hexDecode().drain(); + } + passwordSpec = new DigestPasswordSpec(accountEntry.getName(), loadedState.getRealmName(), hashed); + } + } + + try { + return credentialType.cast(new PasswordCredential(passwordFactory.generatePassword(passwordSpec))); + } catch (InvalidKeySpecException e) { + throw new IllegalStateException(e); + } + } + + @Override + public boolean verifyEvidence(final Evidence evidence) throws RealmUnavailableException { + if (accountEntry.getPasswordRepresentation() == null || !(evidence instanceof PasswordGuessEvidence)) { + log.tracef("Unable to verify evidence for identity [%s]", namePrincipal); + return false; + } + final char[] guess = ((PasswordGuessEvidence) evidence).getGuess(); + + final PasswordFactory passwordFactory; + final PasswordSpec passwordSpec; + final Password actualPassword; + if (plainText) { + passwordFactory = getPasswordFactory(ALGORITHM_CLEAR); + passwordSpec = new ClearPasswordSpec(accountEntry.getPasswordRepresentation().toCharArray()); + } else { + passwordFactory = getPasswordFactory(ALGORITHM_DIGEST_MD5); + try { + byte[] hashed; + if (hashEncoding.equals(Encoding.BASE64)) { + hashed = ByteIterator.ofBytes(accountEntry.getPasswordRepresentation().getBytes(hashCharset)).asUtf8String().base64Decode().drain(); + } else { + // use hex by default otherwise + hashed = ByteIterator.ofBytes(accountEntry.getPasswordRepresentation().getBytes(hashCharset)).asUtf8String().hexDecode().drain(); + } + passwordSpec = new DigestPasswordSpec(accountEntry.getName(), loadedState.getRealmName(), hashed); + } catch (DecodeException e) { + throw log.decodingHashedPasswordFromPropertiesRealmFailed(e); + } + } + try { + + log.tracef("Attempting to authenticate account %s using LegacyPropertiesSecurityRealm.", + accountEntry.getName()); + + actualPassword = passwordFactory.generatePassword(passwordSpec); + return passwordFactory.verify(actualPassword, guess, hashCharset); + } catch (InvalidKeySpecException | InvalidKeyException | IllegalStateException e) { + throw new IllegalStateException(e); + } + } + + public boolean exists() throws RealmUnavailableException { + return true; + } + + @Override + public AuthorizationIdentity getAuthorizationIdentity() throws RealmUnavailableException { + return AuthorizationIdentity.basicIdentity(new MapAttributes(Collections.singletonMap(groupsAttribute, accountEntry.getGroups()))); + } + }; + } + + private PasswordFactory getPasswordFactory(final String algorithm) { + try { + return PasswordFactory.getInstance(algorithm, providers); + } catch (NoSuchAlgorithmException e) { + throw new IllegalStateException(e); + } + } + + @Override + public SupportLevel getCredentialAcquireSupport(final Class credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException { + Assert.checkNotNullParam("credentialType", credentialType); + return PasswordCredential.class.isAssignableFrom(credentialType) && + (algorithmName == null || algorithmName.equals(ALGORITHM_CLEAR) && plainText || algorithmName.equals(ALGORITHM_DIGEST_MD5)) && + (parameterSpec == null || parameterSpec instanceof DigestPasswordAlgorithmSpec) + ? SupportLevel.SUPPORTED : SupportLevel.UNSUPPORTED; + } + + @Override + public SupportLevel getEvidenceVerifySupport(final Class evidenceType, final String algorithmName) throws RealmUnavailableException { + return PasswordGuessEvidence.class.isAssignableFrom(evidenceType) ? SupportLevel.SUPPORTED : SupportLevel.UNSUPPORTED; + } + + /** + * Loads this properties security realm from the given user and groups input streams. + * + * @param usersStream the input stream from which the realm users are loaded + * @param groupsStream the input stream from which the roles of realm users are loaded + * @throws IOException if there is problem while reading the input streams or invalid content is loaded from streams + */ + public void load(InputStream usersStream, InputStream groupsStream) throws IOException { + Map accounts = new HashMap<>(); + Properties groups = new Properties(); + + if (groupsStream != null) { + try (InputStreamReader is = new InputStreamReader(groupsStream, StandardCharsets.UTF_8);) { + groups.load(is); + } + } + + String realmName = null; + if (usersStream != null) { + try (BufferedReader reader = new BufferedReader(new InputStreamReader(usersStream, StandardCharsets.UTF_8))) { + String currentLine; + while ((currentLine = reader.readLine()) != null) { + final String trimmed = currentLine.trim(); + if (trimmed.startsWith(COMMENT_PREFIX1) && trimmed.contains(REALM_COMMENT_PREFIX)) { + // this is the line that contains the realm name. + int start = trimmed.indexOf(REALM_COMMENT_PREFIX) + REALM_COMMENT_PREFIX.length(); + int end = trimmed.indexOf(REALM_COMMENT_SUFFIX, start); + if (end > -1) { + realmName = trimmed.substring(start, end); + } + } else { + if ( ! (trimmed.startsWith(COMMENT_PREFIX1) || trimmed.startsWith(COMMENT_PREFIX2)) ) { + String username = null; + StringBuilder builder = new StringBuilder(); + + CodePointIterator it = CodePointIterator.ofString(trimmed); + while (it.hasNext()) { + int cp = it.next(); + if (cp == '\\' && it.hasNext()) { // escape + //might be regular escape of regex like characters \\t \\! or unicode \\uxxxx + int marker = it.next(); + if(marker != 'u'){ + builder.appendCodePoint(marker); + } else { + StringBuilder hex = new StringBuilder(); + try{ + hex.appendCodePoint(it.next()); + hex.appendCodePoint(it.next()); + hex.appendCodePoint(it.next()); + hex.appendCodePoint(it.next()); + builder.appendCodePoint((char)Integer.parseInt(hex.toString(),16)); + } catch(NoSuchElementException nsee){ + throw ElytronMessages.log.invalidUnicodeSequence(hex.toString(),nsee); + } + } + } else if (username == null && (cp == '=' || cp == ':')) { // username-password delimiter + username = builder.toString().trim(); + builder = new StringBuilder(); + } else { + builder.appendCodePoint(cp); + } + } + if (username != null) { // end of line and delimiter was read + String password = builder.toString().trim(); + accounts.put(username, new AccountEntry(username, password, groups.getProperty(username))); + } + } + } + } + } + + if (realmName == null) { + if (defaultRealm != null || plainText) { + realmName = defaultRealm; + } else { + throw log.noRealmFoundInProperties(); + } + } + } + + // users, which are in groups file only + for (String userName : groups.stringPropertyNames()) { + if (!accounts.containsKey(userName)) { + accounts.put(userName, new AccountEntry(userName, null, groups.getProperty(userName))); + } + } + + loadedState.set(new LoadedState(accounts, realmName, System.currentTimeMillis())); + } + + /** + * Get the time when the realm was last loaded. + * + * @return the time when the realm was last loaded (number of milliseconds since the standard base time) + */ + public long getLoadTime() { + return loadedState.get().getLoadTime(); + } + + /** + * Obtain a new {@link Builder} capable of building a {@link LegacyPropertiesSecurityRealm}. + * + * @return a new {@link Builder} capable of building a {@link LegacyPropertiesSecurityRealm}. + */ + public static Builder builder() { + return new Builder(); + } + + /** + * A builder for legacy properties security realms. + */ + public static class Builder { + + private Supplier providers = INSTALLED_PROVIDERS; + private InputStream usersStream; + private InputStream groupsStream; + private String defaultRealm = null; + private boolean plainText; + private String groupsAttribute = "groups"; + private Encoding hashEncoding = Encoding.HEX; // set to hex by default + private Charset hashCharset = StandardCharsets.UTF_8; // set to UTF-8 by default + + Builder() { + } + + /** + * Set the supplier for {@link Provider} instanced for use bu the realm. + * + * @param providers the supplier for {@link Provider} instanced for use bu the realm. + * @return this {@link Builder} + */ + public Builder setProviders(Supplier providers) { + Assert.checkNotNullParam("providers", providers); + this.providers = providers; + + return this; + } + + /** + * Set the {@link InputStream} to use to load the users. + * + * @param usersStream the {@link InputStream} to use to load the users. + * @return this {@link Builder} + */ + public Builder setUsersStream(InputStream usersStream) { + this.usersStream = usersStream; + + return this; + } + + /** + * Set the {@link InputStream} to use to load the group information. + * + * @param groupsStream the {@link InputStream} to use to load the group information. + * @return this {@link Builder} + */ + public Builder setGroupsStream(InputStream groupsStream) { + this.groupsStream = groupsStream; + + return this; + } + + /** + * Where this realm returns an {@link AuthorizationIdentity} set the key on the Attributes that will be used to hold the + * group membership information. + * + * @param groupsAttribute the key on the Attributes that will be used to hold the group membership information. + * @return this {@link Builder} + */ + public Builder setGroupsAttribute(final String groupsAttribute) { + Assert.checkNotNullParam("groupsAttribute", groupsAttribute); + this.groupsAttribute = groupsAttribute; + + return this; + } + + + /** + * Set the default realm name to use if no realm name is discovered in the properties file. + * + * @param defaultRealm the default realm name if one is not discovered in the properties file. + * @return this {@link Builder} + */ + public Builder setDefaultRealm(String defaultRealm) { + this.defaultRealm = defaultRealm; + + return this; + } + + /** + * Set format of users property file - if the passwords are stored in plain text. + * Otherwise is HEX( MD5( username ":" realm ":" password ) ) expected. + * + * @param plainText if the passwords are stored in plain text. + * @return this {@link Builder} + */ + public Builder setPlainText(boolean plainText) { + this.plainText = plainText; + + return this; + } + + /** + * Set the string format for the password in the properties file if they are not + * stored in plain text. Set to hex by default. + * + * @param hashEncoding specifies the string format for the hashed password + * @return this {@link Builder} + */ + public Builder setHashEncoding(Encoding hashEncoding) { + Assert.checkNotNullParam("hashEncoding", hashEncoding); + this.hashEncoding = hashEncoding; + + return this; + } + + /** + * Set the character set to use when converting the password string + * to a byte array. Set to UTF-8 by default. + * @param hashCharset the name of the character set (must not be {@code null}) + * @return this {@link Builder} + */ + public Builder setHashCharset(Charset hashCharset) { + Assert.checkNotNullParam("hashCharset", hashCharset); + this.hashCharset = hashCharset; + + return this; + } + + /** + * Builds the {@link LegacyPropertiesSecurityRealm}. + * @return built {@link LegacyPropertiesSecurityRealm} + * @throws IOException when loading of property files fails + * @throws java.io.FileNotFoundException when property file does not exist + * @throws RealmUnavailableException when property file of users does not contain realm name specification + */ + public LegacyPropertiesSecurityRealm build() throws IOException { + LegacyPropertiesSecurityRealm realm = new LegacyPropertiesSecurityRealm(this); + realm.load(usersStream, groupsStream); + + return realm; + } + + } + + private static class LoadedState { + + private final Map accounts; + private final String realmName; + private final long loadTime; + + private LoadedState(Map accounts, String realmName, long loadTime) { + this.accounts = accounts; + this.realmName = realmName; + this.loadTime = loadTime; + } + + public Map getAccounts() { + return accounts; + } + + public String getRealmName() { + return realmName; + } + + public long getLoadTime() { + return loadTime; + } + + } + + private static class AccountEntry { + + private final String name; + private final String passwordRepresentation; + private final Set groups; + + private AccountEntry(String name, String passwordRepresentation, String groups) { + this.name = name; + this.passwordRepresentation = passwordRepresentation; + this.groups = convertGroups(groups); + } + + private Set convertGroups(String groups) { + if (groups == null) { + return Collections.emptySet(); + } + + String[] groupArray = groups.split(","); + Set groupsSet = new HashSet<>(groupArray.length); + for (String current : groupArray) { + String value = current.trim(); + if (value.length() > 0) { + groupsSet.add(value); + } + } + + return Collections.unmodifiableSet(groupsSet); + } + + public String getName() { + return name; + } + + public String getPasswordRepresentation() { + return passwordRepresentation; + } + + public Set getGroups() { + return groups; + } + } + + +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/realm/SimpleMapBackedSecurityRealm.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/realm/SimpleMapBackedSecurityRealm.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/realm/SimpleMapBackedSecurityRealm.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,264 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2014 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.auth.realm; + +import static org.wildfly.common.Assert.checkNotNullParam; +import static org.wildfly.security.provider.util.ProviderUtil.INSTALLED_PROVIDERS; + +import java.security.Principal; +import java.security.Provider; +import java.security.spec.AlgorithmParameterSpec; +import java.util.Collections; +import java.util.Map; +import java.util.function.Supplier; + +import org.wildfly.common.Assert; +import org.wildfly.security.auth.principal.NamePrincipal; +import org.wildfly.security.authz.Attributes; +import org.wildfly.security.authz.AuthorizationIdentity; +import org.wildfly.security.auth.server.RealmIdentity; +import org.wildfly.security.auth.server.RealmUnavailableException; +import org.wildfly.security.auth.server.SecurityRealm; +import org.wildfly.security.auth.server.NameRewriter; +import org.wildfly.security.auth.SupportLevel; +import org.wildfly.security.credential.Credential; +import org.wildfly.security.credential.PasswordCredential; +import org.wildfly.security.evidence.Evidence; +import org.wildfly.security.password.Password; + +import static org.wildfly.security.auth.realm.ElytronMessages.log; + +/** + * Simple map-backed security realm. Uses an in-memory copy-on-write map methodology to map user names to + * entries. Since this security realm implementation holds all names in memory, it may not be the best choice + * for very large security realms. + * + * @author David M. Lloyd + * @author Darran Lofthouse + */ +public class SimpleMapBackedSecurityRealm implements SecurityRealm { + + private final Supplier providers; + private final NameRewriter rewriter; + private volatile Map map = Collections.emptyMap(); + + /** + * Construct a new instance. + * + * @param rewriter the name rewriter to use (cannot be {@code null}) + */ + public SimpleMapBackedSecurityRealm(final NameRewriter rewriter) { + this(rewriter, INSTALLED_PROVIDERS); + } + + /** + * Construct a new instance. + * + * @param rewriter the name rewriter to use (cannot be {@code null}) + * @param providers a supplier of providers for use by this realm (cannot be {@code null}) + */ + public SimpleMapBackedSecurityRealm(final NameRewriter rewriter, final Supplier providers) { + this.rewriter = checkNotNullParam("rewriter", rewriter); + this.providers = checkNotNullParam("provider", providers); + } + + /** + * Construct a new instance. + */ + public SimpleMapBackedSecurityRealm() { + this(NameRewriter.IDENTITY_REWRITER); + } + + /** + * Construct a new instance. + * + * @param providers a supplier of providers for use by this realm (cannot be {@code null}) + */ + public SimpleMapBackedSecurityRealm(final Supplier providers) { + this(NameRewriter.IDENTITY_REWRITER, providers); + } + + /** + * Set the realm identity map. Note that the entry map must not be modified after calling this method. + * If it needs to be changed, pass in a new map that is a copy of the old map with the required changes. + * + * @param map the identity map where key is an identity name and value is an identity entry + */ + public void setIdentityMap(final Map map) { + Assert.checkNotNullParam("map", map); + this.map = map; + } + + /** + * Set the realm identity map. Note that the entry map must not be modified after calling this method. + * If it needs to be changed, pass in a new map that is a copy of the old map with the required changes. + * + * @param map the identity map + * @deprecated Use {@link #setIdentityMap(Map)} instead. + */ + @Deprecated + public void setPasswordMap(final Map map) { + setIdentityMap(map); + } + + /** + * Set the realm identity map to contain a single entry. + * + * @param name the entry name + * @param password the password + * @param attributes the identity attributes + * @deprecated Use {@link #setIdentityMap(Map)} instead. + */ + @Deprecated + public void setPasswordMap(final String name, final Password password, final Attributes attributes) { + setIdentityMap(Collections.singletonMap(name, new SimpleRealmEntry( + Collections.singletonList(new PasswordCredential(password)), + attributes + ))); + } + + /** + * Set the realm identity map to contain a single entry. + * + * @param name the entry name + * @param password the password + * @deprecated Use {@link #setIdentityMap(Map)} instead. + */ + @Deprecated + public void setPasswordMap(final String name, final Password password) { + setPasswordMap(name, password, Attributes.EMPTY); + } + + @Override + public RealmIdentity getRealmIdentity(final Principal principal) { + if (! NamePrincipal.isConvertibleTo(principal)) { + return RealmIdentity.NON_EXISTENT; + } + String name = rewriter.rewriteName(principal.getName()); + if (name == null) { + throw log.invalidName(); + } + return new SimpleMapRealmIdentity(name); + } + + @Override + public SupportLevel getCredentialAcquireSupport(final Class credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException { + Assert.checkNotNullParam("credentialType", credentialType); + return SupportLevel.POSSIBLY_SUPPORTED; + } + + @Override + public SupportLevel getEvidenceVerifySupport(final Class evidenceType, final String algorithmName) throws RealmUnavailableException { + Assert.checkNotNullParam("evidenceType", evidenceType); + return SupportLevel.POSSIBLY_SUPPORTED; + } + + private class SimpleMapRealmIdentity implements RealmIdentity { + + private final String name; + + SimpleMapRealmIdentity(final String name) { + this.name = name; + } + + public Principal getRealmIdentityPrincipal() { + return new NamePrincipal(name); + } + + @Override + public SupportLevel getCredentialAcquireSupport(final Class credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException { + Assert.checkNotNullParam("credentialType", credentialType); + final SimpleRealmEntry entry = map.get(name); + if (entry == null) return SupportLevel.UNSUPPORTED; + for (Credential credential : entry.getCredentials()) { + if (credential.matches(credentialType, algorithmName, parameterSpec)) { + return SupportLevel.SUPPORTED; + } + } + return SupportLevel.UNSUPPORTED; + } + + @Override + public C getCredential(final Class credentialType) throws RealmUnavailableException { + return getCredential(credentialType, null); + } + + @Override + public C getCredential(final Class credentialType, final String algorithmName) throws RealmUnavailableException { + return getCredential(credentialType, algorithmName, null); + } + + @Override + public C getCredential(final Class credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException { + checkNotNullParam("credentialType", credentialType); + final SimpleRealmEntry entry = map.get(name); + if (entry == null) return null; + for (Credential credential : entry.getCredentials()) { + if (credential.matches(credentialType, algorithmName, parameterSpec)) { + return credentialType.cast(credential.clone()); + } + } + return null; + } + + @Override + public AuthorizationIdentity getAuthorizationIdentity() { + final SimpleRealmEntry entry = map.get(name); + return entry == null ? AuthorizationIdentity.EMPTY : AuthorizationIdentity.basicIdentity(entry.getAttributes()); + } + + @Override + public SupportLevel getEvidenceVerifySupport(final Class evidenceType, final String algorithmName) throws RealmUnavailableException { + Assert.checkNotNullParam("evidenceType", evidenceType); + SimpleRealmEntry entry = map.get(name); + if (entry == null) { + return SupportLevel.UNSUPPORTED; + } + for (Credential credential : entry.getCredentials()) { + if (credential.canVerify(evidenceType, algorithmName)) { + return SupportLevel.SUPPORTED; + } + } + return SupportLevel.UNSUPPORTED; + } + + @Override + public boolean verifyEvidence(final Evidence evidence) throws RealmUnavailableException { + Assert.checkNotNullParam("evidence", evidence); + SimpleRealmEntry entry = map.get(name); + if (entry == null) { + return false; + } + + log.tracef("Trying to authenticate %s using SimpleMapBackedSecurityRealm.", name); + + for (Credential credential : entry.getCredentials()) { + if (credential.canVerify(evidence)) { + return credential.verify(providers, evidence); + } + } + return false; + } + + @Override + public boolean exists() throws RealmUnavailableException { + return map.containsKey(name); + } + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/realm/SimpleRealmEntry.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/realm/SimpleRealmEntry.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/realm/SimpleRealmEntry.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,75 @@ +/* + * 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.auth.realm; + +import org.wildfly.common.Assert; +import org.wildfly.security.authz.Attributes; +import org.wildfly.security.credential.Credential; + +import java.util.List; + +/** + * A simple in-memory password-based entry for basic realm implementations. + * + * @author David M. Lloyd + */ +public class SimpleRealmEntry { + private final List credentials; + private final Attributes attributes; + + /** + * Construct a new instance. + * + * @param credentials the list of credentials (not {@code null}) + */ + public SimpleRealmEntry(final List credentials) { + this(credentials, Attributes.EMPTY); + } + + /** + * Construct a new instance. + * + * @param credentials the list of credentials (can not be {@code null}) + * @param attributes the entry attributes (can not be {@code null}) + */ + public SimpleRealmEntry(final List credentials, final Attributes attributes) { + Assert.checkNotNullParam("credentials", credentials); + Assert.checkNotNullParam("attributes", attributes); + this.credentials = credentials; + this.attributes = attributes; + } + + /** + * Get the credentials for this entry. + * + * @return the credentials (not {@code null}) + */ + public List getCredentials() { + return credentials; + } + + /** + * Get the entry attributes. + * + * @return the entry attributes (not {@code null}) + */ + public Attributes getAttributes() { + return attributes; + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/realm/package-info.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/realm/package-info.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/realm/package-info.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,22 @@ +/* + * 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. + */ + +/** + * Security realm implementations for Elytron. + */ +package org.wildfly.security.auth.realm; Index: 3rdParty_sources/elytron/org/wildfly/security/auth/server/AbstractMechanismAuthenticationFactory.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/server/AbstractMechanismAuthenticationFactory.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/server/AbstractMechanismAuthenticationFactory.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,186 @@ +/* + * 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.auth.server; + +import java.util.Collection; +import java.util.LinkedHashSet; +import java.util.function.UnaryOperator; + +import javax.security.auth.callback.CallbackHandler; + +import org.wildfly.common.Assert; +import org.wildfly.security.credential.AlgorithmCredential; +import org.wildfly.security.credential.Credential; +import org.wildfly.security.evidence.AlgorithmEvidence; +import org.wildfly.security.evidence.Evidence; + +public abstract class AbstractMechanismAuthenticationFactory implements MechanismAuthenticationFactory { + + private final SecurityDomain securityDomain; + private final MechanismConfigurationSelector mechanismConfigurationSelector; + private final F factory; + + protected AbstractMechanismAuthenticationFactory(final SecurityDomain securityDomain, final MechanismConfigurationSelector mechanismConfigurationSelector, final F factory) { + this.securityDomain = securityDomain; + this.mechanismConfigurationSelector = mechanismConfigurationSelector; + this.factory = factory; + } + + public MechanismConfigurationSelector getMechanismConfigurationSelector() { + return mechanismConfigurationSelector; + } + + public SecurityDomain getSecurityDomain() { + return securityDomain; + } + + public F getFactory() { + return factory; + } + + public M createMechanism(final String name, final UnaryOperator factoryTransformation) throws E { + return doCreate(name, new ServerAuthenticationContext(securityDomain, mechanismConfigurationSelector).createCallbackHandler(), factoryTransformation); + } + + protected abstract M doCreate(String name, CallbackHandler callbackHandler, final UnaryOperator factoryTransformation) throws E; + + protected abstract Collection> getSupportedEvidenceTypes(String mechName); + + protected abstract Collection getSupportedEvidenceAlgorithmNames(Class evidenceType, String mechName); + + protected abstract Collection> getSupportedCredentialTypes(String mechName); + + protected abstract Collection getSupportedCredentialAlgorithmNames(Class credentialType, String mechName); + + /** + * Determine whether the given mechanism name needs credentials from a realm in order to authenticate. + * + * @param mechName the mechanism name + * @return {@code true} if the mechanism requires realm credential support, {@code false} if it does not + */ + protected abstract boolean usesCredentials(String mechName); + + /** + * Determine whether the given mechanism name is known to WildFly Elytron. + * + * If it is not known we can't filter it out as we can not rely upon the other methods being able to + * return accurate responses about the mechanisms requirements. + * + * As this is a new method and other implementations may not know to override this has a default + * implementation to match the current behaviour i.e. assume we know about all mechanisms. + * + * @param mechName the mechanism name + * @return {@code true} if the mechanism is known to WildFly Elytron, {@code false} if it is not + */ + protected boolean isKnownMechanism(String mechName) { + return true; + }; + + public Collection getMechanismNames() { + final Collection names = new LinkedHashSet<>(); + top: for (String mechName : getAllSupportedMechNames()) { + // If we don't know about the mech we have to support it as it is likely + // a custom mechanism so our filtering rules will not be correct. + if ((! isKnownMechanism(mechName)) || + // if the mech doesn't need credentials, then we support it for sure + (! usesCredentials(mechName))) { + names.add(mechName); + continue; + } + + final SecurityDomain securityDomain = this.securityDomain; + + // if the mech supports verification for a type of evidence we have, we support it + for (Class evidenceType : getSupportedEvidenceTypes(mechName)) { + if (AlgorithmEvidence.class.isAssignableFrom(evidenceType)) { + for (String algorithmName : getSupportedEvidenceAlgorithmNames(evidenceType.asSubclass(AlgorithmEvidence.class), mechName)) { + if ("*".equals(algorithmName) && securityDomain.getEvidenceVerifySupport(evidenceType).mayBeSupported() || securityDomain.getEvidenceVerifySupport(evidenceType, algorithmName).mayBeSupported()) { + names.add(mechName); + continue top; + } + } + } else { + if (securityDomain.getEvidenceVerifySupport(evidenceType).mayBeSupported()) { + names.add(mechName); + continue top; + } + } + } + // if the mech supports a type of credential we have, we support it + for (Class credentialType : getSupportedCredentialTypes(mechName)) { + if (AlgorithmCredential.class.isAssignableFrom(credentialType)) { + for (String algorithmName : getSupportedCredentialAlgorithmNames(credentialType.asSubclass(AlgorithmCredential.class), mechName)) { + if ("*".equals(algorithmName) && securityDomain.getCredentialAcquireSupport(credentialType).mayBeSupported() || securityDomain.getCredentialAcquireSupport(credentialType, algorithmName).mayBeSupported()) { + names.add(mechName); + continue top; + } + } + } else { + if (securityDomain.getCredentialAcquireSupport(credentialType).mayBeSupported()) { + names.add(mechName); + continue top; + } + } + } + } + return names; + } + + protected abstract Collection getAllSupportedMechNames(); + + protected abstract static class Builder implements MechanismAuthenticationFactory.Builder { + private SecurityDomain securityDomain; + private MechanismConfigurationSelector mechanismConfigurationSelector; + private F factory; + + protected Builder() { + } + + public Builder setSecurityDomain(final SecurityDomain securityDomain) { + Assert.checkNotNullParam("securityDomain", securityDomain); + this.securityDomain = securityDomain; + return this; + } + + public Builder setMechanismConfigurationSelector(final MechanismConfigurationSelector mechanismConfigurationSelector) { + this.mechanismConfigurationSelector = mechanismConfigurationSelector; + return this; + } + + public Builder setFactory(final F factory) { + Assert.checkNotNullParam("factory", factory); + this.factory = factory; + return this; + } + + public SecurityDomain getSecurityDomain() { + return securityDomain; + } + + public MechanismConfigurationSelector getMechanismConfigurationSelector() { + return mechanismConfigurationSelector; + } + + public F getFactory() { + return factory; + } + } + + +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/server/EvidenceDecoder.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/server/EvidenceDecoder.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/server/EvidenceDecoder.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,71 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2019 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.auth.server; + +import java.security.Principal; +import java.util.function.Function; + +import org.wildfly.common.Assert; +import org.wildfly.security.evidence.Evidence; + +/** + * A decoder for extracting a principal from evidence. + * + * @author Farah Juma + * @since 1.10.0 + */ +@FunctionalInterface +public interface EvidenceDecoder extends Function { + + /** + * Get the principal from the given evidence. If this decoder does not understand the given evidence, + * {@code null} is returned. + * + * @param evidence the evidence to decode + * @return the principal, or {@code null} if this decoder does not understand the evidence + */ + Principal getPrincipal(Evidence evidence); + + default Principal apply(Evidence evidence) { + return getPrincipal(evidence); + } + + /** + * Create an aggregated evidence decoder. The aggregated decoder will try each evidence decoder until one + * returns a {@code non-null} value. If all the evidence decoders return {@code null}, then {@code null} + * will be returned. + * + * @param decoders the constituent decoders + * @return the aggregated decoder + */ + static EvidenceDecoder aggregate(final EvidenceDecoder... decoders) { + Assert.checkNotNullParam("decoders", decoders); + return evidence -> { + Principal result; + for (EvidenceDecoder decoder : decoders) { + result = decoder.getPrincipal(evidence); + if (result != null) { + return result; + } + } + return null; + }; + } + +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/server/FlexibleIdentityAssociation.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/server/FlexibleIdentityAssociation.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/server/FlexibleIdentityAssociation.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,144 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2017 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.auth.server; + +import java.util.function.BiConsumer; +import java.util.function.BiFunction; +import java.util.function.BiPredicate; +import java.util.function.ObjIntConsumer; +import java.util.function.Supplier; + +import org.wildfly.common.Assert; +import org.wildfly.common.function.ExceptionBiConsumer; +import org.wildfly.common.function.ExceptionBiFunction; +import org.wildfly.common.function.ExceptionBiPredicate; +import org.wildfly.common.function.ExceptionObjIntConsumer; +import org.wildfly.security.auth.server._private.ElytronMessages; + +/** + * A flexible identity association which can have its current identity modified. Modifying the identity association + * will affect the current identity of any thread which is currently executing within the scope of this association. + * + * @see SecurityIdentity#createFlexibleAssociation + * @author David M. Lloyd + */ +public final class FlexibleIdentityAssociation implements Scoped, Supplier { + private final SecurityDomain securityDomain; + private volatile SecurityIdentity securityIdentity; + + FlexibleIdentityAssociation(final SecurityDomain securityDomain, final SecurityIdentity securityIdentity) { + assert securityIdentity != null && securityDomain == securityIdentity.getSecurityDomain(); + this.securityDomain = securityDomain; + this.securityIdentity = securityIdentity; + } + + /** + * Set the current associated identity. + * + * @param securityIdentity the current associated identity (must not be {@code null}) + */ + public void setIdentity(SecurityIdentity securityIdentity) { + Assert.checkNotNullParam("securityIdentity", securityIdentity); + if (securityIdentity.getSecurityDomain() != securityDomain) { + throw ElytronMessages.log.securityDomainMismatch(); + } + this.securityIdentity = securityIdentity; + } + + /** + * Get the current associated identity. + * + * @return the current associated identity (not {@code null}) + */ + public SecurityIdentity get() { + return securityIdentity; + } + + public R runAsFunction(final BiFunction action, final T parameter1, final U parameter2) { + final Supplier old = securityDomain.getAndSetCurrentSecurityIdentity(this); + try { + return action.apply(parameter1, parameter2); + } finally { + securityDomain.setCurrentSecurityIdentity(old); + } + } + + public void runAsConsumer(final BiConsumer action, final T parameter1, final U parameter2) { + final Supplier old = securityDomain.getAndSetCurrentSecurityIdentity(this); + try { + action.accept(parameter1, parameter2); + } finally { + securityDomain.setCurrentSecurityIdentity(old); + } + } + + public void runAsObjIntConsumer(final ObjIntConsumer action, final T parameter1, final int parameter2) { + final Supplier old = securityDomain.getAndSetCurrentSecurityIdentity(this); + try { + action.accept(parameter1, parameter2); + } finally { + securityDomain.setCurrentSecurityIdentity(old); + } + } + + public R runAsFunctionEx(final ExceptionBiFunction action, final T parameter1, final U parameter2) throws E { + final Supplier old = securityDomain.getAndSetCurrentSecurityIdentity(this); + try { + return action.apply(parameter1, parameter2); + } finally { + securityDomain.setCurrentSecurityIdentity(old); + } + } + + public void runAsConsumerEx(final ExceptionBiConsumer action, final T parameter1, final U parameter2) throws E { + final Supplier old = securityDomain.getAndSetCurrentSecurityIdentity(this); + try { + action.accept(parameter1, parameter2); + } finally { + securityDomain.setCurrentSecurityIdentity(old); + } + } + + public void runAsObjIntConsumerEx(final ExceptionObjIntConsumer action, final T parameter1, final int parameter2) throws E { + final Supplier old = securityDomain.getAndSetCurrentSecurityIdentity(this); + try { + action.accept(parameter1, parameter2); + } finally { + securityDomain.setCurrentSecurityIdentity(old); + } + } + + public boolean runAsBiPredicate(BiPredicate action, T param1, U param2) { + final Supplier old = securityDomain.getAndSetCurrentSecurityIdentity(this); + try { + return action.test(param1, param2); + } finally { + securityDomain.setCurrentSecurityIdentity(old); + } + } + + public boolean runAsExBiPredicate(ExceptionBiPredicate action, T param1, U param2) throws E { + final Supplier old = securityDomain.getAndSetCurrentSecurityIdentity(this); + try { + return action.test(param1, param2); + } finally { + securityDomain.setCurrentSecurityIdentity(old); + } + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/server/IdentityCredentials.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/server/IdentityCredentials.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/server/IdentityCredentials.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,924 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2016 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.auth.server; + +import static org.wildfly.common.math.HashMath.multiHashOrdered; + +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.security.Provider; +import java.security.spec.AlgorithmParameterSpec; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Objects; +import java.util.Spliterator; +import java.util.Spliterators; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.function.Supplier; +import java.util.stream.StreamSupport; + +import org.wildfly.common.Assert; +import org.wildfly.security.credential.PasswordCredential; +import org.wildfly.security.credential.source.CredentialSource; +import org.wildfly.security.auth.SupportLevel; +import org.wildfly.security.credential.AlgorithmCredential; +import org.wildfly.security.credential.Credential; +import org.wildfly.security.evidence.Evidence; +import org.wildfly.security.util.EnumerationIterator; + +/** + * The public or private credentials retained by an identity, which can be used for authentication forwarding. This + * credentials set can contain zero or one credential of a given type and algorithm name. If the credential type + * does not support algorithm names, then the set can contain zero or one credential of that type. The credential + * set may be iterated; iteration order is not prescribed and may change if the implementation is changed. + * + * @author David M. Lloyd + */ +public abstract class IdentityCredentials implements Iterable, CredentialSource { + IdentityCredentials() { + } + + /** + * Determine whether a credential of the given type is present in this set. + * + * @param credentialType the credential type class (must not be {@code null}) + * @return {@code true} if a matching credential is contained in this set, {@code false} otherwise + */ + public final boolean contains(Class credentialType) { + return contains(credentialType, null); + } + + @Override + public final SupportLevel getCredentialAcquireSupport(final Class credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec) { + return contains(credentialType, algorithmName, parameterSpec) ? SupportLevel.SUPPORTED : SupportLevel.UNSUPPORTED; + } + + @Override + public final SupportLevel getCredentialAcquireSupport(final Class credentialType, final String algorithmName) { + return contains(credentialType, algorithmName) ? SupportLevel.SUPPORTED : SupportLevel.UNSUPPORTED; + } + + @Override + public final SupportLevel getCredentialAcquireSupport(final Class credentialType) { + return contains(credentialType) ? SupportLevel.SUPPORTED : SupportLevel.UNSUPPORTED; + } + + /** + * Determine whether a credential of the given type and algorithm are present in this set. + * + * @param credentialType the credential type class (must not be {@code null}) + * @param algorithmName the algorithm name, or {@code null} if any algorithm is acceptable or the credential type + * does not support algorithm names + * @param parameterSpec the parameter specification or {@code null} if any parameter specification is acceptable + * @return {@code true} if a matching credential is contained in this set, {@code false} otherwise + */ + public abstract boolean contains(Class credentialType, String algorithmName, AlgorithmParameterSpec parameterSpec); + + /** + * Determine whether a credential of the given type and algorithm are present in this set. + * + * @param credentialType the credential type class (must not be {@code null}) + * @param algorithmName the algorithm name, or {@code null} if any algorithm is acceptable or the credential type + * does not support algorithm names + * @return {@code true} if a matching credential is contained in this set, {@code false} otherwise + */ + public final boolean contains(Class credentialType, String algorithmName) { + Assert.checkNotNullParam("credentialType", credentialType); + return contains(credentialType, algorithmName, null); + } + + /** + * Determine whether a credential of the type, algorithm, and parameters of the given credential is present in this set. + * + * @param credential the credential to check against (must not be {@code null}) + * @return {@code true} if a matching credential is contained in this set, {@code false} otherwise + */ + public final boolean containsMatching(Credential credential) { + Assert.checkNotNullParam("credential", credential); + if (credential instanceof AlgorithmCredential) { + final AlgorithmCredential algorithmCredential = (AlgorithmCredential) credential; + return contains(credential.getClass(), algorithmCredential.getAlgorithm(), algorithmCredential.getParameters()); + } else { + return contains(credential.getClass(), null, null); + } + } + + /** + * Acquire a credential of the given type. + * + * @param credentialType the credential type class (must not be {@code null}) + * @param the credential type + * @return the credential, or {@code null} if no such credential exists + */ + @Override + public final C getCredential(Class credentialType) { + return getCredential(credentialType, null, null); + } + + /** + * Acquire a credential of the given type and algorithm name. + * + * @param credentialType the credential type class (must not be {@code null}) + * @param algorithmName the algorithm name, or {@code null} if any algorithm is acceptable or the credential type + * does not support algorithm names + * @param the credential type + * @return the credential, or {@code null} if no such credential exists + */ + @Override + public final C getCredential(final Class credentialType, final String algorithmName) { + return getCredential(credentialType, algorithmName, null); + } + + /** + * Acquire a credential of the given type and algorithm name. + * + * @param credentialType the credential type class (must not be {@code null}) + * @param algorithmName the algorithm name, or {@code null} if any algorithm is acceptable or the credential type + * does not support algorithm names + * @param parameterSpec the parameter specification or {@code null} if any parameter specification is acceptable + * @param the credential type + * + * @return the credential, or {@code null} if no such credential exists + */ + @Override + public abstract C getCredential(Class credentialType, String algorithmName, AlgorithmParameterSpec parameterSpec); + + /** + * Apply the given function to the acquired credential, if it is set and of the given type. + * + * @param credentialType the credential type class (must not be {@code null}) + * @param function the function to apply (must not be {@code null}) + * @param the credential type + * @param the return type + * @return the result of the function, or {@code null} if the criteria are not met + */ + @Override + public final R applyToCredential(Class credentialType, Function function) { + final Credential credential = getCredential(credentialType); + return credential == null ? null : credential.castAndApply(credentialType, function); + } + + + /** + * Apply the given function to the acquired credential, if it is set and of the given type and algorithm. + * + * @param credentialType the credential type class (must not be {@code null}) + * @param algorithmName the algorithm name + * @param function the function to apply (must not be {@code null}) + * @param the credential type + * @param the return type + * @return the result of the function, or {@code null} if the criteria are not met + */ + @Override + public final R applyToCredential(Class credentialType, String algorithmName, Function function) { + final Credential credential = getCredential(credentialType, algorithmName); + return credential == null ? null : credential.castAndApply(credentialType, algorithmName, function); + } + + /** + * Apply the given function to the acquired credential, if it is set and of the given type and algorithm. + * + * @param credentialType the credential type class (must not be {@code null}) + * @param algorithmName the algorithm name + * @param function the function to apply (must not be {@code null}) + * @param the credential type + * @param the return type + * @return the result of the function, or {@code null} if the criteria are not met + */ + @Override + public R applyToCredential(final Class credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec, final Function function) { + final Credential credential = getCredential(credentialType, algorithmName, parameterSpec); + return credential == null ? null : credential.castAndApply(credentialType, algorithmName, parameterSpec, function); + } + + /** + * Return a copy of this credential set, but with the given credential added to it. + * + * @param credential the credential to append (must not be {@code null}) + * @return the new credential set (not {@code null}) + */ + public abstract IdentityCredentials withCredential(Credential credential); + + /** + * Return a copy of this credential set with the given credential set added to it. + * + * @param other the credential set to append (must not be {@code null}) + * @return the new credential set (not {@code null}) + */ + public abstract IdentityCredentials with(IdentityCredentials other); + + /** + * Return a copy of this credential set without any credentials with a type, algorithm name, and parameters matching that of the + * given credential. If the credential type, algorithm name, and parameters are not found in this set, return this instance. + * + * @param credential the credential to match against (must not be {@code null}) + * @return the new credential set (not {@code null}) + */ + public IdentityCredentials withoutMatching(Credential credential) { + Assert.checkNotNullParam("credential", credential); + return without(credential::matches); + } + + /** + * Return a copy of this credential set without any credentials of the given type. If the credential type is not + * found in this set, return this instance. + * + * @param credentialType the credential type to remove (must not be {@code null}) + * @return the new credential set (not {@code null}) + */ + public final IdentityCredentials without(Class credentialType) { + Assert.checkNotNullParam("credentialType", credentialType); + return without(credentialType::isInstance); + } + + /** + * Return a copy of this credential set without any credentials of the given type and algorithm name. If the + * credential type and algorithm name is not found in this set, return this instance. + * + * @param credentialType the credential type to remove (must not be {@code null}) + * @param algorithmName the algorithm name to remove, or {@code null} to match any algorithm name + * @return the new credential set (not {@code null}) + */ + public final IdentityCredentials without(final Class credentialType, final String algorithmName) { + Assert.checkNotNullParam("credentialType", credentialType); + return without(credentialType, algorithmName, null); + } + + /** + * Return a copy of this credential set without any credentials of the given type, algorithm name and + * parameter spec. If the credential type and algorithm name is not found in this set, return this instance. + * + * @param credentialType the credential type to remove (must not be {@code null}) + * @param algorithmName the algorithm name to remove, or {@code null} to match any algorithm name + * @param parameterSpec the parameter spec to remove, or {@code null} to match any parameter spec + * @return the new credential set (not {@code null}) + */ + public IdentityCredentials without(final Class credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec) { + Assert.checkNotNullParam("credentialType", credentialType); + return without(c -> c.matches(credentialType, algorithmName, parameterSpec)); + } + + /** + * Return a copy of this credential set without any credentials that match the predicate. If no credentials match + * the predicate, return this instance. + * + * @param predicate the predicate to test (must not be {@code null}) + * @return the new credential set (not {@code null}) + */ + public abstract IdentityCredentials without(Predicate predicate); + + /** + * Return a copy of this credential set without any credentials of the given type that match the predicate. If no credentials match + * the predicate, return this instance. + * + * @param credentialType the credential type class + * @param predicate the predicate to test (must not be {@code null}) + * @param the credential type + * @return the new credential set (not {@code null}) + */ + public final IdentityCredentials without(Class credentialType, Predicate predicate) { + return without(c -> credentialType.isInstance(c) && predicate.test(credentialType.cast(c))); + } + + /** + * Get a {@link Spliterator} for this credential set. + * + * @return the spliterator (not {@code null}) + */ + public Spliterator spliterator() { + return Spliterators.spliterator(iterator(), size(), Spliterator.IMMUTABLE | Spliterator.DISTINCT | Spliterator.NONNULL | Spliterator.ORDERED | Spliterator.SIZED); + } + + /** + * Test whether some of the credentials in this set can verify an evidence of given class and algorithm name. + * + * @param evidenceClass the class of the evidence (must not be {@code null}) + * @param algorithmName the algorithm name (may be {@code null} if the type of evidence does not support algorithm names) + * @return {@code true} if the evidence can be verified + */ + public boolean canVerify(Class evidenceClass, String algorithmName) { + return StreamSupport.stream(spliterator(), false) + .filter(credential -> credential.canVerify(evidenceClass, algorithmName)) + .count() != 0; + } + + /** + * Test whether some of the credentials in this set can verify an evidence. + * + * @param evidence the evidence (must not be {@code null}) + * @return {@code true} if the evidence can be verified + */ + public boolean canVerify(Evidence evidence) { + return StreamSupport.stream(spliterator(), false) + .filter(credential -> credential.canVerify(evidence)) + .count() != 0; + } + + /** + * Verify the given evidence. + * + * @param evidence the evidence to verify (must not be {@code null}) + * @return {@code true} if the evidence is verified, {@code false} otherwise + */ + public boolean verify(Evidence evidence) { + return verify(evidence, StandardCharsets.UTF_8); + } + + /** + * Verify the given evidence. + * + * @param providerSupplier the provider supplier to use for verification purposes (must not be {@code null}) + * @param evidence the evidence to verify (must not be {@code null}) + * @return {@code true} if the evidence is verified, {@code false} otherwise + */ + public boolean verify(Supplier providerSupplier, Evidence evidence) { + return verify(providerSupplier, evidence, StandardCharsets.UTF_8); + } + + /** + * Verify the given evidence. + * + * @param evidence the evidence to verify (must not be {@code null}) + * @oaram hashCharset the name of the character set (must not be {@code null}) + * @return {@code true} if the evidence is verified, {@code false} otherwise + * @deprecated use {@link #verify(Supplier, Evidence, Charset)} instead + */ + @Deprecated + public boolean verify(Evidence evidence, Charset hashCharset) { + return StreamSupport.stream(spliterator(), false) + .filter(credential -> credential.canVerify(evidence)) + .filter(credential -> + credential instanceof PasswordCredential ? + ((PasswordCredential)credential).verify(evidence, hashCharset) : credential.verify(evidence)) + .count() != 0; + } + + /** + * Verify the given evidence. + * + * @param providerSupplier the provider supplier to use for verification purposes (must not be {@code null}) + * @param evidence the evidence to verify (must not be {@code null}) + * @oaram hashCharset the name of the character set (must not be {@code null}) + * @return {@code true} if the evidence is verified, {@code false} otherwise + */ + public boolean verify(Supplier providerSupplier, Evidence evidence, Charset hashCharset) { + return StreamSupport.stream(spliterator(), false) + .filter(credential -> credential.canVerify(evidence)) + .filter(credential -> + credential instanceof PasswordCredential ? + ((PasswordCredential)credential).verify(providerSupplier, evidence, hashCharset) : credential.verify(providerSupplier, evidence)) + .count() != 0; + } + + /** + * Get the size of this credential set. + * + * @return the size of this credential set + */ + public abstract int size(); + + /** + * The empty credentials object. + */ + public static final IdentityCredentials NONE = new IdentityCredentials() { + public boolean contains(final Class credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec) { + Assert.checkNotNullParam("credentialType", credentialType); + return false; + } + + public C getCredential(final Class credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec) { + Assert.checkNotNullParam("credentialType", credentialType); + return null; + } + + public IdentityCredentials with(final IdentityCredentials other) { + Assert.checkNotNullParam("other", other); + return other; + } + + public IdentityCredentials withCredential(final Credential credential) { + Assert.checkNotNullParam("credential", credential); + return new One(credential); + } + + public CredentialSource with(final CredentialSource other) { + Assert.checkNotNullParam("other", other); + return other; + } + + public Iterator iterator() { + return Collections.emptyIterator(); + } + + public R applyToCredential(final Class credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec, final Function function) { + Assert.checkNotNullParam("credentialType", credentialType); + return null; + } + + public IdentityCredentials withoutMatching(final Credential credential) { + Assert.checkNotNullParam("credential", credential); + return this; + } + + public IdentityCredentials without(final Class credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec) { + Assert.checkNotNullParam("credentialType", credentialType); + return this; + } + + public IdentityCredentials without(final Predicate predicate) { + Assert.checkNotNullParam("predicate", predicate); + return this; + } + + public Spliterator spliterator() { + return Spliterators.emptySpliterator(); + } + + public void forEach(final Consumer action) { + Assert.checkNotNullParam("action", action); + } + + public int size() { + return 0; + } + + public int hashCode() { + return 0; + } + + public boolean equals(Object o) { + return o == this; + } + }; + + static class One extends IdentityCredentials { + private final Credential credential; + + One(final Credential credential) { + this.credential = credential; + } + + public boolean contains(final Class credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec) { + Assert.checkNotNullParam("credentialType", credentialType); + return credential.matches(credentialType, algorithmName, parameterSpec); + } + + public C getCredential(final Class credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec) { + Assert.checkNotNullParam("credentialType", credentialType); + return credential.matches(credentialType, algorithmName, parameterSpec) ? credentialType.cast(credential.clone()) : null; + } + + public IdentityCredentials withCredential(final Credential credential) { + Assert.checkNotNullParam("credential", credential); + if (this.credential.matches(credential)) { + return new One(credential); + } else { + return new Two(this.credential, credential); + } + } + + public IdentityCredentials with(final IdentityCredentials other) { + Assert.checkNotNullParam("other", other); + if (other == NONE) { + return this; + } else if (other instanceof One) { + return withCredential(((One) other).credential); + } else { + return other.with(this); + } + } + + public CredentialSource with(final CredentialSource other) { + Assert.checkNotNullParam("other", other); + if (other instanceof IdentityCredentials) { + return with((IdentityCredentials) other); + } else { + return super.with(other); + } + } + + public Iterator iterator() { + return EnumerationIterator.over(credential); + } + + public R applyToCredential(final Class credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec, final Function function) { + Assert.checkNotNullParam("credentialType", credentialType); + return credential.castAndApply(credentialType, algorithmName, parameterSpec, function); + } + + public IdentityCredentials withoutMatching(final Credential credential) { + Assert.checkNotNullParam("credential", credential); + return this.credential.matches(credential) ? NONE : this; + } + + public IdentityCredentials without(final Class credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec) { + Assert.checkNotNullParam("credentialType", credentialType); + return credential.matches(credentialType, algorithmName, parameterSpec) ? NONE : this; + } + + public IdentityCredentials without(final Predicate predicate) { + Assert.checkNotNullParam("predicate", predicate); + return predicate.test(credential) ? NONE : this; + } + + public void forEach(final Consumer action) { + Assert.checkNotNullParam("action", action); + action.accept(credential); + } + + public int size() { + return 1; + } + + public int hashCode() { + return typeHash(credential); + } + + public boolean equals(final Object obj) { + return obj instanceof One && ((One) obj).credential.equals(credential); + } + } + + static class Two extends IdentityCredentials { + private final Credential credential1; + private final Credential credential2; + + Two(final Credential credential1, final Credential credential2) { + this.credential1 = credential1; + this.credential2 = credential2; + } + + public boolean contains(final Class credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec) { + Assert.checkNotNullParam("credentialType", credentialType); + return credential1.matches(credentialType, algorithmName, parameterSpec) || credential2.matches(credentialType, algorithmName, parameterSpec); + } + + public C getCredential(final Class credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec) { + Assert.checkNotNullParam("credentialType", credentialType); + return credential1.matches(credentialType, algorithmName, parameterSpec) ? credentialType.cast(credential1.clone()) : + credential2.matches(credentialType, algorithmName, parameterSpec) ? credentialType.cast(credential2.clone()) : null; + } + + public IdentityCredentials withCredential(final Credential credential) { + Assert.checkNotNullParam("credential", credential); + if (credential.matches(credential1)) { + return new Two(credential2, credential); + } else if (credential.matches(credential2)) { + return new Two(credential1, credential); + } else { + return new Many(credential1, credential2, credential); + } + } + + public IdentityCredentials with(final IdentityCredentials other) { + Assert.checkNotNullParam("other", other); + if (other == NONE) { + return this; + } else if (other instanceof One) { + return withCredential(((One) other).credential); + } else if (other instanceof Two) { + final Two otherTwo = (Two) other; + return withCredential(otherTwo.credential1).withCredential(otherTwo.credential2); + } else if (other instanceof Many) { + Many otherMany = (Many) other; + if (otherMany.containsMatching(credential1)) { + if (otherMany.containsMatching(credential2)) { + return otherMany; + } else { + return new Many(credential2, otherMany); + } + } else if (otherMany.containsMatching(credential2)) { + return new Many(credential1, otherMany); + } else { + return new Many(credential1, credential2, otherMany); + } + } else { + throw Assert.unreachableCode(); + } + } + + public CredentialSource with(final CredentialSource other) { + Assert.checkNotNullParam("other", other); + if (other instanceof IdentityCredentials) { + return with((IdentityCredentials) other); + } else { + return super.with(other); + } + } + + public Iterator iterator() { + return EnumerationIterator.over(credential1); + } + + public R applyToCredential(final Class credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec, final Function function) { + Assert.checkNotNullParam("credentialType", credentialType); + return credential1.castAndApply(credentialType, algorithmName, parameterSpec, function); + } + + public IdentityCredentials withoutMatching(final Credential credential) { + Assert.checkNotNullParam("credential", credential); + return this.credential1.matches(credential) ? NONE : this; + } + + public IdentityCredentials without(final Class credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec) { + Assert.checkNotNullParam("credentialType", credentialType); + return credential1.matches(credentialType, algorithmName, parameterSpec) ? NONE : this; + } + + public IdentityCredentials without(final Predicate predicate) { + Assert.checkNotNullParam("predicate", predicate); + return predicate.test(credential1) ? predicate.test(credential2) ? NONE : new One(credential2) : predicate.test(credential2) ? new One(credential1) : this; + } + + public void forEach(final Consumer action) { + Assert.checkNotNullParam("action", action); + action.accept(credential1); + action.accept(credential2); + } + + public int size() { + return 2; + } + + public int hashCode() { + return typeHash(credential1) ^ typeHash(credential2); + } + + public boolean equals(final Object obj) { + if (! (obj instanceof Two)) { + return false; + } + final Two two = (Two) obj; + return credential1.equals(two.credential1) && credential2.equals(two.credential2) || credential1.equals(two.credential2) && credential2.equals(two.credential1); + } + } + + /** + * A (hopefully) unique hash code for the kind of credential. + * + * @param credential the credential + * @return the type hash + */ + static int typeHash(Credential credential) { + int ch = credential.getClass().hashCode(); + if (credential instanceof AlgorithmCredential) { + final AlgorithmCredential algorithmCredential = (AlgorithmCredential) credential; + return multiHashOrdered(multiHashOrdered(ch, 42979, Objects.hashCode(algorithmCredential.getAlgorithm())), 62861, Objects.hashCode(algorithmCredential.getParameters())); + } else { + return ch; + } + } + + static class Many extends IdentityCredentials { + private final LinkedHashMap map; + private final int hashCode; + + Many(final Credential c1, final Many subsequent) { + LinkedHashMap map = new LinkedHashMap<>(subsequent.map.size() + 1); + addCredential(c1, map); + map.putAll(subsequent.map); + this.map = map; + int hc = 0; + for (Credential credential : map.values()) { + hc ^= typeHash(credential); + } + hashCode = hc; + assert size() > 2; + } + + Many(final Credential c1, final Credential c2, final Many subsequent) { + LinkedHashMap map = new LinkedHashMap<>(subsequent.map.size() + 2); + addCredential(c1, map); + addCredential(c2, map); + map.putAll(subsequent.map); + this.map = map; + int hc = 0; + for (Credential credential : map.values()) { + hc ^= typeHash(credential); + } + hashCode = hc; + assert size() > 2; + } + + Many(final LinkedHashMap map) { + this.map = map; + int hc = 0; + for (Credential credential : map.values()) { + hc ^= typeHash(credential); + } + hashCode = hc; + assert size() > 2; + } + + Many(final Credential credential1, final Credential credential2, final Credential credential3) { + LinkedHashMap map = new LinkedHashMap<>(3); + addCredential(credential1, map); + addCredential(credential2, map); + addCredential(credential3, map); + this.map = map; + int hc = 0; + for (Credential credential : map.values()) { + hc ^= typeHash(credential); + } + hashCode = hc; + assert size() > 2; + } + + public boolean contains(final Class credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec) { + return map.containsKey(new Key(credentialType, algorithmName, parameterSpec)); + } + + public C getCredential(final Class credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec) { + return credentialType.cast(map.get(new Key(credentialType, algorithmName, parameterSpec)).clone()); + } + + public IdentityCredentials withoutMatching(final Credential credential) { + final Key key = Key.of(credential); + if (map.containsKey(key)) { + final LinkedHashMap clone = new LinkedHashMap<>(map); + clone.remove(key); + if (clone.size() == 2) { + final Iterator iterator = clone.values().iterator(); + return new Two(iterator.next(), iterator.next()); + } else { + return new Many(clone); + } + } else { + return this; + } + } + + public void forEach(final Consumer action) { + map.values().forEach(action); + } + + public int size() { + return map.size(); + } + + public IdentityCredentials withCredential(final Credential credential) { + final LinkedHashMap clone = new LinkedHashMap<>(map); + addCredential(credential, clone); + return new Many(clone); + } + + public IdentityCredentials with(final IdentityCredentials other) { + final LinkedHashMap clone = new LinkedHashMap<>(map); + for (Credential credential : other) { + addCredential(credential, clone); + } + return new Many(clone); + } + + private void addCredential(Credential credential, LinkedHashMap map) { + final Key key = Key.of(credential); + // do this as two steps so it's added to the end + map.remove(key); + map.put(key, credential); + // add an alternate entry without algorithm and parameter spec to allow for loose matches + if (key.getAlgorithm() != null) { + Key altKey = new Key(key.getClazz(), null, null); + map.remove(altKey); + map.put(altKey, credential); + } + // add an alternate entry without parameter spec to allow for loose matches + if (key.getParameterSpec() != null) { + Key altKey = new Key(key.getClazz(), key.getAlgorithm(), null); + map.remove(altKey); + map.put(altKey, credential); + } + } + + public CredentialSource with(final CredentialSource other) { + return other instanceof IdentityCredentials ? with((IdentityCredentials) other) : super.with(other); + } + + public Iterator iterator() { + return Collections.unmodifiableCollection(map.values()).iterator(); + } + + public R applyToCredential(final Class credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec, final Function function) { + final Key key = new Key(credentialType, algorithmName, parameterSpec); + final Credential credential = map.get(key); + if (credential != null) { + return function.apply(credentialType.cast(credential)); + } + return null; + } + + public IdentityCredentials without(final Predicate predicate) { + final LinkedHashMap clone = new LinkedHashMap<>(map); + final Collection values = clone.values(); + values.removeIf(predicate); + final Iterator iterator = values.iterator(); + if (iterator.hasNext()) { + final Credential c1 = iterator.next(); + if (iterator.hasNext()) { + final Credential c2 = iterator.next(); + if (iterator.hasNext()) { + return new Many(clone); + } else { + return new Two(c1, c2); + } + } else { + return new One(c1); + } + } else { + return NONE; + } + } + + public int hashCode() { + return hashCode; + } + + public boolean equals(final Object obj) { + if (! (obj instanceof Many)) { + return false; + } + Many many = (Many) obj; + // check is potentially expensive so start here + if (hashCode != many.hashCode) { + return false; + } + if (map.size() != many.map.size()) { + return false; + } + // now the O(n) part + for (Map.Entry entry : map.entrySet()) { + if (! Objects.equals(many.map.get(entry.getKey()), entry.getValue())) { + return false; + } + } + return true; + } + } + + static final class Key { + private final Class clazz; + private final String algorithm; + private final AlgorithmParameterSpec parameterSpec; + private final int hashCode; + + Key(final Class clazz, final String algorithm, final AlgorithmParameterSpec parameterSpec) { + this.clazz = clazz; + this.algorithm = algorithm; + this.parameterSpec = parameterSpec; + hashCode = multiHashOrdered(multiHashOrdered(clazz.hashCode(), 42979, Objects.hashCode(algorithm)), 62861, Objects.hashCode(parameterSpec)); + } + + static Key of(Credential c) { + if (c instanceof AlgorithmCredential) { + final AlgorithmCredential ac = (AlgorithmCredential) c; + return new Key(ac.getClass(), ac.getAlgorithm(), ac.getParameters()); + } else { + return new Key(c.getClass(), null, null); + } + } + + public int hashCode() { + return hashCode; + } + + public boolean equals(final Object obj) { + return obj instanceof Key && equals((Key) obj); + } + + private boolean equals(final Key key) { + return clazz == key.clazz && Objects.equals(algorithm, key.algorithm) && Objects.equals(parameterSpec, key.parameterSpec); + } + + Class getClazz() { + return clazz; + } + + String getAlgorithm() { + return algorithm; + } + + AlgorithmParameterSpec getParameterSpec() { + return parameterSpec; + } + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/server/MechanismAuthenticationFactory.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/server/MechanismAuthenticationFactory.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/server/MechanismAuthenticationFactory.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,117 @@ +/* + * 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.auth.server; + +import java.util.Collection; +import java.util.function.UnaryOperator; + +/** + * A generalized mechanism factory which authenticates against a security domain. + * + * @param the type of mechanism + * @param the type of the mechanism's factory + * @param the mechanism-type-specific exception that may be thrown upon instantiation + * + * @author David M. Lloyd + */ +public interface MechanismAuthenticationFactory { + + /** + * Get the underlying {@link SecurityDomain} that mechanisms created by this factory will be using for authentication. + * + * @return the underlying {@link SecurityDomain} that mechanisms created by this factory will be using for authentication. + */ + SecurityDomain getSecurityDomain(); + + /** + * Get the actual factory used for instantiation. + * + * @return the actual factory (not {@code null}) + */ + F getFactory(); + + /** + * Create the mechanism instance. + * + * @param name the mechanism name (must not be {@code null}) + * @param factoryTransformation the transformation to apply to the factory (must not be {@code null}) + * @return the mechanism, or {@code null} if the mechanism with the given name is not supported + * @throws E if the mechanism instantiation failed + */ + M createMechanism(String name, UnaryOperator factoryTransformation) throws E; + + /** + * Create the mechanism instance. + * + * @param name the mechanism name (must not be {@code null}) + * @return the mechanism, or {@code null} if the mechanism with the given name is not supported + * @throws E if the mechanism instantiation failed + */ + default M createMechanism(String name) throws E { + return createMechanism(name, UnaryOperator.identity()); + } + + /** + * Get the collection of mechanism names may be supported by this factory. The actual set of available mechanisms depends + * on run-time factors but will generally not be greater than this collection. + * + * @return the mechanism names (not {@code null}) + */ + Collection getMechanismNames(); + + /** + * A builder for a {@link MechanismAuthenticationFactory}. + * + * @param the type of mechanism + * @param the type of the mechanism's factory + * @param the mechanism-type-specific exception that may be thrown upon instantiation + */ + interface Builder { + /** + * Set the security domain to be used for this factory (may not be {@code null}). + * + * @param securityDomain the security domain (may not be {@code null}) + * @return this builder + */ + Builder setSecurityDomain(SecurityDomain securityDomain); + + /** + * Set the {@link MechanismConfigurationSelector} for the factory being built. + * + * @param mechanismConfigurationSelector the {@link MechanismConfigurationSelector} for the factory being built. + * @return this builder + */ + Builder setMechanismConfigurationSelector(MechanismConfigurationSelector mechanismConfigurationSelector); + + /** + * Set the mechanism's underlying factory. + * + * @param factory the factory (must not be {@code null}) + * @return this builder + */ + Builder setFactory(F factory); + + /** + * Build the mechanism factory. + * + * @return the mechanism factory + */ + MechanismAuthenticationFactory build(); + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/server/MechanismConfiguration.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/server/MechanismConfiguration.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/server/MechanismConfiguration.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,296 @@ +/* + * 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.auth.server; + +import static java.util.Arrays.asList; +import static java.util.Collections.emptyList; +import static java.util.Collections.unmodifiableList; +import static org.wildfly.common.Assert.checkNotNullParam; + +import java.security.Principal; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Function; + +import org.wildfly.security.SecurityFactory; +import org.wildfly.security.credential.Credential; +import org.wildfly.security.credential.source.CredentialSource; + +/** + * A configuration that applies to an authentication mechanism. + * + * @author David M. Lloyd + */ +public final class MechanismConfiguration { + private final Function preRealmRewriter; + private final Function postRealmRewriter; + private final Function finalRewriter; + private final RealmMapper realmMapper; + private final Map mechanismRealms; + private final CredentialSource serverCredentialSource; + + MechanismConfiguration(final Function preRealmRewriter, final Function postRealmRewriter, final Function finalRewriter, final RealmMapper realmMapper, final Collection mechanismRealms, final CredentialSource serverCredentialSource) { + checkNotNullParam("mechanismRealms", mechanismRealms); + this.preRealmRewriter = preRealmRewriter; + this.postRealmRewriter = postRealmRewriter; + this.finalRewriter = finalRewriter; + this.realmMapper = realmMapper; + final Iterator iterator = mechanismRealms.iterator(); + if (! iterator.hasNext()) { + // zero + this.mechanismRealms = Collections.emptyMap(); + } else { + MechanismRealmConfiguration item = iterator.next(); + if (! iterator.hasNext()) { + // one + this.mechanismRealms = Collections.singletonMap(item.getRealmName(), item); + } else { + // two or more + Map map = new LinkedHashMap<>(mechanismRealms.size()); + map.put(item.getRealmName(), item); + do { + item = iterator.next(); + map.put(item.getRealmName(), item); + } while (iterator.hasNext()); + this.mechanismRealms = Collections.unmodifiableMap(map); + } + } + this.serverCredentialSource = serverCredentialSource; + } + + /** + * Get the pre-realm rewriter for this mechanism realm. + * + * @return the pre-realm rewriter for this mechanism realm, or {@code null} to use the default + */ + public Function getPreRealmRewriter() { + return preRealmRewriter; + } + + /** + * Get the post-realm rewriter for this mechanism realm. + * + * @return the post-realm rewriter for this mechanism realm, or {@code null} to use the default + */ + public Function getPostRealmRewriter() { + return postRealmRewriter; + } + + /** + * Get the final rewriter for this mechanism realm. + * + * @return the final rewriter for this mechanism realm, or {@code null} to use the default + */ + public Function getFinalRewriter() { + return finalRewriter; + } + + /** + * Get the realm mapper. + * + * @return the realm mapper, or {@code null} to use the default + */ + public RealmMapper getRealmMapper() { + return realmMapper; + } + + /** + * Get the collection of mechanism realm names, in order. If no realms are configured, the collection will be + * empty. + * + * @return the mechanism realm names to offer (may be empty; not {@code null}) + */ + public Collection getMechanismRealmNames() { + return mechanismRealms.keySet(); + } + + /** + * Get the server credential source. + * + * @return the server credential source + */ + public CredentialSource getServerCredentialSource() { + return serverCredentialSource; + } + + /** + * Get the mechanism realm configuration for the offered realm with the given name. If the realm name is not known, + * {@code null} is returned. If the realm name is equal to one of the names returned by {@link #getMechanismRealmNames()} + * on this same instance then it is guaranteed that {@code null} is never returned. + * + * @param realmName the realm name + * @return the realm configuration, or {@code null} if the realm name is unknown + */ + public MechanismRealmConfiguration getMechanismRealmConfiguration(String realmName) { + return mechanismRealms.get(realmName); + } + + /** + * Obtain a new {@link Builder} capable of building a {@link MechanismConfiguration}. + * + * @return a new {@link Builder} capable of building a {@link MechanismConfiguration}. + */ + public static Builder builder() { + return new Builder(); + } + + /** + * A builder for authentication mechanism configuration. + */ + public static final class Builder { + private static final MechanismRealmConfiguration[] NO_REALM_CONFIGS = new MechanismRealmConfiguration[0]; + + private Function preRealmRewriter = Function.identity(); + private Function postRealmRewriter = Function.identity(); + private Function finalRewriter = Function.identity(); + private RealmMapper realmMapper; + private List mechanismRealms; + private CredentialSource serverCredentialSource = CredentialSource.NONE; + + /** + * Construct a new instance. + */ + Builder() { + } + + /** + * Set a principal transformer to apply before the realm is selected. + * + * @param preRealmRewriter a principal transformer to apply before the realm is selected + * @return this builder + */ + public Builder setPreRealmRewriter(final Function preRealmRewriter) { + checkNotNullParam("preRealmRewriter", preRealmRewriter); + this.preRealmRewriter = preRealmRewriter; + return this; + } + + /** + * Set a principal transformer to apply after the realm is selected. Any previously set credential source will be overwritten. + * + * @param postRealmRewriter a principal transformer to apply after the realm is selected + * @return this builder + */ + public Builder setPostRealmRewriter(final Function postRealmRewriter) { + checkNotNullParam("postRealmRewriter", postRealmRewriter); + this.postRealmRewriter = postRealmRewriter; + return this; + } + + /** + * Set a final principal transformer to apply for this mechanism realm. Any previously set credential source will be overwritten. + * + * @param finalRewriter a final principal transformer to apply for this mechanism realm + * @return this builder + */ + public Builder setFinalRewriter(final Function finalRewriter) { + checkNotNullParam("finalRewriter", finalRewriter); + this.finalRewriter = finalRewriter; + return this; + } + + /** + * Sets a realm mapper to be used by the mechanism. Any previously set credential source will be overwritten. + * + * @param realmMapper a realm mapper to be used by the mechanism + * @return this builder + */ + public Builder setRealmMapper(final RealmMapper realmMapper) { + this.realmMapper = realmMapper; + return this; + } + + /** + * Adds a configuration for one of realms of this mechanism. + * + * @param configuration a configuration for one of realms of this mechanism + * @return this builder + */ + public Builder addMechanismRealm(MechanismRealmConfiguration configuration) { + checkNotNullParam("configuration", configuration); + List mechanismRealms = this.mechanismRealms; + if (mechanismRealms == null) { + mechanismRealms = this.mechanismRealms = new ArrayList<>(1); + } + mechanismRealms.add(configuration); + return this; + } + + /** + * Set a single server credential. Any previously set credential source will be overwritten. + * + * @param credential the credential to set (must not be {@code null}) + * @return this builder + */ + public Builder setServerCredential(Credential credential) { + checkNotNullParam("credential", credential); + return setServerCredentialSource(IdentityCredentials.NONE.withCredential(credential)); + } + + /** + * Set a single server credential factory. Any previously set credential source will be overwritten. + * + * @param credentialFactory the credential factory to set (must not be {@code null}) + * @return this builder + */ + public Builder setServerCredential(SecurityFactory credentialFactory) { + checkNotNullParam("credential", credentialFactory); + return setServerCredentialSource(CredentialSource.fromSecurityFactory(credentialFactory)); + } + + /** + * Set the server credential source. Any previously set credential source will be overwritten. + * + * @param serverCredentialSource the server credential source (must not be {@code null}) + * @return this builder + */ + public Builder setServerCredentialSource(final CredentialSource serverCredentialSource) { + checkNotNullParam("serverCredentialSource", serverCredentialSource); + this.serverCredentialSource = serverCredentialSource; + return this; + } + + /** + * Build a new instance. If no mechanism realms are offered, an empty collection should be provided for + * {@code mechanismRealms}; otherwise, if the mechanism only supports one realm, the first will be used. If the + * mechanism does not support realms, {@code mechanismRealms} is ignored. + * + * @return a new instance + */ + public MechanismConfiguration build() { + List mechanismRealms = this.mechanismRealms; + if (mechanismRealms == null) { + mechanismRealms = emptyList(); + } else { + mechanismRealms = unmodifiableList(asList(mechanismRealms.toArray(NO_REALM_CONFIGS))); + } + return new MechanismConfiguration(preRealmRewriter, postRealmRewriter, finalRewriter, realmMapper, mechanismRealms, serverCredentialSource); + } + } + + /** + * An empty mechanism configuration.. + */ + public static final MechanismConfiguration EMPTY = new MechanismConfiguration(Function.identity(), Function.identity(), Function.identity(), null, emptyList(), CredentialSource.NONE); +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/server/MechanismConfigurationSelector.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/server/MechanismConfigurationSelector.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/server/MechanismConfigurationSelector.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,81 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2016 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.auth.server; + +import java.util.function.Predicate; + +/** + * A selector to choose which {@link MechanismConfiguration} to use based on information know about the current authentication + * attempt. + * + * @author Darran Lofthouse + */ +@FunctionalInterface +public interface MechanismConfigurationSelector { + + /** + * Select the {@link MechanismConfiguration} to use for the current authentication attempt. + * + * @param mechanismInformation information about the current authentication attempt. + * @return the {@link MechanismConfiguration} to use for the current authentication attempt. + */ + MechanismConfiguration selectConfiguration(MechanismInformation mechanismInformation); + + /** + * Create a simple {@link MechanismConfigurationSelector} that is paired with a {@link Predicate} to + * test if the configuration should be used for the supplied information. + * + * @param predicate the predicate to test the {@code MechanismInformation} + * @param mechanismConfiguration the {@code MechanismConfiguration} to return if the test passes. + * @return a simple {@code MechanismConfigurationSelector} backed by a {@link Predicate} to test if the configuration should be returned. + */ + static MechanismConfigurationSelector predicateSelector(final Predicate predicate, final MechanismConfiguration mechanismConfiguration) { + return information -> predicate.test(information) ? mechanismConfiguration : null; + } + + /** + * Create a {@link MechanismConfigurationSelector} that is an aggregation of other selectors, when called the selectors will be called in order and the first + * {@link MechanismConfiguration} returned will be used. + * + * @param configurationSelectors the {@link MechanismConfigurationSelector} instances to aggregate. + * @return the {@link MechanismConfigurationSelector} that is an aggregation of the supplied selectors. + */ + static MechanismConfigurationSelector aggregate(final MechanismConfigurationSelector ... configurationSelectors) { + return information -> { + for (MechanismConfigurationSelector current : configurationSelectors) { + MechanismConfiguration configuration = current.selectConfiguration(information); + if (configuration != null) { + return configuration; + } + } + return null; + }; + } + + /** + * Create a constant {@link MechanismConfigurationSelector} which will always return the same {@link MechanismConfiguration} + * instance. + * + * @param mechanismConfiguration a configuration which will be always returned by created selector + * @return the new configuration selector + */ + static MechanismConfigurationSelector constantSelector(final MechanismConfiguration mechanismConfiguration) { + return information -> mechanismConfiguration; + } + +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/server/MechanismInformation.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/server/MechanismInformation.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/server/MechanismInformation.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,79 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2016 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.auth.server; + +/** + * Information about the current mechanism being used for authentication. + * + * @author Darran Lofthouse + */ +public interface MechanismInformation { + + /** + * Get the type of the authentication mechanism. + * + * @return the type of the authentication mechanism. + */ + String getMechanismType(); + + + /** + * Get the name of the current authentication mechanism. + * + * @return the name of the current authentication mechanism. + */ + String getMechanismName(); + + /** + * Get the name of the host the current authentication attempt is for. + * + * @return the name of the host the current authentication attempt is for. + */ + String getHostName(); + + /** + * Get the protocol for the current authentication attempt. + * + * @return the protocol for the current authentication attempt. + */ + String getProtocol(); + + MechanismInformation DEFAULT = new MechanismInformation() { + + @Override + public String getProtocol() { + return null; + } + + @Override + public String getMechanismType() { + return null; + } + + @Override + public String getMechanismName() { + return null; + } + + @Override + public String getHostName() { + return null; + } + }; + +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/server/MechanismRealmConfiguration.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/server/MechanismRealmConfiguration.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/server/MechanismRealmConfiguration.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,201 @@ +/* + * 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.auth.server; + +import java.security.Principal; +import java.util.function.Function; + +import org.wildfly.common.Assert; + +/** + * A configuration for a single mechanism realm. + * + * @author David M. Lloyd + */ +public final class MechanismRealmConfiguration { + private final String realmName; + private final Function preRealmRewriter; + private final Function postRealmRewriter; + private final Function finalRewriter; + private final RealmMapper realmMapper; + + /** + * Construct a new instance. + * + * @param realmName the name of this realm (may not be {@code null}) + * @param preRealmRewriter the pre-realm rewriter to apply (may not be {@code null}) + * @param postRealmRewriter the post-realm rewriter to apply (may not be {@code null}) + * @param finalRewriter the final rewriter to apply (may not be {@code null}) + * @param realmMapper the realm mapper to use + */ + MechanismRealmConfiguration(final String realmName, final Function preRealmRewriter, final Function postRealmRewriter, final Function finalRewriter, final RealmMapper realmMapper) { + this.realmName = realmName; + this.preRealmRewriter = preRealmRewriter; + this.postRealmRewriter = postRealmRewriter; + this.finalRewriter = finalRewriter; + this.realmMapper = realmMapper; + } + + /** + * Get the mechanism realm name. + * + * @return the mechanism realm name (not {@code null}) + */ + public String getRealmName() { + return realmName; + } + + /** + * Get the pre-realm rewriter for this mechanism realm. + * + * @return the pre-realm rewriter for this mechanism realm (not {@code null}) + */ + public Function getPreRealmRewriter() { + return preRealmRewriter; + } + + /** + * Get the post-realm rewriter for this mechanism realm. + * + * @return the post-realm rewriter for this mechanism realm (not {@code null}) + */ + public Function getPostRealmRewriter() { + return postRealmRewriter; + } + + /** + * Get the final rewriter for this mechanism realm. + * + * @return the final rewriter for this mechanism realm (not {@code null}) + */ + public Function getFinalRewriter() { + return finalRewriter; + } + + /** + * Get the realm mapper for this mechanism realm. + * + * @return the realm mapper for this mechanism realm, or {@code null} to use the default + */ + public RealmMapper getRealmMapper() { + return realmMapper; + } + + /** + * A realm configuration for no particular realm, which does no additional rewriting. + */ + public static final MechanismRealmConfiguration NO_REALM = new MechanismRealmConfiguration("none", Function.identity(), Function.identity(), Function.identity(), null); + + /** + * Obtain a new {@link Builder} capable of building a {@link MechanismRealmConfiguration}. + * + * @return a new {@link Builder} capable of building a {@link MechanismRealmConfiguration}. + */ + public static Builder builder() { + return new Builder(); + } + + /** + * A builder for mechanism realm configuration. + */ + public static final class Builder { + private String realmName; + private Function preRealmRewriter = Function.identity(); + private Function postRealmRewriter = Function.identity(); + private Function finalRewriter = Function.identity(); + private RealmMapper realmMapper; + + /** + * Construct a new instance. + */ + Builder() { + } + + /** + * Sets a name of the realm to be presented by the mechanism. + * @param realmName a name of the realm to be presented by the mechanism + * @return this builder + */ + public Builder setRealmName(final String realmName) { + this.realmName = realmName; + + return this; + } + + /** + * Set a principal transformer to apply before the realm is selected. + * + * @param preRealmRewriter a principal transformer to apply before the realm is selected + * @return this builder + */ + public Builder setPreRealmRewriter(final Function preRealmRewriter) { + Assert.checkNotNullParam("preRealmRewriter", preRealmRewriter); + this.preRealmRewriter = preRealmRewriter; + + return this; + } + + /** + * Set a principal transformer to apply after the realm is selected. Any previously set credential source will be overwritten. + * + * @param postRealmRewriter a principal transformer to apply after the realm is selected + * @return this builder + */ + public Builder setPostRealmRewriter(final Function postRealmRewriter) { + Assert.checkNotNullParam("postRealmRewriter", postRealmRewriter); + this.postRealmRewriter = postRealmRewriter; + + return this; + } + + /** + * Set a final principal transformer to apply for this mechanism realm. Any previously set credential source will be overwritten. + * + * @param finalRewriter a final principal transformer to apply for this mechanism realm + * @return this builder + */ + public Builder setFinalRewriter(final Function finalRewriter) { + Assert.checkNotNullParam("finalRewriter", finalRewriter); + this.finalRewriter = finalRewriter; + + return this; + } + + /** + * Sets a realm mapper to be used by the mechanism. Any previously set credential source will be overwritten. + * + * @param realmMapper a realm mapper to be used by the mechanism + * @return this builder + */ + public Builder setRealmMapper(final RealmMapper realmMapper) { + this.realmMapper = realmMapper; + return this; + } + + /** + * Build a new instance. + * + * @return a new instance + */ + public MechanismRealmConfiguration build() { + Assert.checkNotNullParam("realmName", realmName); + return new MechanismRealmConfiguration(realmName, preRealmRewriter, postRealmRewriter, finalRewriter, realmMapper); + } + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/server/ModifiableRealmIdentity.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/server/ModifiableRealmIdentity.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/server/ModifiableRealmIdentity.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,127 @@ +/* + * 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.auth.server; + +import java.security.Principal; +import java.security.spec.AlgorithmParameterSpec; +import java.util.Collection; +import java.util.Collections; + +import org.wildfly.common.Assert; +import org.wildfly.security.auth.server._private.ElytronMessages; +import org.wildfly.security.auth.SupportLevel; +import org.wildfly.security.authz.Attributes; +import org.wildfly.security.credential.Credential; +import org.wildfly.security.evidence.Evidence; + +/** + * A realm identity which is modifiable. + * + * @author David M. Lloyd + */ +public interface ModifiableRealmIdentity extends RealmIdentity { + + /** + * Delete this realm identity. After this call, {@link #exists()} will return {@code false}. If the identity + * does not exist, an exception is thrown. + * + * @throws RealmUnavailableException if deletion fails for some reason + */ + void delete() throws RealmUnavailableException; + + /** + * Create this realm identity. After this call, {@link #exists()} will return {@code true} and the credentials + * and role sets will be empty. If the identity already exists, an exception is thrown. + * + * @throws RealmUnavailableException if creation fails for some reason + */ + void create() throws RealmUnavailableException; + + /** + * Set the credentials of this identity. If the identity does not exist, an exception is thrown. + * Any existing credential(s) are replaced/updated with the new value (in a possibly realm-specific manner). + * + * @param credentials the new credentials to set + * @throws RealmUnavailableException if updating the credentials fails for some reason + */ + void setCredentials(Collection credentials) throws RealmUnavailableException; + + default void updateCredential(Credential credential) throws RealmUnavailableException { + // todo: Need a way to just replace a single credential instead + setCredentials(Collections.singletonList(credential)); + } + + /** + * Modify the attributes collection of this identity. If the identity does not exist, an exception is thrown. + * + * @param attributes the new attributes collection + * @throws RealmUnavailableException if updating the attributes collection fails for some reason + */ + void setAttributes(Attributes attributes) throws RealmUnavailableException; + + /** + * A modifiable identity for a non-existent user who cannot be created. + */ + ModifiableRealmIdentity NON_EXISTENT = new ModifiableRealmIdentity() { + @Override + public Principal getRealmIdentityPrincipal() { + return null; + } + + public SupportLevel getCredentialAcquireSupport(final Class credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException { + Assert.checkNotNullParam("credentialType", credentialType); + return SupportLevel.UNSUPPORTED; + } + + public SupportLevel getEvidenceVerifySupport(final Class evidenceType, final String algorithmName) throws RealmUnavailableException { + Assert.checkNotNullParam("evidenceType", evidenceType); + return SupportLevel.UNSUPPORTED; + } + + public C getCredential(final Class credentialType) throws RealmUnavailableException { + Assert.checkNotNullParam("credentialType", credentialType); + return null; + } + + public boolean verifyEvidence(final Evidence evidence) throws RealmUnavailableException { + Assert.checkNotNullParam("evidence", evidence); + return false; + } + + public boolean exists() throws RealmUnavailableException { + return false; + } + + public void delete() throws RealmUnavailableException { + // no operation + } + + public void create() throws RealmUnavailableException { + throw ElytronMessages.log.unableToCreateIdentity(); + } + + public void setCredentials(final Collection credentials) throws RealmUnavailableException { + throw ElytronMessages.log.noSuchIdentity(); + } + + public void setAttributes(final Attributes attributes) throws RealmUnavailableException { + throw ElytronMessages.log.noSuchIdentity(); + } + }; +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/server/ModifiableRealmIdentityIterator.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/server/ModifiableRealmIdentityIterator.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/server/ModifiableRealmIdentityIterator.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,54 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2016 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.auth.server; + +import java.util.Iterator; + +/** + * An iterator over realm identities. The iterator should be closed in order to release any associated + * resources. + * + * @author Jan Kalina + */ +public interface ModifiableRealmIdentityIterator extends Iterator, AutoCloseable { + + /** + * Close any underlying resources. No need to call if end of sequence already occurred. + */ + default void close() throws RealmUnavailableException {} + + /** + * Returns an iterator that has no elements and is closeable. + * + * @return empty closeable iterator + */ + static ModifiableRealmIdentityIterator emptyIterator() { + return new ModifiableRealmIdentityIterator() { + @Override + public boolean hasNext() { + return false; + } + @Override + public ModifiableRealmIdentity next() { + return null; + } + }; + } + +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/server/ModifiableSecurityRealm.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/server/ModifiableSecurityRealm.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/server/ModifiableSecurityRealm.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,78 @@ +/* + * 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.auth.server; + +import java.security.Principal; + +import org.wildfly.security.evidence.Evidence; + +/** + * A realm which can be modified. + * + * @author David M. Lloyd + */ +public interface ModifiableSecurityRealm extends SecurityRealm { + + /** + * Get an update handle for to the identity for the given principal in the context of this security realm. Any + * validation / name mapping is an implementation detail for the realm. The identity may or may not exist. The + * returned handle must be cleaned up by a call to {@link ModifiableRealmIdentity#dispose()}. During + * the lifespan of a {@code ModifiableRealmIdentity}, no other updates or authentications may take place for the + * corresponding realm identity, thus care should be taken to minimize the duration of the identity's lifespan. + *

+ * If there is not enough information to locate an identity compatible with this realm, {@link + * ModifiableRealmIdentity#NON_EXISTENT} + * may be returned. + * + * @param principal the principal to use to locate the {@link ModifiableRealmIdentity} handle (must not be {@code + * null}) + * @return the {@link ModifiableRealmIdentity} for the provided information (not {@code null}) + */ + default ModifiableRealmIdentity getRealmIdentityForUpdate(Principal principal) throws RealmUnavailableException { + return ModifiableRealmIdentity.NON_EXISTENT; + } + + /** + * Get an update handle for to the identity for the given principal in the context of this security realm. Any + * validation / name mapping is an implementation detail for the realm. The identity may or may not exist. The + * returned handle must be cleaned up by a call to {@link ModifiableRealmIdentity#dispose()}. During + * the lifespan of a {@code ModifiableRealmIdentity}, no other updates or authentications may take place for the + * corresponding realm identity, thus care should be taken to minimize the duration of the identity's lifespan. + *

+ * If there is not enough information to locate an identity compatible with this realm, {@link + * ModifiableRealmIdentity#NON_EXISTENT} + * may be returned. + * + * @param evidence the evidence to use to locate the {@link ModifiableRealmIdentity} handle (must not be {@code + * null}) + * @return the {@link ModifiableRealmIdentity} for the provided information (not {@code null}) + */ + default ModifiableRealmIdentity getRealmIdentityForUpdate(Evidence evidence) throws RealmUnavailableException { + final Principal principal = evidence.getDecodedPrincipal(); + return principal == null ? ModifiableRealmIdentity.NON_EXISTENT : getRealmIdentityForUpdate(principal); + } + + /** + * Get an iterator over all of this realm's identities. + * + * @return the identity iterator + * @throws RealmUnavailableException if the realm fails for some reason + */ + ModifiableRealmIdentityIterator getRealmIdentityIterator() throws RealmUnavailableException; +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/server/NameRewriter.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/server/NameRewriter.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/server/NameRewriter.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,166 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2013 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.auth.server; + +import java.security.Principal; +import java.util.function.UnaryOperator; + +import org.wildfly.common.Assert; +import org.wildfly.security.auth.principal.NamePrincipal; + +/** + * A name rewriter. Name rewriters transform names from one form to another for various purposes, including (but + * not limited to): + *

    + *
  • Normalizing case
  • + *
  • Trimming extra whitespace
  • + *
  • Mapping one naming scheme to another (e.g. "user@realm" to/from "realm/user" or similar)
  • + *
  • Removing a realm component (e.g. "user@realm" to "user")
  • + *
+ * A name rewriter may also be used to perform a validation step on the syntax of the name. If the rewriter returns + * {@code null}, the name is not valid according to the rules of the rewriter. + * + * @author David M. Lloyd + */ +@FunctionalInterface +public interface NameRewriter { + + /** + * The simple identity name rewriter, which does no rewriting. + */ + NameRewriter IDENTITY_REWRITER = original -> original; + + /** + * Rewrite a name. Must not return {@code null}. + * + * @param original the original name (must not be {@code null}) + * @return the rewritten name, or {@code null} if the name is invalid + */ + String rewriteName(String original); + + /** + * Get this name rewriter as a principal rewriter that applies only to {@link NamePrincipal} instances. + * + * @return the principal rewriter (not {@code null}) + */ + default UnaryOperator asPrincipalRewriter() { + return principal -> { + if (principal == null) return null; + if (principal instanceof NamePrincipal) { + String rewritten = NameRewriter.this.rewriteName(principal.getName()); + return rewritten == null ? null : new NamePrincipal(rewritten); + } + return principal; + }; + } + + /** + * Create a name rewriter which chains the given name rewriters; the name will be rewritten through the given rewriters + * in order and then returned. If any rewriter returns {@code null}, then {@code null} is returned. + * + * @param rewriter1 the first name rewriter (must not be {@code null}) + * @param rewriter2 the second name rewriter (must not be {@code null}) + * @return the name rewriter (not {@code null}) + */ + static NameRewriter chain(NameRewriter rewriter1, NameRewriter rewriter2) { + Assert.checkNotNullParam("rewriter1", rewriter1); + Assert.checkNotNullParam("rewriter2", rewriter2); + return n -> { + if (n != null) n = rewriter1.rewriteName(n); + if (n != null) n = rewriter2.rewriteName(n); + return n; + }; + } + + /** + * Create a name rewriter which chains the given name rewriters; the name will be rewritten through the given rewriters + * in order and then returned. If any rewriter returns {@code null}, then {@code null} is returned. + * + * @param nameRewriters the name rewriters (must not be {@code null}, cannot have {@code null} elements) + * @return the name rewriter (not {@code null}) + */ + static NameRewriter chain(NameRewriter... nameRewriters) { + Assert.checkNotNullParam("nameRewriters", nameRewriters); + final NameRewriter[] clone = nameRewriters.clone(); + for (int i = 0; i < clone.length; i++) { + Assert.checkNotNullArrayParam("nameRewriters", i, clone[i]); + } + return n -> { + for (NameRewriter r : clone) { + if (n == null) return null; + n = r.rewriteName(n); + } + return n; + }; + } + + /** + * Create a name rewriter which aggregates the given name rewriters; the first rewriter which successfully rewrites + * the name is used. If all the rewriters return {@code null}, then {@code null} is returned. + * + * @param rewriter1 the first name rewriter (must not be {@code null}) + * @param rewriter2 the second name rewriter (must not be {@code null}) + * @return the name rewriter (not {@code null}) + */ + static NameRewriter aggregate(NameRewriter rewriter1, NameRewriter rewriter2) { + Assert.checkNotNullParam("rewriter1", rewriter1); + Assert.checkNotNullParam("rewriter2", rewriter2); + return n -> { + String rn = rewriter1.rewriteName(n); + if (rn == null) rn = rewriter2.rewriteName(n); + return rn; + }; + } + + /** + * Create a name rewriter which aggregates the given name rewriters; the first rewriter which successfully rewrites + * the name is used. If all the rewriters return {@code null}, then {@code null} is returned. + * + * @param nameRewriters the name rewriters (must not be {@code null}, cannot have {@code null} elements) + * @return the name rewriter (not {@code null}) + */ + static NameRewriter aggregate(NameRewriter... nameRewriters) { + Assert.checkNotNullParam("nameRewriters", nameRewriters); + final NameRewriter[] clone = nameRewriters.clone(); + for (int i = 0; i < clone.length; i++) { + Assert.checkNotNullArrayParam("nameRewriters", i, clone[i]); + } + return n -> { + if (n == null) return null; + String rn; + for (NameRewriter r : clone) { + rn = r.rewriteName(n); + if (rn != null) { + return rn; + } + } + return null; + }; + } + + /** + * Create a name rewriter which always returns the same name. + * + * @param name the name to return + * @return the name + */ + static NameRewriter constant(String name) { + return original -> name; + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/server/PrincipalDecoder.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/server/PrincipalDecoder.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/server/PrincipalDecoder.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,165 @@ +/* + * 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.auth.server; + +import java.security.Principal; +import java.util.StringJoiner; +import java.util.function.Function; +import java.util.function.UnaryOperator; + +import org.wildfly.common.Assert; +import org.wildfly.security.auth.principal.NamePrincipal; + +/** + * A decoder for extracting a simple name from a principal. + * + * @author David M. Lloyd + */ +@FunctionalInterface +public interface PrincipalDecoder extends Function { + + /** + * Get the name from a principal. If this decoder cannot understand the given principal type or contents, + * {@code null} is returned. + * + * @param principal the principal to decode + * @return the name, or {@code null} if this decoder does not understand the principal + */ + String getName(Principal principal); + + default String apply(Principal principal) { + return getName(principal); + } + + /** + * Get this principal decoder as a principal rewriter that produces a {@link NamePrincipal} if the decode succeeds. + * + * @return the rewriter (not {@code null}) + */ + default UnaryOperator asPrincipalRewriter() { + return principal -> { + String result = PrincipalDecoder.this.getName(principal); + return result == null ? principal : new NamePrincipal(result); + }; + } + + /** + * Add a name rewriter to this principal decoder. If the name is decoded, it will then be rewritten with the + * given rewriter. If the rewriter deems the name invalid, then the name will be considered not decoded. + * + * @param nameRewriter the name rewriter + * @return the combined decoder + */ + default PrincipalDecoder withRewriter(NameRewriter nameRewriter) { + return principal -> { + final String name = this.getName(principal); + return name == null ? null : nameRewriter.rewriteName(name); + }; + } + + /** + * Create an aggregated credential decoder. The aggregated decoder will check each credential decoder until one + * matches the credential; this result will be returned. + * + * @param decoders the constituent decoders + * @return the aggregated decoder + */ + static PrincipalDecoder aggregate(final PrincipalDecoder... decoders) { + Assert.checkNotNullParam("decoders", decoders); + return principal -> { + String result; + for (PrincipalDecoder decoder : decoders) { + result = decoder.getName(principal); + if (result != null) { + return result; + } + } + return null; + }; + } + + /** + * Create a principal decoder which concatenates the results of two principal decoders. If one decoder is not able + * to decode the principal, {@code null} is returned. + * + * @param former the former decoder + * @param joinString the string to use to join the results + * @param latter the latter decoder + * @return the concatenated result + */ + static PrincipalDecoder concatenating(final PrincipalDecoder former, final String joinString, final PrincipalDecoder latter) { + Assert.checkNotNullParam("former", former); + Assert.checkNotNullParam("joinString", joinString); + Assert.checkNotNullParam("latter", latter); + return principal -> { + final String formerName = former.getName(principal); + final String latterName = latter.getName(principal); + if (formerName == null || latterName == null) { + return null; + } else { + return formerName + joinString + latterName; + } + }; + } + + /** + * Create a principal decoder that concatenates the results of the given principal decoders in the order in which + * they're given. If any decoder is not able to decode the principal, then {@code null} is returned. + * + * @param joinString the string to use to join the results + * @param decoders the principal decoders (must not be {@code null}, cannot have {@code null} elements) + * @return the concatenating decoder + */ + static PrincipalDecoder concatenating(final String joinString, final PrincipalDecoder... decoders) { + Assert.checkNotNullParam("joinString", joinString); + Assert.checkNotNullParam("decoders", decoders); + return principal -> { + final StringJoiner concatenatedResult = new StringJoiner(joinString); + String result; + for (PrincipalDecoder decoder : decoders) { + result = decoder.getName(principal); + if (result == null) { + return null; + } + concatenatedResult.add(result); + } + return concatenatedResult.toString(); + }; + } + + /** + * Create a principal decoder which always returns the same name. + * + * @param name the name to return + * @return the constant decoder + */ + static PrincipalDecoder constant(String name) { + return principal -> name; + } + + /** + * The default decoder, which just calls {@link Principal#getName()}. + */ + PrincipalDecoder DEFAULT = Principal::getName; + + /** + * A principal decoder which cannot decode any principal. + */ + PrincipalDecoder UNKNOWN = p -> null; +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/server/RealmIdentity.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/server/RealmIdentity.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/server/RealmIdentity.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,342 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2014 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.auth.server; + +import static org.wildfly.security.auth.server._private.ElytronMessages.log; + +import java.security.Principal; +import java.security.spec.AlgorithmParameterSpec; +import java.util.function.Function; + +import org.wildfly.common.Assert; +import org.wildfly.security.auth.SupportLevel; +import org.wildfly.security.auth.principal.AnonymousPrincipal; +import org.wildfly.security.authz.Attributes; +import org.wildfly.security.authz.AuthorizationIdentity; +import org.wildfly.security.credential.AlgorithmCredential; +import org.wildfly.security.credential.Credential; +import org.wildfly.security.evidence.Evidence; +import org.wildfly.security.key.KeyUtil; + +/** + * A representation of a pre-authentication identity. + * + * The life of a {@code RealmIdentity} is short and is for a specific authentication attempt. A {@link SecurityRealm} creating a + * {@code RealmIdentity} does not confirm the existence of the identity. The {@link #exists()} method must be used + * for that purpose. + * + * @author Darran Lofthouse + */ +public interface RealmIdentity { + + /** + * Get the principal that canonically identifies the identity within the realm. This method may return the principal object + * which was passed in as a parameter to {@link SecurityRealm#getRealmIdentity(Principal)} (a.k.a. domain principal), but + * is not required to do so. Any existent realm identity (i.e. any identity which returns {@code true} on invocation + * of {@link #exists()}) must return a non-{@code null} principal. + * + * @return the principal for this realm identity (may not be {@code null}) + */ + Principal getRealmIdentityPrincipal(); + + /** + * @deprecated Transition method; remove before GA. + */ + default SupportLevel getCredentialAcquireSupport(Class credentialType, String algorithmName) throws RealmUnavailableException { + return getCredentialAcquireSupport(credentialType, algorithmName, null); + } + + /** + * Determine whether a given credential type is definitely obtainable, possibly obtainable, or definitely not + * obtainable for this identity. + * + * @param credentialType the exact credential type (must not be {@code null}) + * @param algorithmName the algorithm name, or {@code null} if any algorithm is acceptable or the credential type does + * not support algorithm names + * @param parameterSpec the algorithm parameters to match, or {@code null} if any parameters are acceptable or the credential type + * does not support algorithm parameters + * @return the level of support for this credential type (may not be {@code null}) + * @throws RealmUnavailableException if the realm is not able to handle requests for any reason + */ + SupportLevel getCredentialAcquireSupport(Class credentialType, String algorithmName, AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException; + + /** + * Acquire a credential of the given type. + * + * @param credentialType the credential type class (must not be {@code null}) + * @param the credential type + * @return the credential, or {@code null} if no such credential exists + * @throws RealmUnavailableException if the realm is not able to handle requests for any reason + */ + C getCredential(Class credentialType) throws RealmUnavailableException; + + /** + * Acquire a credential of the given type and algorithm name. Realms which support more than one credential of a + * given type must override this method. + * + * @param credentialType the credential type class (must not be {@code null}) + * @param algorithmName the algorithm name, or {@code null} if any algorithm is acceptable or the credential type + * does not support algorithm names + * @param the credential type + * + * @return the credential, or {@code null} if no such credential exists + * + * @throws RealmUnavailableException if the realm is not able to handle requests for any reason + */ + default C getCredential(Class credentialType, String algorithmName) throws RealmUnavailableException { + if (algorithmName != null) { + final C credential = getCredential(credentialType); + return credential instanceof AlgorithmCredential && algorithmName.equals(((AlgorithmCredential) credential).getAlgorithm()) ? credential : null; + } else { + return getCredential(credentialType); + } + } + + /** + * Acquire a credential of the given type and algorithm name. Realms which support more than one credential of a + * given type and algorithm must override this method. + * + * @param credentialType the credential type class (must not be {@code null}) + * @param algorithmName the algorithm name, or {@code null} if any algorithm is acceptable or the credential type + * does not support algorithm names + * @param parameterSpec the algorithm parameters to match, or {@code null} if any parameters are acceptable or the credential type + * does not support algorithm parameters + * @param the credential type + * + * @return the credential, or {@code null} if no such credential exists + * + * @throws RealmUnavailableException if the realm is not able to handle requests for any reason + */ + default C getCredential(Class credentialType, String algorithmName, AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException { + final C credential = getCredential(credentialType, algorithmName); + if (parameterSpec != null) { + if (! (credential instanceof AlgorithmCredential)) { + return null; + } + return KeyUtil.parametersEqual(parameterSpec, ((AlgorithmCredential) credential).getParameters()) ? credential : null; + } else { + return credential; + } + } + + /** + * Apply the given function to the acquired credential, if it is set and of the given type. + * + * @param credentialType the credential type class (must not be {@code null}) + * @param function the function to apply (must not be {@code null}) + * @param the credential type + * @param the return type + * @return the result of the function, or {@code null} if the criteria are not met + * + * @throws RealmUnavailableException if the realm is not able to handle requests for any reason + */ + default R applyToCredential(Class credentialType, Function function) throws RealmUnavailableException { + final Credential credential = getCredential(credentialType); + return credential == null ? null : credential.castAndApply(credentialType, function); + } + + /** + * Apply the given function to the acquired credential, if it is set and of the given type and algorithm. + * + * @param credentialType the credential type class (must not be {@code null}) + * @param algorithmName the algorithm name, or {@code null} if any algorithm is acceptable or the credential type + * does not support algorithm names + * @param function the function to apply (must not be {@code null}) + * @param the credential type + * @param the return type + * @return the result of the function, or {@code null} if the criteria are not met + * + * @throws RealmUnavailableException if the realm is not able to handle requests for any reason + */ + default R applyToCredential(Class credentialType, String algorithmName, Function function) throws RealmUnavailableException { + final Credential credential = getCredential(credentialType, algorithmName); + return credential == null ? null : credential.castAndApply(credentialType, algorithmName, function); + } + + /** + * Apply the given function to the acquired credential, if it is set and of the given type, algorithm, and parameters. + * + * @param credentialType the credential type class (must not be {@code null}) + * @param algorithmName the algorithm name, or {@code null} if any algorithm is acceptable or the credential type + * does not support algorithm names + * @param parameterSpec the algorithm parameters to match, or {@code null} if any parameters are acceptable or the credential type + * does not support algorithm parameters + * @param function the function to apply (must not be {@code null}) + * @param the credential type + * @param the return type + * @return the result of the function, or {@code null} if the criteria are not met + * + * @throws RealmUnavailableException if the realm is not able to handle requests for any reason + */ + default R applyToCredential(Class credentialType, String algorithmName, AlgorithmParameterSpec parameterSpec, Function function) throws RealmUnavailableException { + final Credential credential = getCredential(credentialType, algorithmName, parameterSpec); + return credential == null ? null : credential.castAndApply(credentialType, algorithmName, parameterSpec, function); + } + + /** + * Update a credential of this realm identity. + * + * @param credential the new credential (must not be {@code null}) + * @throws UnsupportedOperationException if the implementing class does not support updating a credential + * @throws RealmUnavailableException if the realm is not able to handle requests for any reason + */ + default void updateCredential(Credential credential) throws RealmUnavailableException { + throw log.credentialUpdateNotSupportedByRealm(); + } + + /** + * Determine whether a given type of evidence is definitely verifiable, possibly verifiable, or definitely not verifiable. + * + * @param evidenceType the type of evidence to be verified (must not be {@code null}) + * @param algorithmName the algorithm name, or {@code null} if any algorithm is acceptable or the evidence type does + * not support algorithm names + * @return the level of support for this evidence type + * @throws RealmUnavailableException if the realm is not able to handle requests for any reason + */ + SupportLevel getEvidenceVerifySupport(Class evidenceType, String algorithmName) throws RealmUnavailableException; + + /** + * Verify the given evidence against a credential of this identity. The credential to be used is selected based on + * the evidence type. + * + * @param evidence the evidence to verify + * @return {@code true} if verification was successful, {@code false} otherwise + * @throws RealmUnavailableException if the realm is not able to handle requests for any reason + */ + boolean verifyEvidence(Evidence evidence) throws RealmUnavailableException; + + /** + * Determine if the identity exists in lieu of verifying or acquiring a credential. This method is intended to be used to + * verify an identity for non-authentication purposes only. + * + * Implementations of this method should return {@code false} up until the point it is known that a call to + * {@link #getAuthorizationIdentity()} can successfully return an identity. + * + * If a realm can load an identity independently of credential acquisition and evidence verification if not already loaded + * it should be loaded at the time of this call to return an accurate result. + * + * @return {@code true} if the identity exists in this realm, {@code false} otherwise + * @throws RealmUnavailableException if the realm is not able to handle requests for any reason + */ + boolean exists() throws RealmUnavailableException; + + /** + * Dispose this realm identity after a completed authentication attempt. + */ + default void dispose() { + } + + /** + * Get an authorization identity for this pre-authenticated identity. + * + * @return the authorization identity (may not be {@code null}) + * + * @throws IllegalStateException if called for an identity that does not exist + * @throws RealmUnavailableException if the realm is not able to handle requests for any reason + */ + default AuthorizationIdentity getAuthorizationIdentity() throws RealmUnavailableException { + if (exists()) { + return AuthorizationIdentity.EMPTY; + } else { + throw log.userDoesNotExist(); + } + } + + /** + * Get the attributes for the realm identity. + * + * @return the attributes, or {@code null} if the implementing class does not support getting attributes + * @throws RealmUnavailableException if accessing the attributes fails for some reason + */ + default Attributes getAttributes() throws RealmUnavailableException { + return null; + } + + /** + * The anonymous realm identity. + */ + RealmIdentity ANONYMOUS = new RealmIdentity() { + public Principal getRealmIdentityPrincipal() { + return AnonymousPrincipal.getInstance(); + } + + public SupportLevel getCredentialAcquireSupport(final Class credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException { + Assert.checkNotNullParam("credentialType", credentialType); + return SupportLevel.UNSUPPORTED; + } + + public SupportLevel getEvidenceVerifySupport(final Class evidenceType, final String algorithmName) throws RealmUnavailableException { + Assert.checkNotNullParam("evidenceType", evidenceType); + return SupportLevel.UNSUPPORTED; + } + + public C getCredential(final Class credentialType) throws RealmUnavailableException { + Assert.checkNotNullParam("credentialType", credentialType); + return null; + } + + public boolean verifyEvidence(final Evidence evidence) throws RealmUnavailableException { + Assert.checkNotNullParam("evidence", evidence); + return false; + } + + public boolean exists() throws RealmUnavailableException { + return true; + } + }; + + /** + * An identity for a non-existent user. + */ + RealmIdentity NON_EXISTENT = new RealmIdentity() { + @Override + public Principal getRealmIdentityPrincipal() { + return null; + } + + public SupportLevel getCredentialAcquireSupport(final Class credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException { + Assert.checkNotNullParam("credentialType", credentialType); + return SupportLevel.UNSUPPORTED; + } + + public SupportLevel getEvidenceVerifySupport(final Class evidenceType, final String algorithmName) throws RealmUnavailableException { + Assert.checkNotNullParam("evidenceType", evidenceType); + return SupportLevel.UNSUPPORTED; + } + + public C getCredential(final Class credentialType) throws RealmUnavailableException { + Assert.checkNotNullParam("credentialType", credentialType); + return null; + } + + public boolean verifyEvidence(final Evidence evidence) throws RealmUnavailableException { + Assert.checkNotNullParam("evidence", evidence); + return false; + } + + public boolean exists() throws RealmUnavailableException { + return false; + } + + @Override + public String toString() { + return "NON_EXISTENT"; + } + }; +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/server/RealmInfo.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/server/RealmInfo.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/server/RealmInfo.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,79 @@ +/* + * JBoss, Home of Professional Open Source + * + * Copyright 2013 Red Hat, Inc. and/or its affiliates. + * + * 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.server; + +import java.security.Principal; +import java.util.function.Function; + +import org.wildfly.security.authz.RoleDecoder; +import org.wildfly.security.authz.RoleMapper; + +/** + *

Holds the reference to a {@link SecurityRealm} and the configuration associated with it.

+ * + * @author Pedro Igor + */ +class RealmInfo { + + private final SecurityRealm securityRealm; + private final String name; + private final RoleMapper roleMapper; + private final Function principalRewriter; + private final RoleDecoder roleDecoder; + + RealmInfo(final SecurityDomain.RealmBuilder realmBuilder) { + this.name = realmBuilder.getName(); + this.securityRealm = realmBuilder.getRealm(); + this.roleMapper = realmBuilder.getRoleMapper(); + this.principalRewriter = realmBuilder.getPrincipalRewriter(); + this.roleDecoder = realmBuilder.getRoleDecoder(); + } + + RealmInfo() { + this.securityRealm = SecurityRealm.EMPTY_REALM; + this.name = "default"; + this.roleMapper = RoleMapper.IDENTITY_ROLE_MAPPER; + this.principalRewriter = Function.identity(); + this.roleDecoder = RoleDecoder.DEFAULT; + } + + String getName() { + return this.name; + } + + SecurityRealm getSecurityRealm() { + return this.securityRealm; + } + + RoleMapper getRoleMapper() { + return this.roleMapper; + } + + Function getPrincipalRewriter() { + return principalRewriter; + } + + RoleDecoder getRoleDecoder() { + return roleDecoder; + } + + @Override + public String toString() { + return "RealmInfo{name='" + name + "', securityRealm=" + securityRealm + "}"; + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/server/RealmMapper.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/server/RealmMapper.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/server/RealmMapper.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,154 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2013 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.auth.server; + +import java.security.Principal; +import java.util.function.BiPredicate; +import java.util.function.Predicate; + +import org.wildfly.common.Assert; +import org.wildfly.security.evidence.Evidence; + +/** + * A realm mapper. Examines authentication identity information and translates it into a realm name. If the realm + * mapper does not recognize the authentication information, a default realm will be chosen. + * + * @author David M. Lloyd + */ +@FunctionalInterface +public interface RealmMapper { + + /** + * Get the realm mapping. Return {@code null} if the default realm should be used. + * + * @param principal the authentication principal (or {@code null} if none is known for this authentication) + * @param evidence the authentication evidence (or {@code null} if none is known for this authentication) + * @return the realm, or {@code null} if no particular realm matches the authentication information + */ + String getRealmMapping(Principal principal, Evidence evidence); + + /** + * A realm mapper which always falls back to a default realm. + */ + RealmMapper DEFAULT_REALM_MAPPER = single(null); + + /** + * Create a realm mapper that always maps to the given realm. + * + * @param realmName the realm name to return, or {@code null} if the default realm should be used + * @return the realm mapper returning {@code realmName} + */ + static RealmMapper single(String realmName) { + return (principal, evidence) -> realmName; + } + + /** + * Create a realm mapper that matches when the given predicate matches. + * + * @param matchRule the match rule (must not be {@code null}) + * @param realmName the realm name to return, or {@code null} to return the default realm + * @return the realm mapper (not {@code null}) + */ + static RealmMapper matching(BiPredicate matchRule, String realmName) { + Assert.checkNotNullParam("matchRule", matchRule); + return (p, e) -> matchRule.test(p, e) ? realmName : null; + } + + /** + * Create a realm mapper that matches when the given predicate matches the principal. + * + * @param matchRule the match rule (must not be {@code null}) + * @param realmName the realm name to return, or {@code null} to return the default realm + * @return the realm mapper (not {@code null}) + */ + static RealmMapper matchingPrincipal(Predicate matchRule, String realmName) { + Assert.checkNotNullParam("matchRule", matchRule); + return matching((p, e) -> matchRule.test(p), realmName); + } + + /** + * Create a realm mapper that matches when the principal is of the given type. + * + * @param principalType the principal type class (must not be {@code null}) + * @param realmName the realm name to return, or {@code null} to return the default realm + * @return the realm mapper (not {@code null}) + */ + static RealmMapper matchingPrincipalType(Class principalType, String realmName) { + Assert.checkNotNullParam("principalType", principalType); + return matchingPrincipal(principalType::isInstance, realmName); + } + + /** + * Create a realm mapper that matches when the given predicate matches the evidence. + * + * @param matchRule the match rule (must not be {@code null}) + * @param realmName the realm name to return, or {@code null} to return the default realm + * @return the realm mapper (not {@code null}) + */ + static RealmMapper matchingEvidence(Predicate matchRule, String realmName) { + Assert.checkNotNullParam("matchRule", matchRule); + return matching((p, e) -> matchRule.test(e), realmName); + } + + /** + * Create a realm mapper that matches when the evidence is of the given type. + * + * @param evidenceType the evidence type class (must not be {@code null}) + * @param realmName the realm name to return, or {@code null} to return the default realm + * @return the realm mapper (not {@code null}) + */ + static RealmMapper matchingEvidenceType(Class evidenceType, String realmName) { + Assert.checkNotNullParam("evidenceType", evidenceType); + return matchingEvidence(evidenceType::isInstance, realmName); + } + + /** + * Create an aggregate realm mapping strategy. + * + * @param mapper1 the first mapper to try (must not be {@code null}) + * @param mapper2 the second mapper to try (must not be {@code null}) + * @return an aggregated mapper (not {@code null}) + */ + static RealmMapper aggregate(RealmMapper mapper1, RealmMapper mapper2) { + Assert.checkNotNullParam("mapper1", mapper1); + Assert.checkNotNullParam("mapper2", mapper2); + return (principal, evidence) -> { + String mapping = mapper1.getRealmMapping(principal, evidence); + if (mapping == null) mapping = mapper2.getRealmMapping(principal, evidence); + return mapping; + }; + } + + /** + * Create an aggregate realm mapping strategy. + * + * @param mappers the mappers to try (must not be {@code null}) + * @return an aggregated mapper (not {@code null}) + */ + static RealmMapper aggregate(RealmMapper... mappers) { + Assert.checkNotNullParam("mappers", mappers); + return (principal, evidence) -> { + for (RealmMapper mapper : mappers) if (mapper != null) { + String mapping = mapper.getRealmMapping(principal, evidence); + if (mapping != null) return mapping; + } + return null; + }; + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/server/RealmUnavailableException.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/server/RealmUnavailableException.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/server/RealmUnavailableException.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,71 @@ +/* + * 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.auth.server; + +import java.io.IOException; + +/** + * An exception to indicate a general underlying failure of the realm. + * + * Realms should only make use of this exception for general failures within the realm such as being unable to communicate with + * a remote store of users rather than to report errors with a specific authentication request. + * + * @author Darran Lofthouse + */ +public class RealmUnavailableException extends IOException { + + private static final long serialVersionUID = 5893125522523952643L; + + /** + * Constructs a new {@code RealmUnavailableException} instance. The message is left blank ({@code null}), and no + * cause is specified. + */ + public RealmUnavailableException() { + } + + /** + * Constructs a new {@code RealmUnavailableException} instance with an initial message. No cause is specified. + * + * @param msg the message + */ + public RealmUnavailableException(final String msg) { + super(msg); + } + + /** + * Constructs a new {@code RealmUnavailableException} instance with an initial cause. If a non-{@code null} cause + * is specified, its message is used to initialize the message of this {@code RealmUnavailableException}; otherwise + * the message is left blank ({@code null}). + * + * @param cause the cause + */ + public RealmUnavailableException(final Throwable cause) { + super(cause); + } + + /** + * Constructs a new {@code RealmUnavailableException} instance with an initial message and cause. + * + * @param msg the message + * @param cause the cause + */ + public RealmUnavailableException(final String msg, final Throwable cause) { + super(msg, cause); + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/server/Scoped.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/server/Scoped.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/server/Scoped.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,376 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2017 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.auth.server; + +import java.security.PrivilegedAction; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; +import java.util.concurrent.Callable; +import java.util.function.BiConsumer; +import java.util.function.BiFunction; +import java.util.function.BiPredicate; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.IntFunction; +import java.util.function.LongFunction; +import java.util.function.ObjIntConsumer; +import java.util.function.Predicate; +import java.util.function.Supplier; + +import org.wildfly.common.function.ExceptionBiConsumer; +import org.wildfly.common.function.ExceptionBiFunction; +import org.wildfly.common.function.ExceptionBiPredicate; +import org.wildfly.common.function.ExceptionConsumer; +import org.wildfly.common.function.ExceptionFunction; +import org.wildfly.common.function.ExceptionIntFunction; +import org.wildfly.common.function.ExceptionLongFunction; +import org.wildfly.common.function.ExceptionObjIntConsumer; +import org.wildfly.common.function.ExceptionPredicate; +import org.wildfly.common.function.ExceptionSupplier; + +/** + * An identity configuration which can be applied on a scoped basis. + * + * @author David M. Lloyd + */ +public interface Scoped { + /** + * Run an action under this identity. + * + * @param action the action to run + */ + default void runAs(Runnable action) { + if (action == null) return; + runAsConsumer(Runnable::run, action); + } + + /** + * Run an action under this identity. + * + * @param action the action to run + * @param the action return type + * @return the action result (may be {@code null}) + * @throws Exception if the action fails + */ + default T runAs(Callable action) throws Exception { + if (action == null) return null; + return runAsFunctionEx(Callable::call, action); + } + + /** + * Run an action under this identity. + * + * @param parameter the parameter to pass to the action + * @param action the action to run + * @param the action return type + * @param the action parameter type + * @return the action result (may be {@code null}) + */ + default R runAsFunction(Function action, T parameter) { + if (action == null) return null; + return runAsFunction(Function::apply, action, parameter); + } + + /** + * Run an action under this identity. + * + * @param parameter1 the first parameter to pass to the action + * @param parameter2 the second parameter to pass to the action + * @param action the action to run + * @param the action return type + * @param the action first parameter type + * @param the action second parameter type + * @return the action result (may be {@code null}) + */ + R runAsFunction(BiFunction action, T parameter1, U parameter2); + + /** + * Run an action under this identity. + * + * @param parameter the parameter to pass to the action + * @param action the action to run + * @param the action parameter type + */ + default void runAsConsumer(Consumer action, T parameter) { + if (action == null) return; + runAsConsumer(Consumer::accept, action, parameter); + } + + /** + * Run an action under this identity. + * + * @param parameter1 the first parameter to pass to the action + * @param parameter2 the second parameter to pass to the action + * @param action the action to run + * @param the action first parameter type + * @param the action second parameter type + */ + void runAsConsumer(BiConsumer action, T parameter1, U parameter2); + + /** + * Run an action under this identity. + * + * @param parameter1 the first parameter to pass to the action + * @param parameter2 the second parameter to pass to the action + * @param action the action to run + * @param the action first parameter type + */ + void runAsObjIntConsumer(ObjIntConsumer action, T parameter1, int parameter2); + + /** + * Run an action under this identity. + * + * @param action the action to run + * @param the action return type + * @return the action result (may be {@code null}) + */ + default T runAsSupplier(Supplier action) { + if (action == null) return null; + return runAsFunction(Supplier::get, action); + } + + /** + * Run an action under this identity. + * + * @param parameter the parameter to pass to the action + * @param action the action to run + * @param the action return type + * @param the action parameter type + * @param the action exception type + * @return the action result (may be {@code null}) + * @throws E if the action throws this exception + */ + default R runAsFunctionEx(ExceptionFunction action, T parameter) throws E { + if (action == null) return null; + return runAsFunctionEx(ExceptionFunction::apply, action, parameter); + } + + /** + * Run an action under this identity. + * + * @param parameter1 the first parameter to pass to the action + * @param parameter2 the second parameter to pass to the action + * @param action the action to run + * @param the action return type + * @param the action first parameter type + * @param the action second parameter type + * @param the action exception type + * @return the action result (may be {@code null}) + * @throws E if the action throws this exception + */ + R runAsFunctionEx(ExceptionBiFunction action, T parameter1, U parameter2) throws E; + + /** + * Run an action under this identity. + * + * @param parameter the parameter to pass to the action + * @param action the action to run + * @param the action parameter type + * @param the action exception type + * @throws E if the action throws this exception + */ + default void runAsConsumerEx(ExceptionConsumer action, T parameter) throws E { + if (action == null) return; + runAsConsumerEx(ExceptionConsumer::accept, action, parameter); + } + + /** + * Run an action under this identity. + * + * @param parameter1 the first parameter to pass to the action + * @param parameter2 the second parameter to pass to the action + * @param action the action to run + * @param the action first parameter type + * @param the action second parameter type + * @param the action exception type + * @throws E if the action throws this exception + */ + void runAsConsumerEx(ExceptionBiConsumer action, T parameter1, U parameter2) throws E; + + /** + * Run an action under this identity. + * + * @param parameter1 the first parameter to pass to the action + * @param parameter2 the second parameter to pass to the action + * @param action the action to run + * @param the action first parameter type + * @param the action exception type + * @throws E if the action throws this exception + */ + void runAsObjIntConsumerEx(ExceptionObjIntConsumer action, T parameter1, int parameter2) throws E; + + /** + * Run an action under this identity. + * + * @param action the action to run + * @param the action return type + * @param the action exception type + * @return the action result (may be {@code null}) + * @throws E if the action throws this exception + */ + default T runAsSupplierEx(ExceptionSupplier action) throws E { + if (action == null) return null; + return runAsFunctionEx(ExceptionSupplier::get, action); + } + + /** + * Run an action under this identity. + * + * @param action the task to run (must not be {@code null}) + * @param the return value type + * @return the action return value + */ + default R runAsAction(PrivilegedAction action) { + if (action == null) return null; + return runAsFunction(PrivilegedAction::run, action); + } + + /** + * Run an action under this identity. + * + * @param action the task to run (must not be {@code null}) + * @param the return value type + * @return the action return value + * @throws PrivilegedActionException if the action fails with an exception + */ + default R runAsExceptionAction(PrivilegedExceptionAction action) throws PrivilegedActionException { + if (action == null) return null; + try { + return runAsFunctionEx(PrivilegedExceptionAction::run, action); + } catch (Exception e) { + throw new PrivilegedActionException(e); + } + } + + /** + * Run an action under this identity. + * + * @param predicate the task to run (must not be {@code null}) + * @param param1 the first parameter to pass to the task + * @param param2 the second parameter to pass to the task + * @param the first parameter type + * @param the second parameter type + * @return the action return value + * @throws UnsupportedOperationException if this operation is not implemented + */ + default boolean runAsBiPredicate(BiPredicate predicate, T param1, U param2) { + throw new UnsupportedOperationException(); + } + + /** + * Run an action under this identity. + * + * @param predicate the task to run (must not be {@code null}) + * @param param1 the first parameter to pass to the task + * @param param2 the second parameter to pass to the task + * @param the first parameter type + * @param the second parameter type + * @param the exception type + * @return the action return value + * @throws E if an exception occurs in the task + * @throws UnsupportedOperationException if this operation is not implemented + */ + default boolean runAsExBiPredicate(ExceptionBiPredicate predicate, T param1, U param2) throws E { + throw new UnsupportedOperationException(); + } + + /** + * Run an action under this identity. + * + * @param predicate the task to run (must not be {@code null}) + * @param param the parameter to pass to the task + * @param the first parameter type + * @return the action return value + */ + default boolean runAsPredicate(Predicate predicate, T param) { + if (predicate == null) return false; + return runAsBiPredicate(Predicate::test, predicate, param); + } + + /** + * Run an action under this identity. + * + * @param predicate the task to run (must not be {@code null}) + * @param param the parameter to pass to the task + * @param the first parameter type + * @param the exception type + * @return the action return value + * @throws E if an exception occurs in the task + */ + default boolean runAsExPredicate(ExceptionPredicate predicate, T param) throws E { + if (predicate == null) return false; + return runAsExBiPredicate(ExceptionPredicate::test, predicate, param); + } + + /** + * Run an action under this identity. + * + * @param action the task to run (must not be {@code null}) + * @param value the parameter to pass to the task + * @param the return value type + * @return the action return value + */ + default T runAsIntFunction(IntFunction action, int value) { + if (action == null) return null; + return runAsFunction(IntFunction::apply, action, value); + } + + /** + * Run an action under this identity. + * + * @param action the task to run (must not be {@code null}) + * @param value the parameter to pass to the task + * @param the return value type + * @param the exception type + * @return the action return value + * @throws E if an exception occurs in the task + */ + default T runAsExIntFunction(ExceptionIntFunction action, int value) throws E { + if (action == null) return null; + return runAsFunctionEx(ExceptionIntFunction::apply, action, value); + } + + /** + * Run an action under this identity. + * + * @param action the task to run (must not be {@code null}) + * @param value the parameter to pass to the task + * @param the return value type + * @return the action return value + */ + default T runAsLongFunction(LongFunction action, long value) { + if (action == null) return null; + return runAsFunction(LongFunction::apply, action, value); + } + + /** + * Run an action under this identity. + * + * @param action the task to run (must not be {@code null}) + * @param value the parameter to pass to the task + * @param the return value type + * @param the exception type + * @return the action return value + * @throws E if an exception occurs in the task + */ + default T runAsExLongFunction(ExceptionLongFunction action, long value) throws E { + if (action == null) return null; + return runAsFunctionEx(ExceptionLongFunction::apply, action, value); + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/server/SecurityDomain.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/server/SecurityDomain.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/server/SecurityDomain.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,1266 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2013 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.auth.server; + +import static java.security.AccessController.doPrivileged; +import static java.util.Collections.emptyMap; +import static org.wildfly.common.Assert.checkNotNullParam; +import static org.wildfly.security.auth.server._private.ElytronMessages.log; + +import java.security.Principal; +import java.security.PrivilegedAction; +import java.security.spec.AlgorithmParameterSpec; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Executors; +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledThreadPoolExecutor; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.function.Supplier; +import java.util.function.UnaryOperator; + +import org.wildfly.common.Assert; +import org.wildfly.common.function.ExceptionBiFunction; +import org.wildfly.common.function.ExceptionFunction; +import org.wildfly.security.auth.SupportLevel; +import org.wildfly.security.auth.principal.AnonymousPrincipal; +import org.wildfly.security.auth.principal.NamePrincipal; +import org.wildfly.security.auth.principal.RealmNestedPrincipal; +import org.wildfly.security.auth.server.event.SecurityEvent; +import org.wildfly.security.authz.AuthorizationIdentity; +import org.wildfly.security.authz.PermissionMapper; +import org.wildfly.security.authz.RoleDecoder; +import org.wildfly.security.authz.RoleMapper; +import org.wildfly.security.authz.Roles; +import org.wildfly.security.credential.BearerTokenCredential; +import org.wildfly.security.credential.Credential; +import org.wildfly.security.credential.PasswordCredential; +import org.wildfly.security.evidence.BearerTokenEvidence; +import org.wildfly.security.evidence.Evidence; +import org.wildfly.security.evidence.PasswordGuessEvidence; +import org.wildfly.security.password.interfaces.ClearPassword; +import org.wildfly.security.permission.ElytronPermission; +import org.wildfly.security.permission.PermissionVerifier; + +/** + * A security domain. Security domains encapsulate a set of security policies. + * + * @author David M. Lloyd + * @author Darran Lofthouse + */ +public final class SecurityDomain { + + private static final ConcurrentHashMap CLASS_LOADER_DOMAIN_MAP = new ConcurrentHashMap<>(); + private static final RealmInfo EMPTY_REALM_INFO = new RealmInfo(); + + static final ElytronPermission AUTHENTICATE = ElytronPermission.forName("authenticate"); + static final ElytronPermission CREATE_SECURITY_DOMAIN = ElytronPermission.forName("createSecurityDomain"); + static final ElytronPermission REGISTER_SECURITY_DOMAIN = ElytronPermission.forName("registerSecurityDomain"); + static final ElytronPermission GET_SECURITY_DOMAIN = ElytronPermission.forName("getSecurityDomain"); + static final ElytronPermission UNREGISTER_SECURITY_DOMAIN = ElytronPermission.forName("unregisterSecurityDomain"); + static final ElytronPermission CREATE_AUTH_CONTEXT = ElytronPermission.forName("createServerAuthenticationContext"); + static final ElytronPermission GET_IDENTITY = ElytronPermission.forName("getIdentity"); + static final ElytronPermission GET_IDENTITY_FOR_UPDATE = ElytronPermission.forName("getIdentityForUpdate"); + static final ElytronPermission CREATE_AD_HOC_IDENTITY = ElytronPermission.forName("createAdHocIdentity"); + static final ElytronPermission HANDLE_SECURITY_EVENT = ElytronPermission.forName("handleSecurityEvent"); + + private final Map realmMap; + private final String defaultRealmName; + private final Function preRealmPrincipalRewriter; + private final RealmMapper realmMapper; + private final Function postRealmPrincipalRewriter; + private final ThreadLocal> currentSecurityIdentity; + private final RoleMapper roleMapper; + private final SecurityIdentity anonymousIdentity; + private final PermissionMapper permissionMapper; + private final Map categoryRoleMappers; + private final UnaryOperator securityIdentityTransformer; + private final Predicate trustedSecurityDomain; + private final Consumer securityEventListener; + private final Function evidenceDecoder; + private final RoleDecoder roleDecoder; + + SecurityDomain(Builder builder, final LinkedHashMap realmMap) { + this.realmMap = realmMap; + this.defaultRealmName = builder.defaultRealmName; + this.preRealmPrincipalRewriter = builder.principalDecoder.andThen(builder.preRealmRewriter); + this.realmMapper = builder.realmMapper; + this.roleMapper = builder.roleMapper; + this.permissionMapper = builder.permissionMapper; + this.postRealmPrincipalRewriter = builder.postRealmRewriter; + this.securityIdentityTransformer = builder.securityIdentityTransformer; + this.trustedSecurityDomain = builder.trustedSecurityDomain; + this.securityEventListener = builder.securityEventListener; + this.evidenceDecoder = builder.evidenceDecoder; + this.roleDecoder = builder.roleDecoder; + final Map originalRoleMappers = builder.categoryRoleMappers; + final Map copiedRoleMappers; + if (originalRoleMappers.isEmpty()) { + copiedRoleMappers = emptyMap(); + } else if (originalRoleMappers.size() == 1) { + final Map.Entry entry = originalRoleMappers.entrySet().iterator().next(); + copiedRoleMappers = Collections.singletonMap(entry.getKey(), entry.getValue()); + } else { + copiedRoleMappers = new LinkedHashMap<>(originalRoleMappers); + } + this.categoryRoleMappers = copiedRoleMappers; + // todo configurable + anonymousIdentity = Assert.assertNotNull(securityIdentityTransformer.apply(new SecurityIdentity(this, AnonymousPrincipal.getInstance(), EMPTY_REALM_INFO, AuthorizationIdentity.EMPTY, copiedRoleMappers, IdentityCredentials.NONE, IdentityCredentials.NONE))); + currentSecurityIdentity = ThreadLocal.withInitial(() -> anonymousIdentity); + } + + /** + * Register this {@link SecurityDomain} with the specified {@link ClassLoader}. + * + * Registration with enabled security manager requires {@code registerSecurityDomain} {@link ElytronPermission}. + * + * @throws IllegalStateException If a {@link SecurityDomain} is already associated with the specified {@link ClassLoader}. + * @param classLoader the non {@code null} {@link ClassLoader} to associate this {@link SecurityDomain} with. + */ + public void registerWithClassLoader(ClassLoader classLoader) { + checkNotNullParam("classLoader", classLoader); + final SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission(REGISTER_SECURITY_DOMAIN); + } + + final SecurityDomain classLoaderDomain = CLASS_LOADER_DOMAIN_MAP.putIfAbsent(classLoader, this); + if ((classLoaderDomain != null) && (classLoaderDomain != this)) { + throw log.classLoaderSecurityDomainExists(); + } + } + + /** + * Get the {@link SecurityDomain} associated with the context class loader of the calling Thread or {@code null} if one is + * not associated. + * + * Obtaining security domain with enabled security manager requires {@code getSecurityDomain} {@link ElytronPermission}. + * + * @return the {@link SecurityDomain} associated with the context class loader of the calling Thread or {@code null} if one + * is not associated. + */ + public static SecurityDomain getCurrent() { + final SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission(GET_SECURITY_DOMAIN); + } + + final Thread currentThread = Thread.currentThread(); + ClassLoader classLoader; + if (sm != null) { + classLoader = doPrivileged((PrivilegedAction) currentThread::getContextClassLoader); + } else { + classLoader = currentThread.getContextClassLoader(); + } + + return classLoader != null ? CLASS_LOADER_DOMAIN_MAP.get(classLoader) : null; + } + + /** + * Get the security domain associated with the given identity. + * + * Obtaining security domain with enabled security manager requires {@code getSecurityDomain} {@link ElytronPermission}. + * + * @param identity the security identity (must not be {@code null}) + * @return the identity's security domain (not {@code null}) + */ + public static SecurityDomain forIdentity(SecurityIdentity identity) { + checkNotNullParam("identity", identity); + final SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission(GET_SECURITY_DOMAIN); + } + return identity.getSecurityDomain(); + } + + /** + * Unregister any {@link SecurityDomain} associated with the specified {@link ClassLoader}. + * + * Unregistration with enabled security manager requires {@code unregisterSecurityDomain} {@link ElytronPermission}. + * + * @param classLoader the non {@code null} {@link ClassLoader} to clear any {@link SecurityDomain} association. + */ + public static void unregisterClassLoader(ClassLoader classLoader) { + checkNotNullParam("classLoader", classLoader); + final SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission(UNREGISTER_SECURITY_DOMAIN); + } + + CLASS_LOADER_DOMAIN_MAP.remove(classLoader); + } + + /** + * Create a new security domain builder. + * + * @return the builder + */ + public static Builder builder() { + return new Builder(); + } + + /** + * Create a new authentication context for this security domain which can be used to carry out a single authentication + * operation. + * + * Calling with enabled security manager requires {@code createServerAuthenticationContext} {@link ElytronPermission}. + * + * @return the new authentication context + */ + public ServerAuthenticationContext createNewAuthenticationContext() { + final SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission(CREATE_AUTH_CONTEXT); + } + return new ServerAuthenticationContext(this, MechanismConfigurationSelector.constantSelector(MechanismConfiguration.EMPTY)); + } + + /** + * Create a new authentication context for this security domain which can be used to carry out a single authentication + * operation. + * + * Calling with enabled security manager requires {@code createServerAuthenticationContext} {@link ElytronPermission}. + * + * @param mechanismConfigurationSelector the selector to use to obtain the mechanism configuration + * @return the new authentication context + */ + public ServerAuthenticationContext createNewAuthenticationContext(MechanismConfigurationSelector mechanismConfigurationSelector) { + final SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission(CREATE_AUTH_CONTEXT); + } + return new ServerAuthenticationContext(this, mechanismConfigurationSelector); + } + + ServerAuthenticationContext createNewAuthenticationContext(SecurityIdentity capturedIdentity, MechanismConfigurationSelector mechanismConfigurationSelector) { + assert capturedIdentity.getSecurityDomain() == this; + return new ServerAuthenticationContext(capturedIdentity, mechanismConfigurationSelector); + } + + /** + * Perform an authentication based on {@link Evidence} alone. + * + * Note: It is the caller's responsibility to destroy any evidence passed into this method. + * + * @param evidence the {@link Evidence} to use for authentication. + * @return the authenticated identity. + * @throws RealmUnavailableException if the requires {@link SecurityRealm} is not available. + * @throws SecurityException if authentication fails. + */ + public SecurityIdentity authenticate(Evidence evidence) throws RealmUnavailableException, SecurityException { + return authenticate((Principal) null, evidence); + } + + /** + * Perform an authentication based on {@link Evidence} for the specified identity name. + * + * Note: It is the caller's responsibility to destroy any evidence passed into this method. + * + * @param name the name of the identity to authenticate or {@code null} if the identity is to be derived from the evidence. + * @param evidence the {@link Evidence} to use for authentication. + * @return the authenticated identity. + * @throws RealmUnavailableException if the requires {@link SecurityRealm} is not available. + * @throws SecurityException if authentication fails. + */ + public SecurityIdentity authenticate(String name, Evidence evidence) throws RealmUnavailableException, SecurityException { + return authenticate(name != null ? new NamePrincipal(name) : null, evidence); + } + + /** + * Perform an authentication based on {@link Evidence} for the specified identity {@link Principal}. + * + * Note: It is the caller's responsibility to destroy any evidence passed into this method. + * + * Calling with enabled security manager requires {@code authenticate} {@link ElytronPermission}. + * + * @param principal the principal of the identity to authenticate or {@code null} if the identity is to be derived from the evidence. + * @param evidence the {@link Evidence} to use for authentication. + * @return the authenticated identity. + * @throws RealmUnavailableException if the requires {@link SecurityRealm} is not available. + * @throws SecurityException if authentication fails. + */ + public SecurityIdentity authenticate(Principal principal, Evidence evidence) throws RealmUnavailableException, SecurityException { + final SecurityManager securityManager = System.getSecurityManager(); + if (securityManager != null) { + securityManager.checkPermission(AUTHENTICATE); + } + + try (final ServerAuthenticationContext serverAuthenticationContext = new ServerAuthenticationContext(this, + MechanismConfigurationSelector.constantSelector(MechanismConfiguration.EMPTY))) { + if (principal != null) + serverAuthenticationContext.setAuthenticationPrincipal(principal); + if (serverAuthenticationContext.verifyEvidence(evidence)) { + if (serverAuthenticationContext.authorize()) { + if (evidence instanceof PasswordGuessEvidence) { + PasswordGuessEvidence passwordGuessEvidence = PasswordGuessEvidence.class.cast(evidence); + serverAuthenticationContext.addPrivateCredential(new PasswordCredential( + ClearPassword.createRaw(ClearPassword.ALGORITHM_CLEAR, passwordGuessEvidence.getGuess()))); + } else if (evidence instanceof BearerTokenEvidence) { + BearerTokenEvidence tokenEvidence = BearerTokenEvidence.class.cast(evidence); + serverAuthenticationContext.addPrivateCredential(new BearerTokenCredential(tokenEvidence.getToken())); + } else { + log.tracef( + "Evidence [%s] does not map to a supported credential type. Credentials are not available from authorized identity and identity propagation may not work", + evidence.getClass().getName()); + } + serverAuthenticationContext.succeed(); + return serverAuthenticationContext.getAuthorizedIdentity(); + } else { + serverAuthenticationContext.fail(); + throw log.authenticationFailedAuthorization(); + } + } else { + serverAuthenticationContext.fail(); + throw log.authenticationFailedEvidenceVerification(); + } + } + } + + /** + * Look up a {@link RealmIdentity} by name by wrapping the name in a {@link NamePrincipal} and calling {@link #getIdentity(Principal)}. + * The returned identity must be {@linkplain RealmIdentity#dispose() disposed}. + * + * @param name the name to map (must not be {@code null}) + * @return the identity for the name (not {@code null}, may be non-existent) + * @throws RealmUnavailableException if the realm is not able to perform the mapping + * @throws IllegalArgumentException if the name is not valid + * @throws SecurityException if the caller is not authorized to perform the operation + */ + public RealmIdentity getIdentity(String name) throws RealmUnavailableException { + Assert.checkNotNullParam("name", name); + return getIdentity(new NamePrincipal(name)); + } + + /** + * Look up a {@link RealmIdentity} by principal. + * The returned identity must be {@linkplain RealmIdentity#dispose() disposed}. + * + * Calling with enabled security manager requires {@code getIdentity} {@link ElytronPermission}. + * + * @param principal the principal to map (must not be {@code null}) + * @return the identity for the name (not {@code null}, may be non-existent) + * @throws IllegalArgumentException if the principal could not be successfully decoded to a name + * @throws RealmUnavailableException if the realm is not able to perform the mapping + * @throws SecurityException if the caller is not authorized to perform the operation + */ + public RealmIdentity getIdentity(Principal principal) throws RealmUnavailableException, IllegalArgumentException { + final SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission(GET_IDENTITY); + } + return getIdentityPrivileged(principal, SecurityRealm.class, SecurityRealm::getRealmIdentity, () -> RealmIdentity.NON_EXISTENT, () -> RealmIdentity.ANONYMOUS); + } + + /** + * Look up a {@link ModifiableRealmIdentity} by principal. + * The returned identity must be {@linkplain RealmIdentity#dispose() disposed}. + * + * Calling with enabled security manager requires {@code getIdentityForUpdate} {@link ElytronPermission}. + * + * @param principal the principal to map (must not be {@code null}) + * @return the identity for the name (not {@code null}, may be non-existent) + * @throws IllegalArgumentException if the principal could not be successfully decoded to a name + * @throws RealmUnavailableException if the realm is not able to perform the mapping + * @throws SecurityException if the caller is not authorized to perform the operation + */ + public ModifiableRealmIdentity getIdentityForUpdate(Principal principal) throws RealmUnavailableException, IllegalArgumentException { + final SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission(GET_IDENTITY_FOR_UPDATE); + } + return getIdentityPrivileged(principal, ModifiableSecurityRealm.class, ModifiableSecurityRealm::getRealmIdentityForUpdate, () -> ModifiableRealmIdentity.NON_EXISTENT, () -> ModifiableRealmIdentity.NON_EXISTENT); + } + + /** + * Get a function which can be used to look up principals without a security manager permission check. + * All returned identities must be {@linkplain RealmIdentity#dispose() disposed}. + * + * Calling with enabled security manager requires {@code getIdentity} {@link ElytronPermission}. + * + * @return the lookup function (not {@code null}) + * @throws SecurityException if the caller is not authorized to perform the operation + */ + public ExceptionFunction getIdentityLookupFunction() { + final SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission(GET_IDENTITY); + } + return p -> getIdentityPrivileged(p, SecurityRealm.class, SecurityRealm::getRealmIdentity, () -> RealmIdentity.NON_EXISTENT, () -> RealmIdentity.ANONYMOUS); + } + + /** + * Get a function which can be used to look up principals for update without a security manager permission check. + * All returned identities must be {@linkplain RealmIdentity#dispose() disposed}. + * Calling with enabled security manager requires {@code getIdentityForUpdate} {@link ElytronPermission}. + * + * @return the lookup function (not {@code null}) + * @throws SecurityException if the caller is not authorized to perform the operation + */ + public ExceptionFunction getIdentityLookupForUpdateFunction() { + final SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission(GET_IDENTITY_FOR_UPDATE); + } + return p -> getIdentityPrivileged(p, ModifiableSecurityRealm.class, ModifiableSecurityRealm::getRealmIdentityForUpdate, () -> ModifiableRealmIdentity.NON_EXISTENT, () -> ModifiableRealmIdentity.NON_EXISTENT); + } + + I getIdentityPrivileged(Principal principal, Class realmType, ExceptionBiFunction fn, Supplier nonExistent, Supplier anonymous) throws RealmUnavailableException { + Assert.checkNotNullParam("principal", principal); + if (principal instanceof AnonymousPrincipal) { + return anonymous.get(); + } + if (principal instanceof RealmNestedPrincipal) { + final RealmNestedPrincipal realmNestedPrincipal = (RealmNestedPrincipal) principal; + final SecurityRealm securityRealm = getRealmInfo(realmNestedPrincipal.getRealmName()).getSecurityRealm(); + if (realmType.isInstance(securityRealm)) { + return fn.apply(realmType.cast(securityRealm), realmNestedPrincipal.getNestedPrincipal()); + } else { + return nonExistent.get(); + } + } + Principal preRealmPrincipal = preRealmPrincipalRewriter.apply(principal); + if (preRealmPrincipal == null) { + throw log.invalidName(); + } + + String realmName = mapRealmName(preRealmPrincipal, null); + RealmInfo realmInfo = getRealmInfo(realmName); + SecurityRealm securityRealm = realmInfo.getSecurityRealm(); + assert securityRealm != null; + + Principal postRealmPrincipal = postRealmPrincipalRewriter.apply(preRealmPrincipal); + if (postRealmPrincipal == null) { + throw log.invalidName(); + } + + Principal realmRewrittenPrincipal = realmInfo.getPrincipalRewriter().apply(postRealmPrincipal); + if (realmRewrittenPrincipal == null) { + throw log.invalidName(); + } + + log.tracef("Principal mapping: [%s], pre-realm rewritten: [%s], realm name: [%s], post realm rewritten: [%s], realm rewritten: [%s]", + principal, preRealmPrincipal, realmName, postRealmPrincipal, realmRewrittenPrincipal); + + if (realmType.isInstance(securityRealm)) { + return fn.apply(realmType.cast(securityRealm), realmRewrittenPrincipal); + } else { + return nonExistent.get(); + } + } + + SecurityRealm getRealm(final String realmName) { + return getRealmInfo(realmName).getSecurityRealm(); + } + + RealmInfo getRealmInfo(final String realmName) { + RealmInfo realmInfo = this.realmMap.get(realmName); + + if (realmInfo == null) { + realmInfo = this.realmMap.get(this.defaultRealmName); + } + if (realmInfo == null) { + log.tracef("Unable to obtain RealmInfo [%s] and no default set - using empty", realmName); + realmInfo = EMPTY_REALM_INFO; + } + return realmInfo; + } + + Collection getRealmInfos() { + return realmMap.values(); + } + + /** + * Determine whether a credential of the given type and algorithm is definitely obtainable, possibly obtainable (for + * some identities), or definitely not obtainable. + * + * Credential is {@link SupportLevel#SUPPORTED}, if it is supported by all realms of the domain. + * Credential is {@link SupportLevel#POSSIBLY_SUPPORTED} if it is supported or possibly supported by at least one realm of the domain. + * Otherwise it is {@link SupportLevel#UNSUPPORTED}. + * + * @param credentialType the exact credential type (must not be {@code null}) + * @param algorithmName the algorithm name, or {@code null} if any algorithm is acceptable or the credential type does + * not support algorithm names + * @param parameterSpec the algorithm parameters to match, or {@code null} if any parameters are acceptable or the credential type + * does not support algorithm parameters + * @return the level of support for this credential + */ + public SupportLevel getCredentialAcquireSupport(Class credentialType, String algorithmName, AlgorithmParameterSpec parameterSpec) { + return getSupportLevel(r -> { + try { + return r.getCredentialAcquireSupport(credentialType, algorithmName, parameterSpec); + } catch (RealmUnavailableException e) { + log.trace("Failed to obtain credential acquire support from realm", e); + return null; + } + }); + } + + /** + * Determine whether a credential of the given type and algorithm is definitely obtainable, possibly obtainable (for + * some identities), or definitely not obtainable. + * + * Credential is {@link SupportLevel#SUPPORTED}, if it is supported by all realms of the domain. + * Credential is {@link SupportLevel#POSSIBLY_SUPPORTED} if it is supported or possibly supported by at least one realm of the domain. + * Otherwise it is {@link SupportLevel#UNSUPPORTED}. + * + * @param credentialType the exact credential type (must not be {@code null}) + * @param algorithmName the algorithm name, or {@code null} if any algorithm is acceptable or the credential type does + * not support algorithm names + * @return the level of support for this credential + */ + public SupportLevel getCredentialAcquireSupport(Class credentialType, String algorithmName) { + return getCredentialAcquireSupport(credentialType, algorithmName, null); + } + + /** + * Determine whether a credential of the given type and algorithm is definitely obtainable, possibly obtainable (for + * some identities), or definitely not obtainable. + * + * Credential is {@link SupportLevel#SUPPORTED}, if it is supported by all realms of the domain. + * Credential is {@link SupportLevel#POSSIBLY_SUPPORTED} if it is supported or possibly supported by at least one realm of the domain. + * Otherwise it is {@link SupportLevel#UNSUPPORTED}. + * + * @param credentialType the exact credential type (must not be {@code null}) + * @return the level of support for this credential + */ + public SupportLevel getCredentialAcquireSupport(Class credentialType) { + return getCredentialAcquireSupport(credentialType, null); + } + + /** + * Determine whether a given type of evidence is definitely verifiable, possibly verifiable (for some identities), + * or definitely not verifiable. + * + * Evidence is {@link SupportLevel#SUPPORTED}, if it is supported by all realms of the domain. + * Evidence is {@link SupportLevel#POSSIBLY_SUPPORTED} if it is supported or possibly supported by at least one realm of the domain. + * Otherwise it is {@link SupportLevel#UNSUPPORTED}. + * + * @param evidenceType the type of evidence to be verified (must not be {@code null}) + * @param algorithmName the algorithm name, or {@code null} if any algorithm is acceptable or the evidence type does + * not support algorithm names + * @return the level of support for this evidence type + */ + public SupportLevel getEvidenceVerifySupport(Class evidenceType, String algorithmName) { + return getSupportLevel(r -> { + try { + return r.getEvidenceVerifySupport(evidenceType, algorithmName); + } catch (RealmUnavailableException e) { + log.trace("Failed to obtain evidence verify support from realm", e); + return null; + } + }); + } + + /** + * Determine whether a given type of evidence is definitely verifiable, possibly verifiable (for some identities), + * or definitely not verifiable. + * + * Evidence is {@link SupportLevel#SUPPORTED}, if it is supported by all realms of the domain. + * Evidence is {@link SupportLevel#POSSIBLY_SUPPORTED} if it is supported or possibly supported by at least one realm of the domain. + * Otherwise it is {@link SupportLevel#UNSUPPORTED}. + * + * @param evidenceType the type of evidence to be verified (must not be {@code null}) + * @return the level of support for this evidence type + */ + public SupportLevel getEvidenceVerifySupport(Class evidenceType) { + return getEvidenceVerifySupport(evidenceType, null); + } + + private SupportLevel getSupportLevel(final Function getSupportLevel) { + SupportLevel min, max; + min = max = null; + Iterator iterator = realmMap.values().iterator(); + + while (iterator.hasNext()) { + RealmInfo realmInfo = iterator.next(); + SecurityRealm realm = realmInfo.getSecurityRealm(); + final SupportLevel support = getSupportLevel.apply(realm); + + if (support != null) { + if (min == null || max == null) { + min = max = support; + } else { + if (support.compareTo(min) < 0) { min = support; } + if (support.compareTo(max) > 0) { max = support; } + } + } + } + + if (min == null || max == null) { + return SupportLevel.UNSUPPORTED; + } else { + return minMax(min, max); + } + } + + private SupportLevel minMax(SupportLevel min, SupportLevel max) { + if (min == max) return min; + if (max == SupportLevel.UNSUPPORTED) { + return SupportLevel.UNSUPPORTED; + } else if (min == SupportLevel.SUPPORTED) { + return SupportLevel.SUPPORTED; + } else { + return SupportLevel.POSSIBLY_SUPPORTED; + } + } + + /** + * Get the current security identity for this domain. + * + * Code can be executed with given identity using {@code SecurityIdentity.runAs*} methods. + * + * @return the current security identity for this domain (not {@code null}) + */ + public SecurityIdentity getCurrentSecurityIdentity() { + final SecurityIdentity identity = currentSecurityIdentity.get().get(); + return identity == null ? anonymousIdentity : identity; + } + + /** + * Get the anonymous security identity for this realm. + * + * @return the anonymous security identity for this realm (not {@code null}) + */ + public SecurityIdentity getAnonymousSecurityIdentity() { + return anonymousIdentity; + } + + /** + * Create an empty ad-hoc identity. The identity will have no authorization information and no credentials associated + * with it. + * + * @param name the identity name (must not be {@code null}) + * @return the ad-hoc identity + */ + public SecurityIdentity createAdHocIdentity(String name) { + checkNotNullParam("name", name); + return createAdHocIdentity(new NamePrincipal(name)); + } + + /** + * Create an empty ad-hoc identity. The identity will have no authorization information and no credentials associated + * with it. + * + * Calling with enabled security manager requires {@code createAdHocIdentity} {@link ElytronPermission}. + * + * @param principal the identity principal (must not be {@code null}) + * @return the ad-hoc identity + */ + public SecurityIdentity createAdHocIdentity(Principal principal) { + checkNotNullParam("principal", principal); + final SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission(CREATE_AD_HOC_IDENTITY); + } + return new SecurityIdentity(this, principal, EMPTY_REALM_INFO, AuthorizationIdentity.EMPTY, emptyMap(), IdentityCredentials.NONE, IdentityCredentials.NONE); + } + + Supplier getAndSetCurrentSecurityIdentity(Supplier newIdentity) { + try { + final Supplier oldIdentity = currentSecurityIdentity.get(); + return oldIdentity == null ? anonymousIdentity : oldIdentity; + } finally { + if (newIdentity == anonymousIdentity) { + currentSecurityIdentity.remove(); + } else { + currentSecurityIdentity.set(newIdentity); + } + } + } + + void setCurrentSecurityIdentity(Supplier newIdentity) { + if (newIdentity == anonymousIdentity) { + currentSecurityIdentity.remove(); + } else { + currentSecurityIdentity.set(newIdentity); + } + } + + Roles mapRoles(SecurityIdentity securityIdentity) { + Assert.checkNotNullParam("securityIdentity", securityIdentity); + + AuthorizationIdentity identity = securityIdentity.getAuthorizationIdentity(); + RealmInfo realmInfo = securityIdentity.getRealmInfo(); + + // zeroth role mapping, just grab roles from the identity + Roles decodedRoles = realmInfo.getRoleDecoder().decodeRoles(identity); + + // determine roles based on any runtime attributes associated with the identity + Roles domainDecodedRoles = securityIdentity.getSecurityDomain().getRoleDecoder().decodeRoles(identity); + Roles combinedRoles = decodedRoles.or(domainDecodedRoles); + + // apply the first level mapping, which is based on the role mapper associated with a realm. + Roles realmMappedRoles = realmInfo.getRoleMapper().mapRoles(combinedRoles); + + // apply the second level mapping, which is based on the role mapper associated with this security domain. + Roles domainMappedRoles = roleMapper.mapRoles(realmMappedRoles); + + if (log.isTraceEnabled()) { + log.tracef("Role mapping: principal [%s] -> decoded roles [%s] -> domain decoded roles [%s] -> realm mapped roles [%s] -> domain mapped roles [%s]", + securityIdentity.getPrincipal(), String.join(", ", decodedRoles), String.join(", ", domainDecodedRoles), String.join(", ", realmMappedRoles), String.join(", ", domainMappedRoles)); + } + + return domainMappedRoles; + } + + PermissionVerifier mapPermissions(final SecurityIdentity securityIdentity) { + Assert.checkNotNullParam("securityIdentity", securityIdentity); + final Roles roles = securityIdentity.getRoles(); + PermissionVerifier verifier = permissionMapper.mapPermissions(securityIdentity, roles); + + if (log.isTraceEnabled()) { + return (permission) -> { + boolean decision = verifier.implies(permission); + log.tracef("Permission mapping: identity [%s] with roles [%s] implies %s = %b", + securityIdentity.getPrincipal(), String.join(", ", roles), permission, decision); + return decision; + }; + } else { + return verifier; + } + } + + Function getPreRealmRewriter() { + return preRealmPrincipalRewriter; + } + + String mapRealmName(final Principal principal, final Evidence evidence) { + String realm = realmMapper.getRealmMapping(principal, evidence); + return realm != null ? realm : defaultRealmName; + } + + String getDefaultRealmName() { + return defaultRealmName; + } + + RealmMapper getRealmMapper() { + return realmMapper; + } + + Function getPostRealmRewriter() { + return postRealmPrincipalRewriter; + } + + RoleMapper getRoleMapper() { + return roleMapper; + } + + Map getCategoryRoleMappers() { + return categoryRoleMappers; + } + + SecurityIdentity transform(final SecurityIdentity securityIdentity) { + Assert.checkNotNullParam("securityIdentity", securityIdentity); + return Assert.assertNotNull(securityIdentityTransformer.apply(securityIdentity)); + } + + boolean trustsDomain(final SecurityDomain domain) { + Assert.checkNotNullParam("domain", domain); + return this == domain || trustedSecurityDomain.test(domain); + } + + /** + * Handle a {@link SecurityEvent}. + * + * Calling with enabled security manager requires {@code handleSecurityEvent} {@link ElytronPermission}. + * + * @param securityEvent {@link SecurityEvent} to be handled + * @see Builder#setSecurityEventListener(Consumer) + */ + public void handleSecurityEvent(final SecurityEvent securityEvent) { + final SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission(HANDLE_SECURITY_EVENT); + } + if (!securityEvent.getSecurityIdentity().getSecurityDomain().equals(this)) { + log.securityEventIdentityWrongDomain(); + } + this.securityEventListener.accept(securityEvent); + } + + static void safeHandleSecurityEvent(final SecurityDomain domain, final SecurityEvent event) { + checkNotNullParam("domain", domain); + checkNotNullParam("event", event); + try { + domain.handleSecurityEvent(event); + } catch (Exception e) { + log.eventHandlerFailed(e); + } + } + + Function getEvidenceDecoder() { + return evidenceDecoder; + } + + RoleDecoder getRoleDecoder() { + return roleDecoder; + } + + /** + * A builder for creating new security domains. + */ + public static final class Builder { + private boolean built = false; + + private final HashMap realms = new HashMap<>(); + private Function preRealmRewriter = Function.identity(); + private Function principalDecoder = Function.identity(); + private Function postRealmRewriter = Function.identity(); + private String defaultRealmName; + private RealmMapper realmMapper = RealmMapper.DEFAULT_REALM_MAPPER; + private RoleMapper roleMapper = RoleMapper.IDENTITY_ROLE_MAPPER; + private PermissionMapper permissionMapper = PermissionMapper.EMPTY_PERMISSION_MAPPER; + private Map categoryRoleMappers = emptyMap(); + private UnaryOperator securityIdentityTransformer = UnaryOperator.identity(); + private Predicate trustedSecurityDomain = domain -> false; + private Consumer securityEventListener = e -> {}; + private Function evidenceDecoder = evidence -> evidence.getDefaultPrincipal(); + private RoleDecoder roleDecoder = RoleDecoder.EMPTY; + + Builder() { + } + + /** + * Sets a pre-realm name rewriter, which rewrites the authentication name before a realm is selected. + * + * @param rewriter the name rewriter (must not be {@code null}) + * @return this builder + */ + public Builder setPreRealmRewriter(NameRewriter rewriter) { + return setPreRealmRewriter(rewriter.asPrincipalRewriter()); + } + + /** + * Sets a pre-realm name rewriter, which rewrites the authentication name before a realm is selected. + * + * @param rewriter the name rewriter (must not be {@code null}) + * @return this builder + */ + public Builder setPreRealmRewriter(final Function rewriter) { + Assert.checkNotNullParam("rewriter", rewriter); + assertNotBuilt(); + this.preRealmRewriter = rewriter; + return this; + } + + /** + * Sets a post-realm name rewriter, which rewrites the authentication name after a realm is selected. + * + * @param rewriter the name rewriter (must not be {@code null}) + * @return this builder + */ + public Builder setPostRealmRewriter(NameRewriter rewriter) { + return setPostRealmRewriter(rewriter.asPrincipalRewriter()); + } + + /** + * Sets a post-realm name rewriter, which rewrites the authentication name after a realm is selected. + * + * @param rewriter the name rewriter (must not be {@code null}) + * @return this builder + */ + public Builder setPostRealmRewriter(Function rewriter) { + Assert.checkNotNullParam("rewriter", rewriter); + assertNotBuilt(); + this.postRealmRewriter = rewriter; + + return this; + } + + /** + * Set the realm mapper for this security domain, which selects a realm based on the authentication name. + * + * @param realmMapper the realm mapper (must not be {@code null}) + * @return this builder + */ + public Builder setRealmMapper(RealmMapper realmMapper) { + Assert.checkNotNullParam("realmMapper", realmMapper); + assertNotBuilt(); + this.realmMapper = realmMapper; + + return this; + } + + /** + * Set the role mapper for this security domain, which will be used to perform the last mapping before + * returning the roles associated with an identity obtained from this security domain. + * + * @param roleMapper the role mapper (must not be {@code null}) + * @return this builder + */ + public Builder setRoleMapper(RoleMapper roleMapper) { + Assert.checkNotNullParam("roleMapper", roleMapper); + assertNotBuilt(); + this.roleMapper = roleMapper; + return this; + } + + /** + * Set the permission mapper for this security domain, which will be used to obtain and map permissions based on the + * identities from this security domain. + * + * @param permissionMapper the permission mapper (must not be {@code null}) + * @return this builder + */ + public Builder setPermissionMapper(PermissionMapper permissionMapper) { + Assert.checkNotNullParam("permissionMapper", permissionMapper); + assertNotBuilt(); + this.permissionMapper = permissionMapper; + return this; + } + + /** + * Set the principal decoder for this security domain, which will be used to convert {@link Principal} objects + * into names for handling in the realm. + * + * @param principalDecoder the principal decoder (must not be {@code null}) + * @return this builder + */ + public Builder setPrincipalDecoder(PrincipalDecoder principalDecoder) { + Assert.checkNotNullParam("principalDecoder", principalDecoder); + assertNotBuilt(); + this.principalDecoder = principalDecoder.asPrincipalRewriter(); + return this; + } + + /** + * Add a realm to this security domain. + * + * @param name the realm's name in this configuration + * @param realm the realm + * @return the new realm builder + */ + public RealmBuilder addRealm(String name, SecurityRealm realm) { + Assert.checkNotNullParam("name", name); + Assert.checkNotNullParam("realm", realm); + assertNotBuilt(); + return new RealmBuilder(this, name, realm); + } + + Builder addRealm(RealmBuilder realmBuilder) { + realms.put(realmBuilder.getName(), realmBuilder); + + return this; + } + + /** + * Get the default realm name. + * + * @return the default realm name + */ + public String getDefaultRealmName() { + return defaultRealmName; + } + + /** + * Set the default realm name. + * + * @param defaultRealmName the default realm name (must not be {@code null}) + */ + public Builder setDefaultRealmName(final String defaultRealmName) { + Assert.checkNotNullParam("defaultRealmName", defaultRealmName); + assertNotBuilt(); + this.defaultRealmName = defaultRealmName; + + return this; + } + + /** + * Get the category role mapper map. + * + * @return the category role mapper map + */ + public Map getCategoryRoleMappers() { + return categoryRoleMappers; + } + + /** + * Set the category role mapper map. + * + * @param categoryRoleMappers the category role mapper map (must not be {@code null}) + */ + public void setCategoryRoleMappers(final Map categoryRoleMappers) { + Assert.checkNotNullParam("categoryRoleMappers", categoryRoleMappers); + this.categoryRoleMappers = categoryRoleMappers; + } + + /** + * Set the security identity transformer to use. The transformer must not return {@code null}, or authentication + * will fail. + * + * @param securityIdentityTransformer the security identity transformer to use (must not be {@code null}) + * @return this builder + */ + public Builder setSecurityIdentityTransformer(UnaryOperator securityIdentityTransformer) { + Assert.checkNotNullParam("securityIdentityTransformer", securityIdentityTransformer); + this.securityIdentityTransformer = securityIdentityTransformer; + return this; + } + + /** + * Set the predicate that should be used to determine if a given domain is trusted by this domain. + * + * @param trustedSecurityDomain the predicate that should be used to determine if a given domain is + * trusted by this domain (must not be {@code null}) + */ + public Builder setTrustedSecurityDomainPredicate(final Predicate trustedSecurityDomain) { + Assert.checkNotNullParam("trustedSecurityDomain", trustedSecurityDomain); + this.trustedSecurityDomain = trustedSecurityDomain; + return this; + } + + /** + * Set the security event listener that will consume all {@link SecurityEvent} instances emitted but the domain. + * + * @param securityEventListener the security event listener that will consume all {@link SecurityEvent} instances emitted but the domain. + * @return this builder + */ + public Builder setSecurityEventListener(final Consumer securityEventListener) { + this.securityEventListener = Assert.checkNotNullParam("securityEventListener", securityEventListener); + return this; + } + + /** + * Set the evidence decoder for this security domain which will be used to extract the principal from the given + * {@link Evidence}. + * + * @param evidenceDecoder the evidence decoder (must not be {@code null}) + * @return this builder + * @since 1.10.0 + */ + public Builder setEvidenceDecoder(EvidenceDecoder evidenceDecoder) { + Assert.checkNotNullParam("evidenceDecoder", evidenceDecoder); + assertNotBuilt(); + this.evidenceDecoder = evidenceDecoder; + return this; + } + + /** + * Set the role decoder for this security domain. + * + * @param roleDecoder the role decoder (must not be {@code null}) + * @return this builder + * @since 1.11.0 + */ + public Builder setRoleDecoder(RoleDecoder roleDecoder) { + Assert.checkNotNullParam("roleDecoder", roleDecoder); + assertNotBuilt(); + this.roleDecoder = roleDecoder; + return this; + } + + /** + * Construct this security domain. + * + * Construction requires {@code createSecurityDomain} {@link ElytronPermission}. + * + * @return the new security domain + */ + public SecurityDomain build() { + final SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission(CREATE_SECURITY_DOMAIN); + } + + final LinkedHashMap realmMap = new LinkedHashMap<>(realms.size()); + + for (RealmBuilder realmBuilder : realms.values()) { + realmMap.put(realmBuilder.getName(), new RealmInfo(realmBuilder)); + } + if (defaultRealmName != null && !realmMap.containsKey(defaultRealmName)) { + throw log.realmMapDoesNotContainDefault(defaultRealmName); + } + + assertNotBuilt(); + built = true; + + if(log.isTraceEnabled()) { + log.tracef("Building security domain with defaultRealmName %s.", defaultRealmName); + if(realmMap.size() > 1) { + log.tracef("The following additional realms were added: %s.", realmMap.keySet().toString()); + } + } + + return new SecurityDomain(this, realmMap); + } + + void assertNotBuilt() { + if (built) { + throw log.builderAlreadyBuilt(); + } + } + } + + /** + * A builder for a realm within a security domain. + */ + public static class RealmBuilder { + + private final Builder parent; + private final String name; + private final SecurityRealm realm; + private RoleMapper roleMapper = RoleMapper.IDENTITY_ROLE_MAPPER; + private Function principalRewriter = Function.identity(); + private RoleDecoder roleDecoder = RoleDecoder.DEFAULT; + private boolean built = false; + + RealmBuilder(final Builder parent, final String name, final SecurityRealm realm) { + this.parent = parent; + this.name = name; + this.realm = realm; + } + + /** + * Get the realm name. + * + * @return the realm name (not {@code null}) + */ + public String getName() { + return name; + } + + /** + * Get the security realm. + * + * @return the security realm (not {@code null}) + */ + public SecurityRealm getRealm() { + return realm; + } + + /** + * Get the role mapper. + * + * @return the role mapper (not {@code null}) + */ + public RoleMapper getRoleMapper() { + return roleMapper; + } + + /** + * Set the role mapper. + * + * @param roleMapper the role mapper (may not be {@code null}) + */ + public RealmBuilder setRoleMapper(final RoleMapper roleMapper) { + assertNotBuilt(); + Assert.checkNotNullParam("roleMapper", roleMapper); + this.roleMapper = roleMapper; + + return this; + } + + /** + * Get the name rewriter. + * + * @return the name rewriter (not {@code null}) + */ + public Function getPrincipalRewriter() { + return principalRewriter; + } + + /** + * Set the name rewriter. + * + * @param principalRewriter the name rewriter (may not be {@code null}) + */ + public RealmBuilder setPrincipalRewriter(final Function principalRewriter) { + Assert.checkNotNullParam("principalRewriter", principalRewriter); + assertNotBuilt(); + this.principalRewriter = principalRewriter; + + return this; + } + + @Deprecated + public RealmBuilder setNameRewriter(final NameRewriter nameRewriter) { + return setPrincipalRewriter(nameRewriter.asPrincipalRewriter()); + } + + /** + * Get the role decoder. + * + * @return the role decoder (not {@code null}) + */ + public RoleDecoder getRoleDecoder() { + return roleDecoder; + } + + /** + * Set the role decoder. + * + * @param roleDecoder the role decoder (may not be {@code null}) + */ + public RealmBuilder setRoleDecoder(final RoleDecoder roleDecoder) { + Assert.checkNotNullParam("roleDecoder", roleDecoder); + assertNotBuilt(); + this.roleDecoder = roleDecoder; + + return this; + } + + /** + * Constructs this realm info and adds it into the domain. + * + * @return the security domain builder + */ + public Builder build() { + assertNotBuilt(); + return parent.addRealm(this); + } + + private void assertNotBuilt() { + parent.assertNotBuilt(); + if (built) { + throw log.builderAlreadyBuilt(); + } + } + } + + private static class ScheduledExecutorServiceProvider { + + private static final ScheduledThreadPoolExecutor INSTANCE = new ScheduledThreadPoolExecutor(1, runnable -> { + // use daemon thread + Thread thread = Executors.defaultThreadFactory().newThread(runnable); + thread.setDaemon(true); + return thread; + }); + + static { + INSTANCE.setRemoveOnCancelPolicy(true); + INSTANCE.setExecuteExistingDelayedTasksAfterShutdownPolicy(false); + } + } + + /** + * Gets {@link ScheduledExecutorService} for authentication related scheduled task (like authentication timeout). + * + * @return the executor service + */ + public static ScheduledExecutorService getScheduledExecutorService() { + return ScheduledExecutorServiceProvider.INSTANCE; + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/server/SecurityIdentity.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/server/SecurityIdentity.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/server/SecurityIdentity.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,965 @@ +/* + * 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.auth.server; + +import static org.wildfly.security.auth.server._private.ElytronMessages.log; + +import java.security.Permission; +import java.security.Principal; +import java.security.PrivilegedAction; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; +import java.time.Instant; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.BiConsumer; +import java.util.function.BiFunction; +import java.util.function.BiPredicate; +import java.util.function.Function; +import java.util.function.ObjIntConsumer; +import java.util.function.Supplier; + +import org.wildfly.common.Assert; +import org.wildfly.common.function.ExceptionBiConsumer; +import org.wildfly.common.function.ExceptionBiFunction; +import org.wildfly.common.function.ExceptionBiPredicate; +import org.wildfly.common.function.ExceptionFunction; +import org.wildfly.common.function.ExceptionObjIntConsumer; +import org.wildfly.common.function.ExceptionSupplier; +import org.wildfly.security.ParametricPrivilegedAction; +import org.wildfly.security.ParametricPrivilegedExceptionAction; +import org.wildfly.security.auth.permission.ChangeRoleMapperPermission; +import org.wildfly.security.auth.permission.RunAsPrincipalPermission; +import org.wildfly.security.auth.principal.AnonymousPrincipal; +import org.wildfly.security.auth.principal.NamePrincipal; +import org.wildfly.security.auth.server.event.SecurityPermissionCheckFailedEvent; +import org.wildfly.security.auth.server.event.SecurityPermissionCheckSuccessfulEvent; +import org.wildfly.security.authz.Attributes; +import org.wildfly.security.authz.AuthorizationIdentity; +import org.wildfly.security.authz.PermissionMappable; +import org.wildfly.security.authz.RoleMapper; +import org.wildfly.security.authz.Roles; +import org.wildfly.security.credential.Credential; +import org.wildfly.security.permission.ElytronPermission; +import org.wildfly.security.permission.PermissionVerifier; + +/** + * A loaded and authenticated security identity. + * + * @author David M. Lloyd + */ +public final class SecurityIdentity implements PermissionVerifier, PermissionMappable, Supplier, Scoped { + private static final Permission SET_RUN_AS_PERMISSION = ElytronPermission.forName("setRunAsPrincipal"); + private static final Permission PRIVATE_CREDENTIALS_PERMISSION = ElytronPermission.forName("getPrivateCredentials"); + private static final Permission WITH_DEFAULT_ROLE_MAPPER_PERMISSION = ElytronPermission.forName("withDefaultRoleMapper"); + + private static final SecurityIdentity[] NO_IDENTITIES = new SecurityIdentity[0]; + + private final SecurityDomain securityDomain; + private final Principal principal; + private final AuthorizationIdentity authorizationIdentity; + private final RealmInfo realmInfo; + private final Function defaultRoles; + private final Map roleMappers; + private final Instant creationTime; + private final PermissionVerifier verifier; + private final IdentityCredentials publicCredentials; + private final IdentityCredentials privateCredentials; + private final Supplier withSuppliedIdentities; + private final SecurityIdentity[] withIdentities; + + SecurityIdentity(final SecurityDomain securityDomain, final Principal principal, final RealmInfo realmInfo, final AuthorizationIdentity authorizationIdentity, final Map roleMappers, final IdentityCredentials publicCredentials, final IdentityCredentials privateCredentials) { + this.securityDomain = securityDomain; + this.principal = principal; + this.realmInfo = realmInfo; + this.authorizationIdentity = authorizationIdentity; + this.defaultRoles = securityDomain::mapRoles; + this.roleMappers = roleMappers; + this.creationTime = Instant.now(); + this.verifier = securityDomain.mapPermissions(this); + this.publicCredentials = publicCredentials; + this.privateCredentials = privateCredentials; + this.withSuppliedIdentities = null; + this.withIdentities = null; + } + + SecurityIdentity(final SecurityIdentity old, final Map roleMappers) { + this.securityDomain = old.securityDomain; + this.principal = old.principal; + this.realmInfo = old.realmInfo; + this.authorizationIdentity = old.authorizationIdentity; + this.defaultRoles = old.defaultRoles; + this.roleMappers = roleMappers; + this.creationTime = old.creationTime; + this.verifier = old.verifier; + this.publicCredentials = old.publicCredentials; + this.privateCredentials = old.privateCredentials; + this.withSuppliedIdentities = old.withSuppliedIdentities; + this.withIdentities = old.withIdentities; + } + + SecurityIdentity(final SecurityIdentity old, final PermissionVerifier verifier) { + this.securityDomain = old.securityDomain; + this.principal = old.principal; + this.realmInfo = old.realmInfo; + this.authorizationIdentity = old.authorizationIdentity; + this.defaultRoles = old.defaultRoles; + this.roleMappers = old.roleMappers; + this.creationTime = old.creationTime; + this.verifier = verifier; + this.publicCredentials = old.publicCredentials; + this.privateCredentials = old.privateCredentials; + this.withSuppliedIdentities = old.withSuppliedIdentities; + this.withIdentities = old.withIdentities; + } + + SecurityIdentity(final SecurityIdentity old, final Credential credential, final boolean isPrivate) { + this.securityDomain = old.securityDomain; + this.principal = old.principal; + this.realmInfo = old.realmInfo; + this.authorizationIdentity = old.authorizationIdentity; + this.defaultRoles = old.defaultRoles; + this.roleMappers = old.roleMappers; + this.creationTime = old.creationTime; + this.verifier = old.verifier; + this.publicCredentials = isPrivate ? old.publicCredentials : old.publicCredentials.withCredential(credential); + this.privateCredentials = isPrivate ? old.privateCredentials.withCredential(credential) : old.privateCredentials; + this.withSuppliedIdentities = old.withSuppliedIdentities; + this.withIdentities = old.withIdentities; + } + + SecurityIdentity(final SecurityIdentity old, final IdentityCredentials credentials, final boolean isPrivate) { + this.securityDomain = old.securityDomain; + this.principal = old.principal; + this.realmInfo = old.realmInfo; + this.authorizationIdentity = old.authorizationIdentity; + this.defaultRoles = old.defaultRoles; + this.roleMappers = old.roleMappers; + this.creationTime = old.creationTime; + this.verifier = old.verifier; + this.publicCredentials = isPrivate ? old.publicCredentials : old.publicCredentials.with(credentials); + this.privateCredentials = isPrivate ? old.privateCredentials.with(credentials) : old.privateCredentials; + this.withSuppliedIdentities = old.withSuppliedIdentities; + this.withIdentities = old.withIdentities; + } + + SecurityIdentity(final SecurityIdentity old, final Supplier withSuppliedIdentites) { + this.securityDomain = old.securityDomain; + this.principal = old.principal; + this.realmInfo = old.realmInfo; + this.authorizationIdentity = old.authorizationIdentity; + this.defaultRoles = old.defaultRoles; + this.roleMappers = old.roleMappers; + this.creationTime = old.creationTime; + this.verifier = old.verifier; + this.publicCredentials = old.publicCredentials; + this.privateCredentials = old.privateCredentials; + this.withSuppliedIdentities = withSuppliedIdentites; + this.withIdentities = null; + } + + SecurityIdentity(final SecurityIdentity old, final SecurityIdentity[] withIdentities) { + this.securityDomain = old.securityDomain; + this.principal = old.principal; + this.realmInfo = old.realmInfo; + this.authorizationIdentity = old.authorizationIdentity; + this.defaultRoles = old.defaultRoles; + this.roleMappers = old.roleMappers; + this.creationTime = old.creationTime; + this.verifier = old.verifier; + this.publicCredentials = old.publicCredentials; + this.privateCredentials = old.privateCredentials; + this.withSuppliedIdentities = null; + this.withIdentities = withIdentities; + } + + SecurityIdentity(final SecurityIdentity old, final FunctiondefaultRoles) { + this.securityDomain = old.securityDomain; + this.principal = old.principal; + this.realmInfo = old.realmInfo; + this.authorizationIdentity = old.authorizationIdentity; + this.defaultRoles = defaultRoles; + this.roleMappers = old.roleMappers; + this.creationTime = old.creationTime; + this.verifier = old.verifier; + this.publicCredentials = old.publicCredentials; + this.privateCredentials = old.privateCredentials; + this.withSuppliedIdentities = null; + this.withIdentities = old.withIdentities; + } + + SecurityIdentity(final SecurityIdentity old, final Attributes runtimeAttributes) { + this.securityDomain = old.securityDomain; + this.principal = old.principal; + this.realmInfo = old.realmInfo; + this.authorizationIdentity = AuthorizationIdentity.basicIdentity(old.authorizationIdentity, runtimeAttributes); + this.defaultRoles = old.defaultRoles; + this.roleMappers = old.roleMappers; + this.creationTime = old.creationTime; + this.verifier = old.verifier; + this.publicCredentials = old.publicCredentials; + this.privateCredentials = old.privateCredentials; + this.withSuppliedIdentities = null; + this.withIdentities = old.withIdentities; + } + + SecurityDomain getSecurityDomain() { + return securityDomain; + } + + RealmInfo getRealmInfo() { + return this.realmInfo; + } + + AuthorizationIdentity getAuthorizationIdentity() { + return authorizationIdentity; + } + + @SuppressWarnings("unchecked") + private Supplier[] establishIdentities() { + SecurityIdentity[] withIdentities = this.withIdentities != null ? this.withIdentities : withSuppliedIdentities != null ? withSuppliedIdentities.get() : NO_IDENTITIES; + if (withIdentities.length == 0) { + return NO_IDENTITIES; + } + + Supplier[] oldIdentities = new Supplier[withIdentities.length]; + for (int i = 0; i < withIdentities.length; i++) { + Supplier securityIdentity = withIdentities[i]; + oldIdentities[i] = securityIdentity.get().getSecurityDomain().getAndSetCurrentSecurityIdentity(securityIdentity); + } + + return oldIdentities; + } + + private void restoreIdentities(Supplier[] securityIdentities) { + for (Supplier currentIdentity : securityIdentities) { + currentIdentity.get().securityDomain.setCurrentSecurityIdentity(currentIdentity); + } + } + + /** + * Run an action under this identity. + * + * @param action the action to run + * @param the action return type + * @return the action result (may be {@code null}) + * @deprecated Use {@link #runAsSupplier(Supplier)} instead. + */ + @Deprecated + public T runAs(PrivilegedAction action) { + if (action == null) return null; + return runAs(action, (ParametricPrivilegedAction>) PrivilegedAction::run); + } + + /** + * Run an action under this identity. + * + * @param action the action to run + * @param the action return type + * @return the action result (may be {@code null}) + * @throws PrivilegedActionException if the action fails + * @deprecated Use {@link #runAsSupplierEx(ExceptionSupplier)} instead. + */ + @Deprecated + public T runAs(PrivilegedExceptionAction action) throws PrivilegedActionException { + if (action == null) return null; + return runAs(action, (ParametricPrivilegedExceptionAction>) PrivilegedExceptionAction::run); + } + + /** + * Run an action under this identity. + * + * @param parameter the parameter to pass to the action + * @param action the action to run + * @param the action return type + * @param

the action parameter type + * @return the action result (may be {@code null}) + * @deprecated Use {@link #runAsFunction(Function, Object)} instead. + */ + @Deprecated + public T runAs(P parameter, ParametricPrivilegedAction action) { + if (action == null) return null; + final Supplier[] oldWithIdentities = establishIdentities(); + final Supplier oldIdentity = securityDomain.getAndSetCurrentSecurityIdentity(this); + try { + return action.run(parameter); + } finally { + securityDomain.setCurrentSecurityIdentity(oldIdentity); + restoreIdentities(oldWithIdentities); + } + } + + /** + * Run an action under this identity. + * + * @param parameter the parameter to pass to the action + * @param action the action to run + * @param the action return type + * @param

the action parameter type + * @return the action result (may be {@code null}) + * @throws PrivilegedActionException if the action fails + * @deprecated Use {@link #runAsFunctionEx(ExceptionFunction, Object)} instead. + */ + @Deprecated + public T runAs(P parameter, ParametricPrivilegedExceptionAction action) throws PrivilegedActionException { + if (action == null) return null; + final Supplier[] oldWithIdentities = establishIdentities(); + final Supplier oldIdentity = securityDomain.getAndSetCurrentSecurityIdentity(this); + try { + return action.run(parameter); + } catch (RuntimeException | PrivilegedActionException e) { + throw e; + } catch (Exception e) { + throw new PrivilegedActionException(e); + } finally { + securityDomain.setCurrentSecurityIdentity(oldIdentity); + restoreIdentities(oldWithIdentities); + } + } + + /** + * Run an action under this identity. + * + * @param parameter1 the first parameter to pass to the action + * @param parameter2 the second parameter to pass to the action + * @param action the action to run + * @param the action return type + * @param the action first parameter type + * @param the action second parameter type + * @return the action result (may be {@code null}) + */ + public R runAsFunction(BiFunction action, T parameter1, U parameter2) { + if (action == null) return null; + final Supplier[] oldWithIdentities = establishIdentities(); + final Supplier oldIdentity = securityDomain.getAndSetCurrentSecurityIdentity(this); + try { + return action.apply(parameter1, parameter2); + } finally { + securityDomain.setCurrentSecurityIdentity(oldIdentity); + restoreIdentities(oldWithIdentities); + } + } + + /** + * Run an action under this identity. + * + * @param parameter1 the first parameter to pass to the action + * @param parameter2 the second parameter to pass to the action + * @param action the action to run + * @param the action first parameter type + * @param the action second parameter type + */ + public void runAsConsumer(BiConsumer action, T parameter1, U parameter2) { + if (action == null) return; + final Supplier[] oldWithIdentities = establishIdentities(); + final Supplier oldIdentity = securityDomain.getAndSetCurrentSecurityIdentity(this); + try { + action.accept(parameter1, parameter2); + } finally { + securityDomain.setCurrentSecurityIdentity(oldIdentity); + restoreIdentities(oldWithIdentities); + } + } + + /** + * Run an action under this identity. + * + * @param parameter1 the first parameter to pass to the action + * @param parameter2 the second parameter to pass to the action + * @param action the action to run + * @param the action first parameter type + */ + public void runAsObjIntConsumer(ObjIntConsumer action, T parameter1, int parameter2) { + if (action == null) return; + final Supplier[] oldWithIdentities = establishIdentities(); + final Supplier oldIdentity = securityDomain.getAndSetCurrentSecurityIdentity(this); + try { + action.accept(parameter1, parameter2); + } finally { + securityDomain.setCurrentSecurityIdentity(oldIdentity); + restoreIdentities(oldWithIdentities); + } + } + + /** + * Run an action under this identity. + * + * @param parameter1 the first parameter to pass to the action + * @param parameter2 the second parameter to pass to the action + * @param action the action to run + * @param the action return type + * @param the action first parameter type + * @param the action second parameter type + * @param the action exception type + * @return the action result (may be {@code null}) + * @throws E if the action throws this exception + */ + public R runAsFunctionEx(ExceptionBiFunction action, T parameter1, U parameter2) throws E { + if (action == null) return null; + final Supplier[] oldWithIdentities = establishIdentities(); + final Supplier oldIdentity = securityDomain.getAndSetCurrentSecurityIdentity(this); + try { + return action.apply(parameter1, parameter2); + } finally { + securityDomain.setCurrentSecurityIdentity(oldIdentity); + restoreIdentities(oldWithIdentities); + } + } + + /** + * Run an action under this identity. + * + * @param parameter1 the first parameter to pass to the action + * @param parameter2 the second parameter to pass to the action + * @param action the action to run + * @param the action first parameter type + * @param the action second parameter type + * @param the action exception type + * @throws E if the action throws this exception + */ + public void runAsConsumerEx(ExceptionBiConsumer action, T parameter1, U parameter2) throws E { + if (action == null) return; + final Supplier[] oldWithIdentities = establishIdentities(); + final Supplier oldIdentity = securityDomain.getAndSetCurrentSecurityIdentity(this); + try { + action.accept(parameter1, parameter2); + } finally { + securityDomain.setCurrentSecurityIdentity(oldIdentity); + restoreIdentities(oldWithIdentities); + } + } + + /** + * Run an action under this identity. + * + * @param parameter1 the first parameter to pass to the action + * @param parameter2 the second parameter to pass to the action + * @param action the action to run + * @param the action first parameter type + * @param the action exception type + * @throws E if the action throws this exception + */ + public void runAsObjIntConsumerEx(ExceptionObjIntConsumer action, T parameter1, int parameter2) throws E { + if (action == null) return; + final Supplier[] oldWithIdentities = establishIdentities(); + final Supplier oldIdentity = securityDomain.getAndSetCurrentSecurityIdentity(this); + try { + action.accept(parameter1, parameter2); + } finally { + securityDomain.setCurrentSecurityIdentity(oldIdentity); + restoreIdentities(oldWithIdentities); + } + } + + /** + * Run an action under this identity. + * + * @param parameter1 the first parameter to pass to the action + * @param parameter2 the second parameter to pass to the action + * @param action the action to run + * @param the action first parameter type + * @param the action second parameter type + * @return the action result (may be {@code null}) + */ + public boolean runAsBiPredicate(BiPredicate action, T parameter1, U parameter2) { + if (action == null) return false; + final Supplier[] oldWithIdentities = establishIdentities(); + final Supplier oldIdentity = securityDomain.getAndSetCurrentSecurityIdentity(this); + try { + return action.test(parameter1, parameter2); + } finally { + securityDomain.setCurrentSecurityIdentity(oldIdentity); + restoreIdentities(oldWithIdentities); + } + } + + /** + * Run an action under this identity. + * + * @param parameter1 the first parameter to pass to the action + * @param parameter2 the second parameter to pass to the action + * @param action the action to run + * @param the action first parameter type + * @param the action second parameter type + * @param the action exception type + * @return the action result (may be {@code null}) + * @throws E if the action throws this exception + */ + public boolean runAsExBiPredicate(ExceptionBiPredicate action, T parameter1, U parameter2) throws E { + if (action == null) return false; + final Supplier[] oldWithIdentities = establishIdentities(); + final Supplier oldIdentity = securityDomain.getAndSetCurrentSecurityIdentity(this); + try { + return action.test(parameter1, parameter2); + } finally { + securityDomain.setCurrentSecurityIdentity(oldIdentity); + restoreIdentities(oldWithIdentities); + } + } + + /** + * Run an action under a series of identities. + * + * @param action the action to run + * @param identities the identities to set up + * @param the action return type + * @return the action result (may be {@code null}) + * @throws PrivilegedActionException if the action fails + */ + @SuppressWarnings({ "unchecked", "rawtypes" }) + public static T runAsAll(PrivilegedExceptionAction action, SecurityIdentity... identities) throws PrivilegedActionException { + if (action == null) return null; + int length = identities.length; + Supplier[] oldIdentities = new Supplier[length]; + for (int i = 0; i < length; i++) { + Supplier securityIdentity = identities[i]; + SecurityDomain securityDomain = securityIdentity.get().getSecurityDomain(); + oldIdentities[i] = securityDomain.getAndSetCurrentSecurityIdentity(securityIdentity); + } + try { + return action.run(); + } catch (RuntimeException | PrivilegedActionException e) { + throw e; + } catch (Exception e) { + throw new PrivilegedActionException(e); + } finally { + for (int i = 0; i < length; i++) { + Supplier oldIdentity = oldIdentities[i]; + SecurityDomain securityDomain = oldIdentity.get().getSecurityDomain(); + securityDomain.setCurrentSecurityIdentity(oldIdentity); + } + } + } + + /** + * Get the roles associated with this identity. + * + * @return the roles associated with this identity + */ + public Roles getRoles() { + return defaultRoles.apply(this); + } + + /** + * Get the mapped roles associated with this identity. If no role mapping exists for the given category, an + * empty role set is returned. + * + * @param category the role mapping category + * @return the category roles + */ + public Roles getRoles(String category) { + return getRoles(category, false); + } + + + /** + * Attempt to create a new identity that is the same as this identity but with a {@link Supplier Supplier} to supply identities that will be associated with all 'run' calls. + * + * Any existing individual identities associated with this identity will be dropped. + * + * The supplier will be called for each run call so were possible should handle it's own optimisation. + * + * @param securityIdentities a {@link Supplier Supplier} for identities to be associated with every run call. + * @return the new identity + * @throws IllegalArgumentException if the supplied identity + */ + public SecurityIdentity withSecurityIdentitySupplier(Supplier securityIdentities) { + Assert.checkNotNullParam("securityIdentities", securityIdentities); + if (this.withSuppliedIdentities == securityIdentities) { + return this; + } + + return new SecurityIdentity(this, securityIdentities); + } + + /** + * Attempt to create a new identity that is the same as this identity but with an additional identity from a different + * security domain that will be associated with all 'run' calls. + * + * If a {@link Supplier Supplier} has previously been associated with this identity it will be dropped. + * + * @param securityIdentity the {@link SecurityIdentity} to also be associated with all run calls made to this identity. + * @return the new identity + * @throws IllegalArgumentException if the supplied identity + */ + public SecurityIdentity withSecurityIdentity(SecurityIdentity securityIdentity) { + Assert.checkNotNullParam("securityIdentity", securityIdentity); + if (securityIdentity == this) { + return this; + } + + if (securityDomain == securityIdentity.securityDomain) { + throw log.cantWithSameSecurityDomainDomain(); + } + + int oldCapacity = this.withIdentities == null ? 0 : this.withIdentities.length; + List withIdentities = new ArrayList<>(oldCapacity + 1); + if (oldCapacity != 0) { + for (SecurityIdentity currentIdentity : this.withIdentities) { + if (currentIdentity == securityIdentity) { + return this; // already added + } + + if (currentIdentity.securityDomain != securityIdentity.securityDomain) { + withIdentities.add(currentIdentity); // re-add identities from other domains + } + } + } + withIdentities.add(securityIdentity); + + return new SecurityIdentity(this, withIdentities.toArray(new SecurityIdentity[0])); + } + + /** + * Get the mapped roles associated with this identity. + * + * @param category the role mapping category + * @param fallbackToDefault {@code true} if the default roles associated with this identity should be returned if no + * role mapping exists for the given category, {@code false} otherwise + * @return the category roles + */ + public Roles getRoles(String category, boolean fallbackToDefault) { + final RoleMapper roleMapper = roleMappers.get(category); + return roleMapper == null ? (fallbackToDefault ? getRoles() : Roles.NONE) : roleMapper.mapRoles(getRoles()); + } + + /** + * Attempt to create a new identity which replaces a role mapper category on the current identity. If the given role + * mapper is already set on the current identity, the current identity is returned. + * + * @param category the category name + * @param roleMapper the role mapper to use + * @return the new identity + * @throws SecurityException if the calling class is not granted the {@link ChangeRoleMapperPermission} for the given + * category name + */ + public SecurityIdentity withRoleMapper(String category, RoleMapper roleMapper) { + Assert.checkNotNullParam("category", category); + Assert.checkNotNullParam("roleMapper", roleMapper); + final Map roleMappers = this.roleMappers; + final RoleMapper existingRoleMapper = roleMappers.get(category); + if (existingRoleMapper == roleMapper) { + // identical + return this; + } + // it's a change of some sort + final SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission(new ChangeRoleMapperPermission(category)); + } + // authorized; next see if we can use a memory-efficient collection + final Map newMap; + if (roleMappers.isEmpty() || roleMappers.size() == 1 && roleMappers.keySet().iterator().next().equals(category)) { + newMap = Collections.singletonMap(category, roleMapper); + } else { + newMap = new HashMap<>(roleMappers); + newMap.put(category, roleMapper); + } + return new SecurityIdentity(this, newMap); + } + + /** + * Attempt to create a new identity which wraps the default roles with a default role mapper. + * + * @param roleMapper the roleMapper to map the roles. + * @return the new identity + * @throws SecurityException if the calling class is not granted the withDefaultRoleMapper permission. + */ + public SecurityIdentity withDefaultRoleMapper(final RoleMapper roleMapper) { + final SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission(WITH_DEFAULT_ROLE_MAPPER_PERMISSION); + } + + return new SecurityIdentity(this, (SecurityIdentity si) -> roleMapper.mapRoles(this.getRoles())); + } + + /** + * Attempt to create a new identity that can be used to run as a user with the given name. If the + * current identity is not authorized to run as a user with the given name, an exception is thrown. + * + * Calling with enabled security manager requires {@code setRunAsPrincipal} {@link ElytronPermission}. + * Regardless security manager is enabled, {@link RunAsPrincipalPermission} for given name is required. + * + * @param name the name to attempt to run as + * @return the new security identity + * @throws SecurityException if the operation authorization failed for any reason + */ + public SecurityIdentity createRunAsIdentity(String name) throws SecurityException { + return createRunAsIdentity(name, true); + } + + /** + * Attempt to create a new identity that can be used to run as a user with the given name. + * + * Calling with enabled security manager requires {@code setRunAsPrincipal} {@link ElytronPermission}. + * + * @param name the name to attempt to run as + * @param authorize whether to check the current identity is authorized to run as a user + * with the given principal (has {@link RunAsPrincipalPermission}) + * @return the new security identity + * @throws SecurityException if the caller does not have the {@code setRunAsPrincipal} + * {@link ElytronPermission} or if the operation authorization failed for any other reason + */ + public SecurityIdentity createRunAsIdentity(String name, boolean authorize) throws SecurityException { + Assert.checkNotNullParam("name", name); + return createRunAsIdentity(new NamePrincipal(name), authorize); + } + + /** + * Attempt to create a new identity that can be used to run as a user with the given principal. + * + * Calling with enabled security manager requires {@code setRunAsPrincipal} {@link ElytronPermission}. + * + * @param principal the principal to attempt to run as + * @param authorize whether to check the current identity is authorized to run as a user + * with the given principal (has {@link RunAsPrincipalPermission}) + * @return the new security identity + * @throws SecurityException if the caller does not have the {@code setRunAsPrincipal} + * {@link ElytronPermission} or if the operation authorization failed for any other reason + */ + public SecurityIdentity createRunAsIdentity(Principal principal, boolean authorize) throws SecurityException { + Assert.checkNotNullParam("principal", principal); + + final SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission(SET_RUN_AS_PERMISSION); + } + + try (final ServerAuthenticationContext context = securityDomain.createNewAuthenticationContext(this, MechanismConfigurationSelector.constantSelector(MechanismConfiguration.EMPTY))) { + if (! (context.importIdentity(this) && context.authorize(principal, authorize))) { + throw log.runAsAuthorizationFailed(this.principal, principal, null); + } + return context.getAuthorizedIdentity(); + } catch (RealmUnavailableException e) { + throw log.runAsAuthorizationFailed(this.principal, principal, e); + } + } + + /** + * Attempt to create a new identity that can be used to run as an anonymous user. If the + * current identity is not authorized to run as an anonymous user, an exception is thrown. + * + * Calling with enabled security manager requires {@code setRunAsPrincipal} {@link ElytronPermission}. + * {@link org.wildfly.security.auth.permission.LoginPermission} granted to the anonymous identity will be required. + * + * @return the new security identity + * @throws SecurityException if the operation authorization failed for any reason + */ + public SecurityIdentity createRunAsAnonymous() throws SecurityException { + return createRunAsAnonymous(true); + } + + /** + * Attempt to create a new identity that can be used to run as an anonymous user + * + * Calling with enabled security manager requires {@code setRunAsPrincipal} {@link ElytronPermission}. + * + * @param authorize whether to check the anonymous identity is authorized to log in + * (has {@link org.wildfly.security.auth.permission.LoginPermission}) + * @return the new security identity + * @throws SecurityException if the caller does not have the {@code setRunAsPrincipal} + * {@link ElytronPermission} or if the operation authorization failed for any other reason + */ + public SecurityIdentity createRunAsAnonymous(boolean authorize) throws SecurityException { + final SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission(SET_RUN_AS_PERMISSION); + } + + try (final ServerAuthenticationContext context = securityDomain.createNewAuthenticationContext(this, MechanismConfigurationSelector.constantSelector(MechanismConfiguration.EMPTY))) { + if (! context.authorizeAnonymous(authorize)) { + throw log.runAsAuthorizationFailed(principal, AnonymousPrincipal.getInstance(), null); + } + return context.getAuthorizedIdentity(); + } + } + + /** + * Create a new security identity which is the same as this one, but which limits authorization privileges to the + * intersection of the current privileges and the given verifier. + * + * @param verifier the restricted verifier (must not be {@code null}) + * @return the restricted identity + */ + public SecurityIdentity intersectWith(PermissionVerifier verifier) { + Assert.checkNotNullParam("verifier", verifier); + return new SecurityIdentity(this, this.verifier.and(verifier)); + } + + public boolean implies(final Permission permission) { + final boolean result = verifier.implies(permission); + SecurityDomain.safeHandleSecurityEvent(securityDomain, + result ? new SecurityPermissionCheckSuccessfulEvent(this, permission) : new SecurityPermissionCheckFailedEvent(this, permission)); + return result; + } + + /** + * Get the attributes associated with this identity. + * + * @return a read-only instance of {@link Attributes} with all attributes associated with this identity + */ + public Attributes getAttributes() { + return this.authorizationIdentity.getAttributes().asReadOnly(); + } + + /** + * Get the principal of this identity. + * + * @return the principal of this identity + */ + public Principal getPrincipal() { + return principal; + } + + /** + * Get the creation time of this identity, which is the time that the initial authentication occurred. + * + * @return the creation time of this identity (not {@code null}) + */ + public Instant getCreationTime() { + return creationTime; + } + + /** + * Get the public credentials of this identity. + * + * @return the public credentials of this identity (not {@code null}) + */ + public IdentityCredentials getPublicCredentials() { + return publicCredentials; + } + + /** + * Convenience method to determine if this identity is anonymous. + * + * @return {@code true} if the identity is anonymous, {@code false} otherwise + */ + public boolean isAnonymous() { + return principal instanceof AnonymousPrincipal; + } + + /** + * Create a new security identity which is the same as this one, but which includes the given credential as a + * public credential. + * + * @param credential the credential (must not be {@code null}) + * @return the new identity + */ + public SecurityIdentity withPublicCredential(Credential credential) { + Assert.checkNotNullParam("credential", credential); + return new SecurityIdentity(this, credential, false); + } + + /** + * Create a new security identity which is the same as this one, but which includes the given credentials as + * public credentials. + * + * @param credentials the credential set (must not be {@code null}) + * @return the new identity + */ + public SecurityIdentity withPublicCredentials(final IdentityCredentials credentials) { + Assert.checkNotNullParam("credentials", credentials); + return credentials == IdentityCredentials.NONE ? this : new SecurityIdentity(this, credentials, false); + } + + /** + * Create a new security identity which is the same as this one, but which includes the given credential as a + * private credential. + * + * @param credential the credential (must not be {@code null}) + * @return the new identity + */ + public SecurityIdentity withPrivateCredential(Credential credential) { + Assert.checkNotNullParam("credential", credential); + return new SecurityIdentity(this, credential, true); + } + + /** + * Create a new security identity which is the same as this one, but which includes the given credentials as + * private credentials. + * + * @param credentials the credential set (must not be {@code null}) + * @return the new identity + */ + public SecurityIdentity withPrivateCredentials(final IdentityCredentials credentials) { + Assert.checkNotNullParam("credentials", credentials); + return credentials == IdentityCredentials.NONE ? this : new SecurityIdentity(this, credentials, true); + } + + /** + * Create a new security identity which is the same as this one, but which includes the given runtime attributes. + * + * @param runtimeAttributes the runtime attributes (must not be {@code null}) + * @return the new identity + */ + public SecurityIdentity withRuntimeAttributes(final Attributes runtimeAttributes) { + Assert.checkNotNullParam("runtimeAttributes", runtimeAttributes); + return runtimeAttributes == Attributes.EMPTY ? this : new SecurityIdentity(this, runtimeAttributes); + } + + /** + * Get the private credentials of this identity. The caller must have the {@code getPrivateCredentials} {@link ElytronPermission}. + * + * @return the private credentials of this identity (not {@code null}) + */ + public IdentityCredentials getPrivateCredentials() { + final SecurityManager sm = System.getSecurityManager(); + if (sm != null) { + sm.checkPermission(PRIVATE_CREDENTIALS_PERMISSION); + } + return getPrivateCredentialsPrivate(); + } + + /** + * Get this identity. + * + * @return this identity + */ + public SecurityIdentity get() { + return this; + } + + /** + * Create a new flexible identity association, initializing it with this identity. + * + * @return the new flexible identity association (not {@code null}) + */ + public FlexibleIdentityAssociation createFlexibleAssociation() { + return new FlexibleIdentityAssociation(securityDomain, this); + } + + IdentityCredentials getPrivateCredentialsPrivate() { + return privateCredentials; + } + + @Override + public String toString() { + return "SecurityIdentity{" + + "principal=" + principal + + ", securityDomain=" + securityDomain + + ", authorizationIdentity=" + authorizationIdentity + + ", realmInfo=" + realmInfo + + ", creationTime=" + creationTime + + '}'; + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/server/SecurityRealm.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/server/SecurityRealm.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/server/SecurityRealm.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,175 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2013 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.auth.server; + +import java.security.Principal; +import java.security.spec.AlgorithmParameterSpec; +import java.util.function.Function; + +import org.wildfly.common.Assert; +import org.wildfly.security.auth.server._private.ElytronMessages; +import org.wildfly.security.auth.SupportLevel; +import org.wildfly.security.auth.server.event.RealmEvent; +import org.wildfly.security.credential.Credential; +import org.wildfly.security.evidence.Evidence; + +/** + * A single authentication realm. A realm is backed by a single homogeneous store of identities and credentials. + * + * @author David M. Lloyd + * @author Darran Lofthouse + */ +public interface SecurityRealm { + + /** + * Get a handle for to the identity for the given principal in the context of this security realm. Any + * validation / name mapping is an implementation detail for the realm. The identity may or may not exist. The + * returned handle must be cleaned up by a call to {@link RealmIdentity#dispose()}. + * + * @param principal the principal which identifies the identity within the realm (must not be {@code null}) + * @return the {@link RealmIdentity} for the provided principal (not {@code null}) + */ + default RealmIdentity getRealmIdentity(Principal principal) throws RealmUnavailableException { + return RealmIdentity.NON_EXISTENT; + } + + /** + * Get a handle for to the identity for the given evidence in the context of this security realm. Any validation / name + * mapping is an implementation detail for the realm. The identity may or may not exist. The returned handle must + * be cleaned up by a call to {@link RealmIdentity#dispose()}. + * + * Where this method is used to obtain a {@link RealmIdentity} prior to evidence verification the method + * {@link RealmIdentity#getEvidenceVerifySupport(Class, String)} will be used to verify if the identity is usable. + * + * @param evidence an evidence instance which identifies the identity within the realm (must not be {@code null}) + * @return the {@link RealmIdentity} for the provided evidence (not {@code null}) + */ + default RealmIdentity getRealmIdentity(Evidence evidence) throws RealmUnavailableException { + final Principal principal = evidence.getDecodedPrincipal(); + return principal == null ? RealmIdentity.NON_EXISTENT : getRealmIdentity(principal); + } + + /** + * Get a handle for the identity for the given evidence in the context of this security realm. Any validation / name + * mapping is an implementation detail for the realm. The identity may or may not exist. The principal obtained is + * transformed prior to obtaining the authorization identity. The returned handle must be cleaned up by a call + * to {@link RealmIdentity#dispose()}. + * + * Where this method is used to obtain a {@link RealmIdentity} prior to evidence verification the method + * {@link RealmIdentity#getEvidenceVerifySupport(Class, String)} will be used to verify if the identity is usable. + * + * @param evidence an evidence instance which identifies the identity within the realm (must not be {@code null}) + * @param principalTransformer a function which defines how the principal is transformed before the authorization identity + * is obtained + * @return the {@link RealmIdentity} for the provided evidence (not {@code null}) + */ + default RealmIdentity getRealmIdentity(Evidence evidence, Function principalTransformer) throws RealmUnavailableException { + final Principal principal = evidence.getDecodedPrincipal(); + Principal transformedPrincipal = principalTransformer.apply(principal); + return transformedPrincipal == null ? RealmIdentity.NON_EXISTENT : getRealmIdentity(transformedPrincipal); + } + + /** + * @deprecated Transition method; remove before GA. + */ + default SupportLevel getCredentialAcquireSupport(Class credentialType, String algorithmName) throws RealmUnavailableException { + return getCredentialAcquireSupport(credentialType, algorithmName, null); + } + + /** + * Determine whether a credential of the given type and algorithm is definitely obtainable, possibly obtainable (for] + * some identities), or definitely not obtainable. + * + * @param credentialType the exact credential type (must not be {@code null}) + * @param algorithmName the algorithm name, or {@code null} if any algorithm is acceptable or the credential type does + * not support algorithm names + * @param parameterSpec the algorithm parameters to match, or {@code null} if any parameters are acceptable or the credential type + * does not support algorithm parameters + * @return the level of support for this credential + * @throws RealmUnavailableException if the realm is not able to handle requests for any reason + */ + SupportLevel getCredentialAcquireSupport(Class credentialType, String algorithmName, AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException; + + /** + * Determine whether a given type of evidence is definitely verifiable, possibly verifiable (for some identities), + * or definitely not verifiable. + * + * @param evidenceType the type of evidence to be verified (must not be {@code null}) + * @param algorithmName the algorithm name, or {@code null} if any algorithm is acceptable or the evidence type does + * not support algorithm names + * @return the level of support for this evidence type + * @throws RealmUnavailableException if the realm is not able to handle requests for any reason + */ + SupportLevel getEvidenceVerifySupport(Class evidenceType, String algorithmName) throws RealmUnavailableException; + + /** + * Handle a realm event. These events allow the realm to act upon occurrences that are relevant to policy of + * the realm; for example, the realm may choose to increase password iteration count on authentication success, + * or change the salt of a password after a certain number of authentications. + *

+ * The default implementation does nothing. + * + * @param event the realm event + */ + default void handleRealmEvent(RealmEvent event) {} + + /** + * Safely pass an event to a security realm, absorbing and logging any exception that occurs. + * + * @param realm the security realm to notify (not {@code null}) + * @param event the event to send (not {@code null}) + */ + static void safeHandleRealmEvent(SecurityRealm realm, RealmEvent event) { + Assert.checkNotNullParam("realm", realm); + Assert.checkNotNullParam("event", event); + try { + realm.handleRealmEvent(event); + } catch (Throwable t) { + ElytronMessages.log.eventHandlerFailed(t); + } + } + + /** + * An empty security realm. + */ + SecurityRealm EMPTY_REALM = new SecurityRealm() { + public RealmIdentity getRealmIdentity(final Principal principal) throws RealmUnavailableException { + return RealmIdentity.NON_EXISTENT; + } + + public RealmIdentity getRealmIdentity(final Evidence evidence) throws RealmUnavailableException { + return RealmIdentity.NON_EXISTENT; + } + + public SupportLevel getCredentialAcquireSupport(final Class credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException { + Assert.checkNotNullParam("credentialType", credentialType); + return SupportLevel.UNSUPPORTED; + } + + public SupportLevel getEvidenceVerifySupport(final Class evidenceType, final String algorithmName) throws RealmUnavailableException { + Assert.checkNotNullParam("evidenceType", evidenceType); + return SupportLevel.UNSUPPORTED; + } + + @Override + public String toString() { + return "EMPTY_REALM"; + } + }; +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/server/ServerAuthenticationContext.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/server/ServerAuthenticationContext.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/server/ServerAuthenticationContext.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,2580 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2014 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.auth.server; + +import static org.wildfly.common.Assert.checkNotNullParam; +import static org.wildfly.security.auth.server._private.ElytronMessages.log; +import static org.wildfly.security.authz.RoleDecoder.KEY_SOURCE_ADDRESS; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.security.NoSuchAlgorithmException; +import java.security.Principal; +import java.security.cert.X509Certificate; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.InvalidKeySpecException; +import java.util.Collection; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.concurrent.atomic.AtomicReference; +import java.util.function.Function; +import java.util.function.Predicate; + +import javax.net.ssl.SSLPeerUnverifiedException; +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.NameCallback; +import javax.security.auth.callback.PasswordCallback; +import javax.security.auth.callback.UnsupportedCallbackException; +import javax.security.sasl.AuthorizeCallback; +import javax.security.sasl.RealmCallback; + +import org.wildfly.common.Assert; +import org.wildfly.security.auth.SupportLevel; +import org.wildfly.security.auth.callback.AnonymousAuthorizationCallback; +import org.wildfly.security.auth.callback.AuthenticationCompleteCallback; +import org.wildfly.security.auth.callback.AuthenticationConfigurationCallback; +import org.wildfly.security.auth.callback.AvailableRealmsCallback; +import org.wildfly.security.auth.callback.CachedIdentityAuthorizeCallback; +import org.wildfly.security.auth.callback.CallbackUtil; +import org.wildfly.security.auth.callback.ChannelBindingCallback; +import org.wildfly.security.auth.callback.CredentialCallback; +import org.wildfly.security.auth.callback.CredentialUpdateCallback; +import org.wildfly.security.auth.callback.EvidenceDecodePrincipalCallback; +import org.wildfly.security.auth.callback.EvidenceVerifyCallback; +import org.wildfly.security.auth.callback.ExclusiveNameCallback; +import org.wildfly.security.auth.callback.FastUnsupportedCallbackException; +import org.wildfly.security.auth.callback.PrincipalAuthorizeCallback; +import org.wildfly.security.auth.callback.MechanismInformationCallback; +import org.wildfly.security.auth.callback.IdentityCredentialCallback; +import org.wildfly.security.auth.callback.PeerPrincipalCallback; +import org.wildfly.security.auth.callback.RequestInformationCallback; +import org.wildfly.security.auth.callback.SSLCallback; +import org.wildfly.security.auth.callback.SecurityIdentityCallback; +import org.wildfly.security.auth.callback.ServerCredentialCallback; +import org.wildfly.security.auth.callback.SocketAddressCallback; +import org.wildfly.security.auth.permission.LoginPermission; +import org.wildfly.security.auth.permission.RunAsPrincipalPermission; +import org.wildfly.security.auth.principal.AnonymousPrincipal; +import org.wildfly.security.auth.principal.NamePrincipal; +import org.wildfly.security.auth.server.event.RealmFailedAuthenticationEvent; +import org.wildfly.security.auth.server.event.RealmIdentityFailedAuthorizationEvent; +import org.wildfly.security.auth.server.event.RealmIdentitySuccessfulAuthorizationEvent; +import org.wildfly.security.auth.server.event.RealmSuccessfulAuthenticationEvent; +import org.wildfly.security.auth.server.event.SecurityAuthenticationFailedEvent; +import org.wildfly.security.auth.server.event.SecurityAuthenticationSuccessfulEvent; +import org.wildfly.security.auth.server.event.SecurityRealmUnavailableEvent; +import org.wildfly.security.authz.AggregateAttributes; +import org.wildfly.security.authz.Attributes; +import org.wildfly.security.authz.AuthorizationIdentity; +import org.wildfly.security.authz.MapAttributes; +import org.wildfly.security.credential.Credential; +import org.wildfly.security.credential.PasswordCredential; +import org.wildfly.security.credential.source.CredentialSource; +import org.wildfly.security.evidence.AlgorithmEvidence; +import org.wildfly.security.evidence.Evidence; +import org.wildfly.security.evidence.X509PeerCertificateChainEvidence; +import org.wildfly.security.password.Password; +import org.wildfly.security.password.PasswordFactory; +import org.wildfly.security.password.TwoWayPassword; +import org.wildfly.security.password.interfaces.DigestPassword; +import org.wildfly.security.password.spec.ClearPasswordSpec; +import org.wildfly.security.ssl.SSLConnection; +import org.wildfly.security.x500.X500; +import org.wildfly.security.auth.server._private.ElytronMessages; + +/** + * Server-side authentication context. Instances of this class are used to perform all authentication and re-authorization + * operations that involve the usage of an identity in a {@linkplain SecurityDomain security domain}. + *

+ * There are various effective states, described as follows: + *

    + *
  • The inactive state.
  • + *
  • + * The unassigned states: + *
      + *
    • Initial
    • + *
    • Realm-assigned
    • + *
    + *
  • + *
  • The assigned state
  • + *
  • + * The authorized states: + *
      + *
    • Anonymous-authorized
    • + *
    • Authorized
    • + *
    • Authorized-authenticated
    • + *
    + *
  • + *
  • + * The terminal states: + *
      + *
    • Complete
    • + *
    • Failed
    • + *
    + *
  • + *
+ * + *

+ * When an instance of this class is first constructed, it is in the inactive state. In this state, the context retains + * a captured {@linkplain SecurityIdentity identity} and contains a reference to a + * {@linkplain MechanismConfigurationSelector}. The captured identity may be used for various + * context-sensitive authorization decisions. Additional mechanism information can be supplied to this state so that when + * authentication begins an appropriate {@linkplain MechanismConfiguration} can be selected. + *

+ * Once authentication commences the state will automatically transition to the initial state. In this state, the + * context retains an captured {@linkplain SecurityIdentity identity} and a {@linkplain MechanismConfiguration mechanism configuration} + * which was resolved from the information supplied to the inactive state. The captured identity may be + * used for various context-sensitive authorization decisions. The mechanism configuration is used to associate + * an authentication mechanism-specific configuration, including rewriters, {@linkplain MechanismRealmConfiguration mechanism realms}, + * server credential factories, and more. + *

+ * When an authentication mechanism is "realm-aware" (that is, it has a notion of realms that is specific to that particular + * authentication mechanism, e.g. the DIGEST-MD5 SASL mechanism), it + * is necessary for the mechanism to relay the realm selection. This is done by way of the {@link #setMechanismRealmName(String) setMechanismRealmName()} + * method. Calling this method in the initial state causes a transition to the realm-assigned state, + * in which the method may be reinvoked idempotently as long as it is called with the same name (calling the method with + * a different name will result in an exception). + *

+ * The realm-assigned state is nearly identical to the initial state, except that from this state, the + * mechanism realm-specific configuration is applied to all subsequent operation. + *

+ * From these unassigned states, several possible actions may be taken, depending on the necessary progression + * of the authentication: + *

    + *
  • + * A name may be assigned by way of the {@link #setAuthenticationName(String)} method. The name is + * {@linkplain NameRewriter rewritten} and {@linkplain RealmMapper mapped to a realm} according to the + * domain settings, the mechanism configuration, and/or the mechanism realm configuration. The + * {@linkplain SecurityRealm realm} that is the resultant target of the mapping is queried for a + * {@linkplain RealmIdentity realm identity}. The realm identity may or may not be + * existent; this status will affect the outcome of certain operations in subsequent states (as described below). + * After the realm identity is selected, any final rewrite operations which are configured are applied, + * and the resultant name is transformed into a {@link NamePrincipal}, and associated as the + * {@linkplain #getAuthenticationPrincipal() authentication principal} which may subsequently be queried. + *
  • + *
  • + * A principal may be assigned using the {@link #setAuthenticationPrincipal(Principal)} method. The + * principal is {@linkplain PrincipalDecoder decoded} according to the configuration of the security domain (see + * the method documentation for input requirements and failure conditions). Once a name is decoded from the + * principal, it is assigned as described above. + *
  • + *
  • + * A unit of {@linkplain Evidence evidence} may be verified. This is mostly described below in the + * context of the assigned state, but with the important distinction the evidence is first examined + * to locate the corresponding evidence, in the following steps: + *
      + *
    • + * Firstly, the evidence is examined to determine whether it {@linkplain Evidence#getPrincipal() contains a principal}. + * If so, the principal name is first established using the procedure described above, and then the normal + * evidence verification procedure described below commences. + *
    • + *
    • + * Secondly, the evidence is socialized to each realm in turn, to see if a realm can recognize + * and {@linkplain SecurityRealm#getRealmIdentity(Principal) locate} an identity based on + * the evidence. If so, the realm identity is {@linkplain RealmIdentity#getRealmIdentityPrincipal() queried} + * for an authentication principal, which is then decoded and established as described above. Once this + * is done successfully, the evidence verification procedure described below commences. + *
    • + *
    • Finally, if none of these steps succeeds, the verification fails and no state transition occurs.
    • + *
    + *
  • + *
  • + * An identity may be {@linkplain #importIdentity(SecurityIdentity) imported}. In this process, + * a {@link SecurityIdentity} instance is examined to determine whether it can be used to complete an implicit + * authentication operation which would yield an authorized identity. The {@code SecurityIdentity} may + * be from the same domain or from a different one. + *

    + * If the identity being imported is from the same security domain as this context, then the identity + * is implicitly authorized for usage, entering the authorized state described below. + *

    + * If the identity being imported is not from the same security domain, then the principal is extracted + * from the identity and used to assign a realm identity in the same manner as {@link #setAuthenticationPrincipal(Principal)}. + * The domain is then {@linkplain SecurityDomain.Builder#setTrustedSecurityDomainPredicate(Predicate) queried} + * to determine whether the target identity's source domain is trusted. If so, a normal + * authorization is carried out as described below for the assigned state, resulting in an + * authorized-authenticated state. If not, then the realm of the realm identity is + * compared against the realm of the identity being imported. If they are the same, the + * identity is imported and a normal authorization is carried out as described below. + *

  • + *
  • + * An anonymous authorization may be carried out by way of the {@link #authorizeAnonymous()} method. + * If the {@linkplain SecurityDomain#getAnonymousSecurityIdentity() anonymous identity} has the + * {@link LoginPermission} granted to it, the context will transition into the anonymous-authorized + * state; otherwise no state transition occurs. + *
  • + *
  • + * An external authorization may be carried out using the {@link #authorize()} method. The + * captured identity (which may be anonymous) is queried for the presence of the + * {@link LoginPermission}; if present, the context will transition into the authorized or + * anonymous-authorized state (depending on whether the captured identity is anonymous); + * otherwise no state transition occurs. + *
  • + *
  • + * An external run-as authorization may be carried out using the {@link #authorize(String)} method. + * First, the given name is rewritten in the same manner as the {@link #setAuthenticationName(String)} + * method. Then, the captured identity (which may be anonymous) is queried for the presence of a + * {@link RunAsPrincipalPermission} for the target name. If present, the authentication name is assigned + * as described above, and the resultant realm identity is queried for {@link LoginPermission}. If present, + * the context will transition to the authorized-authenticated state. If any step fails, no state transition + * occurs. + *
  • + *
  • + * The authentication may be failed by way of the {@link #fail()} method. This method will dispose + * of all authentication resources and transition to the failed state. + *
  • + *
+ *

+ * In the name-assigned (or, for brevity, assigned) state, the following actions may be performed: + *

    + *
  • + * A name or principal may be assigned as above, however the resultant decoded and rewritten name + * and realm identity must be identical to the previously selected name and identity. + *
  • + *
  • + * Evidence may be verified. The realm identity is queried directly and no state transitions + * will occur. Evidence verification will fail if the evidence has an evidence principal which does + * not result in the same realm identity as the current one after decoding and rewriting. + *
  • + *
  • + * An authorization may be performed via the {@link #authorize()} method. If the selected realm identity + * possesses the {@link LoginPermission}, then the context transitions to the authorized-authenticated state, + * otherwise no state transition occurs. + *
  • + *
  • + * A run-as authorization may be performed via the {@link #authorize(String)} method. + * First, the given name is rewritten in the same manner as the {@link #setAuthenticationName(String)} method. + * The current identity is then authorized as described above, and then the authorized identity + * is tested for a {@link RunAsPrincipalPermission} for the rewritten target name. If authorized, + * the context transitions to the authorized state for the realm identity corresponding to the + * rewritten name; otherwise no state transition occurs. + *
  • + *
  • + * The authentication may be failed by way of the {@link #fail()} method. This method will dispose + * of all authentication resources and transition to the failed state. + *
  • + *
+ *

+ * There are three states related to authorization: the anonymous-authorized state, the authorized state, + * and the authorized-authenticated state. In all three states, the following actions may be taken: + *

    + *
  • + * As above, a name or principal may be assigned so long as it matches the existing identity. In particular, + * for the anonymous-authorized state, all names are rejected, and only the {@linkplain AnonymousPrincipal anonymous principal} + * is accepted. + *
  • + *
  • + * An authorization may be performed via the {@link #authorize()} method. Since the identity is + * always authorized, this is generally a no-op. + *
  • + *
  • + * A run-as authorization may be performed via the {@link #authorize(String)} method. The given + * name is rewritten as previously described, and then the authorized identity + * is tested for a {@link RunAsPrincipalPermission} for the rewritten target name. If authorized, + * the context transitions to the authorized state for the realm identity corresponding to the + * rewritten name; otherwise no state transition occurs. + *
  • + *
  • + * The authentication may be completed by way of the {@link #succeed()} method. This method will + * dispose of all authentication resources and transition to the complete state. + *
  • + *
  • + * The authentication may be failed by way of the {@link #fail()} method. This method will dispose + * of all authentication resources and transition to the failed state. + *
  • + *
+ * The authorized-authenticated state has the additional capability of verifying credentials as described above for + * the assigned state. + *

+ * The complete state has only one capability: the retrieval of the final authorized identity by way + * of the {@link #getAuthorizedIdentity()} method. + *

+ * The failed state has no capabilities and retains no reference to any identities or objects used during + * authentication. + * + * @author David M. Lloyd + * @author Darran Lofthouse + */ +public final class ServerAuthenticationContext implements AutoCloseable { + + private final AtomicReference stateRef; + + ServerAuthenticationContext(final SecurityDomain domain, final MechanismConfigurationSelector mechanismConfigurationSelector) { + this(domain.getCurrentSecurityIdentity(), mechanismConfigurationSelector); + } + + ServerAuthenticationContext(final SecurityIdentity capturedIdentity, final MechanismConfigurationSelector mechanismConfigurationSelector) { + stateRef = new AtomicReference<>(new InactiveState(capturedIdentity, mechanismConfigurationSelector, IdentityCredentials.NONE, IdentityCredentials.NONE, Attributes.EMPTY)); + } + + /** + * Set information about the current mechanism and request for this authentication attempt. If the mechanism + * information cannot be resolved to a mechanism configuration, an exception is thrown. + * + * @param mechanismInformation the mechanism information about the current authentication attempt. + * @throws IllegalStateException if the mechanism information about the current authentication attempt cannot be + * resolved to a mechanism configuration + */ + public void setMechanismInformation(final MechanismInformation mechanismInformation) throws IllegalStateException { + stateRef.get().setMechanismInformation(mechanismInformation); + } + + /** + * Get the authorized identity result of this authentication. + * + * @return the authorized identity + * @throws IllegalStateException if the authentication is incomplete + */ + public SecurityIdentity getAuthorizedIdentity() throws IllegalStateException { + return stateRef.get().getAuthorizedIdentity(); + } + + /** + * Set the authentication to anonymous, completing the authentication process. + * + * @throws IllegalStateException if the authentication is already complete + */ + public boolean authorizeAnonymous() throws IllegalStateException { + return authorizeAnonymous(true); + } + + /** + * Set the authentication to anonymous, completing the authentication process. + * + * @param requireLoginPermission {@code true} if {@link LoginPermission} is required and {@code false} otherwise + * @throws IllegalStateException if the authentication is already complete + */ + public boolean authorizeAnonymous(boolean requireLoginPermission) throws IllegalStateException { + return stateRef.get().authorizeAnonymous(requireLoginPermission); + } + + /** + * Set the authentication name for this authentication. If the name is already set, then the new name must be + * equal to the old name, or else an exception is thrown. + * + * @param name the authentication name + * @throws IllegalArgumentException if the name is syntactically invalid + * @throws RealmUnavailableException if the realm is not available + * @throws IllegalStateException if the authentication name was already set and there is a mismatch + */ + public void setAuthenticationName(String name) throws IllegalArgumentException, RealmUnavailableException, IllegalStateException { + setAuthenticationName(name, false); + } + + /** + * Set the authentication name for this authentication. If the name is already set, then the new name must be + * equal to the old name, or else an exception is thrown. + * + * @param name the authentication name + * @param exclusive {@code true} if exclusive access to the backing identity is required + * @throws IllegalArgumentException if the name is syntactically invalid + * @throws RealmUnavailableException if the realm is not available or if exclusive access to the backing identity + * is required but could not be granted + * @throws IllegalStateException if the authentication name was already set and there is a mismatch + */ + public void setAuthenticationName(String name, boolean exclusive) throws IllegalArgumentException, RealmUnavailableException, IllegalStateException { + Assert.checkNotNullParam("name", name); + setAuthenticationPrincipal(new NamePrincipal(name), exclusive); + } + + /** + * Set the authentication principal for this authentication. Calling this method initiates authentication. + * + * @param principal the authentication principal + * @throws IllegalArgumentException if the principal cannot be mapped to a name, or the mapped name is syntactically invalid + * @throws RealmUnavailableException if the realm is not available + * @throws IllegalStateException if the authentication name was already set + */ + public void setAuthenticationPrincipal(Principal principal) throws IllegalArgumentException, RealmUnavailableException, IllegalStateException { + setAuthenticationPrincipal(principal, false); + } + + /** + * Set the authentication principal for this authentication. Calling this method initiates authentication. + * + * @param principal the authentication principal + * @param exclusive {@code true} if exclusive access to the backing identity is required + * @throws IllegalArgumentException if the principal cannot be mapped to a name, or the mapped name is syntactically invalid + * @throws RealmUnavailableException if the realm is not available + * @throws IllegalStateException if the authentication name was already set + */ + public void setAuthenticationPrincipal(Principal principal, boolean exclusive) throws IllegalArgumentException, RealmUnavailableException, IllegalStateException { + Assert.checkNotNullParam("principal", principal); + stateRef.get().setPrincipal(principal, exclusive); + } + + /** + * Determine if the given name refers to the same identity as the currently set authentication name. + * + * @param name the authentication name + * @return {@code true} if the name matches the current identity, {@code false} otherwise + * @throws IllegalArgumentException if the name is syntactically invalid + * @throws RealmUnavailableException if the realm is not available + * @throws IllegalStateException if the authentication name was already set + */ + public boolean isSameName(String name) throws IllegalArgumentException, RealmUnavailableException, IllegalStateException { + Assert.checkNotNullParam("name", name); + return isSamePrincipal(new NamePrincipal(name)); + } + + /** + * Determine if the given principal refers to the same identity as the currently set authentication name. + * + * @param principal the authentication name + * @return {@code true} if the name matches the current identity, {@code false} otherwise + * @throws IllegalArgumentException if the name is syntactically invalid + * @throws RealmUnavailableException if the realm is not available + * @throws IllegalStateException if the authentication name was already set + */ + public boolean isSamePrincipal(Principal principal) throws IllegalArgumentException, RealmUnavailableException, IllegalStateException { + Assert.checkNotNullParam("principal", principal); + return stateRef.get().isSamePrincipal(principal); + } + + /** + * Determine if the current authentication identity actually exists in the realm. + * + * @return {@code true} if the identity exists, {@code false} otherwise + * @throws RealmUnavailableException if the realm failed to access the identity + * @throws IllegalStateException if there is no authentication name set + */ + public boolean exists() throws RealmUnavailableException, IllegalStateException { + return stateRef.get().getRealmIdentity().exists(); + } + + /** + * Mark this authentication as "failed". The context cannot be used after this method is called. + * + * @throws IllegalStateException if no authentication has been initiated or authentication is already completed + */ + public void fail() throws IllegalStateException { + stateRef.get().fail(true); + } + + /** + * Attempt to authorize an authentication attempt. If the authorization is successful, {@code true} is returned and + * the context is placed in the "authorized" state with the new authorization identity. If the authorization fails, + * {@code false} is returned and the state of the context is unchanged. + * + * @return {@code true} if the authorization succeeded, {@code false} otherwise + * @throws RealmUnavailableException if the realm is not available + * @throws IllegalStateException if the authentication name was not set or authentication was already complete + */ + public boolean authorize() throws RealmUnavailableException, IllegalStateException { + return authorize(true); + } + + boolean authorize(boolean requireLoginPermission) throws RealmUnavailableException, IllegalStateException { + return stateRef.get().authorize(requireLoginPermission); + } + + /** + * Attempt to authorize a change to a new user (possibly including an authentication attempt). If the authorization + * is successful, {@code true} is returned and the context is placed in the "authorized" state with the new authorization + * identity. If the authorization fails, {@code false} is returned and the state of the context is unchanged. + * + * @param name the authorization name (must not be {@code null}) + * @return {@code true} if the authorization succeeded, {@code false} otherwise + * @throws IllegalArgumentException if the name is syntactically invalid + * @throws RealmUnavailableException if the realm is not available + * @throws IllegalStateException if the authentication name was not set or authentication was already complete + */ + public boolean authorize(String name) throws IllegalArgumentException, RealmUnavailableException, IllegalStateException { + checkNotNullParam("name", name); + return authorize(new NamePrincipal(name), true); + } + + /** + * Attempt to authorize a change to a new user (possibly including an authentication attempt). If the authorization + * is successful, {@code true} is returned and the context is placed in the "authorized" state with the new authorization + * identity. If the authorization fails, {@code false} is returned and the state of the context is unchanged. + * + * @param principal the authorization principal (must not be {@code null}) + * @return {@code true} if the authorization succeeded, {@code false} otherwise + * @throws IllegalArgumentException if the principal is syntactically invalid + * @throws RealmUnavailableException if the realm is not available + * @throws IllegalStateException if the authentication principal was not set or authentication was already complete + */ + public boolean authorize(Principal principal) throws IllegalArgumentException, RealmUnavailableException, IllegalStateException { + return authorize(principal, true); + } + + boolean authorize(Principal principal, boolean authorizeRunAs) throws IllegalArgumentException, RealmUnavailableException, IllegalStateException { + checkNotNullParam("principal", principal); + return stateRef.get().authorize(principal, authorizeRunAs); + } + + /** + * Mark this authentication as "successful". The context cannot be used after this method is called, however + * the authorized identity may thereafter be accessed via the {@link #getAuthorizedIdentity()} method. If no + * authentication actually happened, then authentication will complete anonymously. + * + * @throws IllegalStateException if authentication is already completed + * @throws RealmUnavailableException if the realm is not able to handle requests for any reason + */ + public void succeed() throws IllegalStateException, RealmUnavailableException { + stateRef.get().succeed(); + } + + /** + * Determine if authentication was already completed on this context. + * + * @return {@code true} if authentication was completed; {@code false} otherwise + */ + public boolean isDone() { + return stateRef.get().isDone(); + } + + /** + * Get the principal associated with the current authentication name. Only valid during authentication process. + * + * @return the principal + * @throws IllegalStateException if no authentication has been initiated or authentication is already completed + */ + public Principal getAuthenticationPrincipal() { + return stateRef.get().getAuthenticationPrincipal(); + } + + /** + * Determine whether a given credential is definitely obtainable, possibly obtainable, or definitely not obtainable. + * + * If an authentication identity is established this will be for that identity, otherwise this will be the general + * level of support advertised by the security domain. + * + * @param credentialType the credential type class (must not be {@code null}) + * @param algorithmName the algorithm name, or {@code null} if any algorithm is acceptable or the credential type does + * not support algorithm names + * @param parameterSpec the algorithm parameters to match, or {@code null} if any parameters are acceptable or the credential type + * does not support algorithm parameters + * @return the level of support for this credential type + * + * @throws RealmUnavailableException if the realm is not able to handle requests for any reason + * @throws IllegalStateException if no authentication has been initiated or authentication is already completed + */ + public SupportLevel getCredentialAcquireSupport(Class credentialType, String algorithmName, AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException { + Assert.checkNotNullParam("credentialType", credentialType); + return stateRef.get().getCredentialAcquireSupport(credentialType, algorithmName, parameterSpec); + } + + /** + * Determine whether a given credential is definitely obtainable, possibly obtainable, or definitely not obtainable. + * + * If an authentication identity is established this will be for that identity, otherwise this will be the general + * level of support advertised by the security domain. + * + * @param credentialType the credential type class (must not be {@code null}) + * @param algorithmName the algorithm name, or {@code null} if any algorithm is acceptable or the credential type does + * not support algorithm names + * @return the level of support for this credential type + * + * @throws RealmUnavailableException if the realm is not able to handle requests for any reason + * @throws IllegalStateException if no authentication has been initiated or authentication is already completed + */ + public SupportLevel getCredentialAcquireSupport(Class credentialType, String algorithmName) throws RealmUnavailableException { + return getCredentialAcquireSupport(credentialType, algorithmName, null); + } + + /** + * Determine whether a given credential is definitely obtainable, possibly obtainable, or definitely not obtainable. + * + * If an authentication identity is established this will be for that identity, otherwise this will be the general + * level of support advertised by the security domain. + * + * @param credentialType the credential type class (must not be {@code null}) + * @return the level of support for this credential type + * + * @throws RealmUnavailableException if the realm is not able to handle requests for any reason + * @throws IllegalStateException if no authentication has been initiated or authentication is already completed + */ + public SupportLevel getCredentialAcquireSupport(Class credentialType) throws RealmUnavailableException { + Assert.checkNotNullParam("credentialType", credentialType); + return getCredentialAcquireSupport(credentialType, null); + } + + /** + * Determine whether a given piece of evidence is definitely verifiable, possibly verifiable, or definitely not verifiable. + * + * If an authentication identity is established this will be for that identity, otherwise this will be the general + * level of support advertised by the security domain. + * + * @param evidenceType the evidence type class (must not be {@code null}) + * @param algorithmName the algorithm name, or {@code null} if any algorithm is acceptable or the evidence type does + * not support algorithm names + * @return the level of support for this credential type + * + * @throws RealmUnavailableException if the realm is not able to handle requests for any reason + * @throws IllegalStateException if no authentication has been initiated or authentication is already completed + */ + public SupportLevel getEvidenceVerifySupport(Class evidenceType, String algorithmName) throws RealmUnavailableException { + Assert.checkNotNullParam("evidenceType", evidenceType); + return stateRef.get().getEvidenceVerifySupport(evidenceType, algorithmName); + } + + /** + * Determine whether a given piece of evidence is definitely verifiable, possibly verifiable, or definitely not verifiable. + * + * If an authentication identity is established this will be for that identity, otherwise this will be the general + * level of support advertised by the security domain. + * + * @param evidenceType the evidence type class (must not be {@code null}) + * @return the level of support for this credential type + * + * @throws RealmUnavailableException if the realm is not able to handle requests for any reason + * @throws IllegalStateException if no authentication has been initiated or authentication is already completed + */ + public SupportLevel getEvidenceVerifySupport(Class evidenceType) throws RealmUnavailableException { + Assert.checkNotNullParam("evidenceType", evidenceType); + return getEvidenceVerifySupport(evidenceType, null); + } + + /** + * Acquire a credential of the given type. The credential type is defined by its {@code Class} and an optional {@code algorithmName}. If the + * algorithm name is not given, then the query is performed for any algorithm of the given type. + * + * @param credentialType the credential type class (must not be {@code null}) + * @param algorithmName the algorithm name, or {@code null} if any algorithm is acceptable or the credential type does + * not support algorithm names + * @param parameterSpec the algorithm parameters to match, or {@code null} if any parameters are acceptable or the credential type + * does not support algorithm parameters + * @param the credential type + * + * @return the credential, or {@code null} if the principal has no credential of that type + * + * @throws RealmUnavailableException if the realm is not able to handle requests for any reason + * @throws IllegalStateException if no authentication has been initiated or authentication is already completed + */ + public C getCredential(Class credentialType, String algorithmName, AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException { + Assert.checkNotNullParam("credentialType", credentialType); + return stateRef.get().getCredential(credentialType, algorithmName, parameterSpec); + } + + /** + * Acquire a credential of the given type. The credential type is defined by its {@code Class} and an optional {@code algorithmName}. If the + * algorithm name is not given, then the query is performed for any algorithm of the given type. + * + * @param credentialType the credential type class (must not be {@code null}) + * @param algorithmName the algorithm name, or {@code null} if any algorithm is acceptable or the credential type does + * not support algorithm names + * @param the credential type + * + * @return the credential, or {@code null} if the principal has no credential of that type + * + * @throws RealmUnavailableException if the realm is not able to handle requests for any reason + * @throws IllegalStateException if no authentication has been initiated or authentication is already completed + */ + public C getCredential(Class credentialType, String algorithmName) throws RealmUnavailableException { + Assert.checkNotNullParam("credentialType", credentialType); + return stateRef.get().getCredential(credentialType, algorithmName, null); + } + + /** + * Acquire a credential of the given type. The credential type is defined by its {@code Class} and an optional {@code algorithmName}. If the + * algorithm name is not given, then the query is performed for any algorithm of the given type. + * + * @param credentialType the credential type class (must not be {@code null}) + * @param the credential type + * + * @return the credential, or {@code null} if the principal has no credential of that type + * + * @throws RealmUnavailableException if the realm is not able to handle requests for any reason + * @throws IllegalStateException if no authentication has been initiated or authentication is already completed + */ + public C getCredential(Class credentialType) throws RealmUnavailableException { + Assert.checkNotNullParam("credentialType", credentialType); + return stateRef.get().getCredential(credentialType, null, null); + } + + /** + * Apply the given function to the acquired credential, if it is set and of the given type. + * + * @param credentialType the credential type class (must not be {@code null}) + * @param function the function to apply (must not be {@code null}) + * @param the credential type + * @param the return type + * @return the result of the function, or {@code null} if the criteria are not met + * + * @throws RealmUnavailableException if the realm is not able to handle requests for any reason + * @throws IllegalStateException if no authentication has been initiated or authentication is already completed + */ + public R applyToCredential(Class credentialType, Function function) throws RealmUnavailableException { + final Credential credential = getCredential(credentialType); + return credential == null ? null : credential.castAndApply(credentialType, function); + } + + /** + * Apply the given function to the acquired credential, if it is set and of the given type and algorithm. + * + * @param credentialType the credential type class (must not be {@code null}) + * @param algorithmName the algorithm name + * @param function the function to apply (must not be {@code null}) + * @param the credential type + * @param the return type + * @return the result of the function, or {@code null} if the criteria are not met + * + * @throws RealmUnavailableException if the realm is not able to handle requests for any reason + * @throws IllegalStateException if no authentication has been initiated or authentication is already completed + */ + public R applyToCredential(Class credentialType, String algorithmName, Function function) throws RealmUnavailableException { + final Credential credential = getCredential(credentialType, algorithmName); + return credential == null ? null : credential.castAndApply(credentialType, algorithmName, function); + } + + /** + * Apply the given function to the acquired credential, if it is set and of the given type and algorithm. + * + * @param credentialType the credential type class (must not be {@code null}) + * @param algorithmName the algorithm name + * @param parameterSpec the algorithm parameters to match, or {@code null} if any parameters are acceptable or the credential type + * does not support algorithm parameters + * @param function the function to apply (must not be {@code null}) + * @param the credential type + * @param the return type + * @return the result of the function, or {@code null} if the criteria are not met + * + * @throws RealmUnavailableException if the realm is not able to handle requests for any reason + * @throws IllegalStateException if no authentication has been initiated or authentication is already completed + */ + public R applyToCredential(Class credentialType, String algorithmName, AlgorithmParameterSpec parameterSpec, Function function) throws RealmUnavailableException { + final Credential credential = getCredential(credentialType, algorithmName, parameterSpec); + return credential == null ? null : credential.castAndApply(credentialType, algorithmName, parameterSpec, function); + } + + /** + * Verify the given evidence. + * + * @param evidence the evidence to verify + * + * @return {@code true} if verification was successful, {@code false} otherwise + * + * @throws RealmUnavailableException if the realm is not able to handle requests for any reason + * @throws IllegalStateException if no authentication has been initiated or authentication is already completed + */ + public boolean verifyEvidence(Evidence evidence) throws RealmUnavailableException { + Assert.checkNotNullParam("evidence", evidence); + return stateRef.get().verifyEvidence(evidence); + } + + /** + * Set the decoded evidence principal. + * + * @param evidence the evidence to decode and associate with a principal + * @since 1.10.0 + */ + public void setDecodedEvidencePrincipal(Evidence evidence) throws RealmUnavailableException { + Assert.checkNotNullParam("evidence", evidence); + evidence.setDecodedPrincipal(stateRef.get().getSecurityDomain().getEvidenceDecoder().apply(evidence)); + } + + /** + * Add a public credential to the identity being authenticated. + * + * @param credential the credential to add (must not be {@code null}) + */ + public void addPublicCredential(Credential credential) { + Assert.checkNotNullParam("credential", credential); + stateRef.get().addPublicCredential(credential); + } + + /** + * Add a private credential to the identity being authenticated. This credential may be forwarded to outbound + * authentication mechanisms. + * + * @param credential the credential to add (must not be {@code null}) + */ + public void addPrivateCredential(Credential credential) { + Assert.checkNotNullParam("credential", credential); + stateRef.get().addPrivateCredential(credential); + } + + /** + * Add runtime attributes to the identity being authenticated. + * + * @param runtimeAttributes the runtime attributes to add (must not be {@code null}) + */ + public void addRuntimeAttributes(Attributes runtimeAttributes) { + Assert.checkNotNullParam("runtimeAttributes", runtimeAttributes); + stateRef.get().addRuntimeAttributes(runtimeAttributes); + } + + /** + * Attempt to import the given security identity as a trusted identity. If this method returns {@code true}, + * the context will be in an authorized state, and the new identity can be retrieved. + * + * @param identity the identity to import (must not be {@code null}) + * @return {@code true} if the identity is authorized, {@code false} otherwise + * @throws RealmUnavailableException if the realm is not able to handle requests for any reason + */ + public boolean importIdentity(SecurityIdentity identity) throws RealmUnavailableException { + Assert.checkNotNullParam("identity", identity); + return stateRef.get().importIdentity(identity); + } + + /** + * Set the mechanism realm name to be equal to the given name. If no mechanism realms are configured, the realm + * name is ignored. + * + * @param realmName the selected realm name + * @throws IllegalStateException if a realm name was already selected or it is too late to choose a realm + * @throws IllegalArgumentException if the selected realm name was not offered + */ + public void setMechanismRealmName(String realmName) throws IllegalStateException, IllegalArgumentException { + Assert.checkNotNullParam("realmName", realmName); + stateRef.get().setMechanismRealmName(realmName); + } + + /** + * Update the credential for the current authentication identity. + * + * @param credential the new credential (must not be {@code null}) + * @throws RealmUnavailableException if the realm is not able to handle requests for any reason + */ + public void updateCredential(Credential credential) throws RealmUnavailableException { + Assert.checkNotNullParam("credential", credential); + stateRef.get().updateCredential(credential); + } + + /** + * Close the server authentication context, failing any in-progress authentication and releasing any + * associated resources. + */ + public void close() { + stateRef.get().fail(false); + } + + AtomicReference getStateRef() { + return stateRef; + } + + CallbackHandler createCallbackHandler() { + return new CallbackHandler() { + private SSLConnection sslConnection; + private X509Certificate[] peerCerts; + private boolean saslSkipCertificateVerification; + + @Override + public void handle(final Callback[] callbacks) throws IOException, UnsupportedCallbackException { + handleOne(callbacks, 0); + } + + private void handleOne(final Callback[] callbacks, final int idx) throws IOException, UnsupportedCallbackException { + if (idx == callbacks.length) { + return; + } + final AtomicReference stateRef = getStateRef(); + final Callback callback = callbacks[idx]; + if (callback instanceof AnonymousAuthorizationCallback) { + boolean authorized = authorizeAnonymous(); + log.tracef("Handling AnonymousAuthorizationCallback: authorized = %b", authorized); + ((AnonymousAuthorizationCallback) callback).setAuthorized(authorized); + handleOne(callbacks, idx + 1); + } else if (callback instanceof AuthorizeCallback) { + final AuthorizeCallback authorizeCallback = (AuthorizeCallback) callback; + String authenticationID = authorizeCallback.getAuthenticationID(); + if (authenticationID != null) { + // always re-set the authentication name to ensure it hasn't changed. + setAuthenticationName(authenticationID); + } else { + // This is a special case to support scenarios where the identity was already established by some + // external method (e.g.: EXTERNAL SASL and TLS) where only authorization is necessary. We delay authentication + // until we receive an authorization request. + // In the future, we may want to support external methods other than TLS peer authentication + if (stateRef.get().canVerifyEvidence()) { + if (peerCerts != null) { + log.tracef("Authentication ID is null but SSL peer certificates are available. Trying to authenticate peer"); + // if SASL mechanism is used with skip-certificate-verification property then do not verifyEvidence against the security realm + if (saslSkipCertificateVerification) { + // Since evidence verification is being skipped here, ensure evidence decoding still takes place + X509PeerCertificateChainEvidence evidence = new X509PeerCertificateChainEvidence(peerCerts); + setDecodedEvidencePrincipal(evidence); + stateRef.get().setPrincipal(evidence.getDecodedPrincipal(), false); + } + else { + verifyEvidence(new X509PeerCertificateChainEvidence(peerCerts)); + } + } + } + } + String authorizationID = authorizeCallback.getAuthorizationID(); + boolean authorized = authorizationID != null ? authorize(authorizationID) : authorize(); + log.tracef("Handling AuthorizeCallback: authenticationID = %s authorizationID = %s authorized = %b", authenticationID, authorizationID, authorized); + authorizeCallback.setAuthorized(authorized); + handleOne(callbacks, idx + 1); + } else if (callback instanceof ExclusiveNameCallback) { + final ExclusiveNameCallback exclusiveNameCallback = ((ExclusiveNameCallback) callback); + // login name + final String name = exclusiveNameCallback.getDefaultName(); + try { + boolean exclusive = exclusiveNameCallback.needsExclusiveAccess(); + log.tracef("Handling ExclusiveNameCallback: authenticationName = %s needsExclusiveAccess = %b", name, exclusive); + if (exclusive) { + setAuthenticationName(name, true); + exclusiveNameCallback.setExclusiveAccess(true); + } else { + setAuthenticationName(name); + } + } catch (Exception e) { + throw new IOException(e); + } + handleOne(callbacks, idx + 1); + } else if (callback instanceof NameCallback) { + // login name + final String name = ((NameCallback) callback).getDefaultName(); + try { + log.tracef("Handling NameCallback: authenticationName = %s", name); + setAuthenticationName(name); + } catch (Exception e) { + throw new IOException(e); + } + handleOne(callbacks, idx + 1); + } else if (callback instanceof PeerPrincipalCallback) { + // login name + final Principal principal = ((PeerPrincipalCallback) callback).getPrincipal(); + try { + log.tracef("Handling PeerPrincipalCallback: principal = %s", principal); + setAuthenticationPrincipal(principal); + } catch (Exception e) { + throw new IOException(e); + } + handleOne(callbacks, idx + 1); + } else if (callback instanceof PasswordCallback) { + final PasswordCallback passwordCallback = (PasswordCallback) callback; + + if (getCredentialAcquireSupport(PasswordCredential.class).mayBeSupported()) { + final TwoWayPassword password = applyToCredential(PasswordCredential.class, c -> c.getPassword(TwoWayPassword.class)); + if (password != null) { + final ClearPasswordSpec clearPasswordSpec; + try { + final PasswordFactory passwordFactory = PasswordFactory.getInstance(password.getAlgorithm()); + clearPasswordSpec = passwordFactory.getKeySpec(password, ClearPasswordSpec.class); + } catch (InvalidKeySpecException | NoSuchAlgorithmException e) { + log.trace("Unable to get key spec", e); + throw new FastUnsupportedCallbackException(callback); + } + log.tracef("Handling PasswordCallback: obtained successfully"); + passwordCallback.setPassword(clearPasswordSpec.getEncodedPassword()); + handleOne(callbacks, idx + 1); + return; + } + log.tracef("Handling PasswordCallback: failed to obtain PasswordCredential"); + throw new FastUnsupportedCallbackException(callback); + } + + // otherwise just fail out; some mechanisms will try again with different credentials + log.tracef("Handling PasswordCallback: PasswordCredential may not be supported"); + throw new FastUnsupportedCallbackException(callback); + + } else if (callback instanceof CredentialCallback) { + final CredentialCallback credentialCallback = (CredentialCallback) callback; + String requestedRealm = stateRef.get().getMechanismRealmConfiguration().getRealmName(); + + final Credential credential = getCredential(credentialCallback.getCredentialType(), credentialCallback.getAlgorithm(), credentialCallback.getParameterSpec()); + if (credential != null) { + if (credential instanceof PasswordCredential) { + Password password = ((PasswordCredential) credential).getPassword(); + if (password instanceof DigestPassword) { + String providedRealm = ((DigestPassword) password).getRealm(); + if ( ! providedRealm.equals(requestedRealm)) { + log.tracef("Handling CredentialCallback: credential for realm \"%s\" is not available (\"%s\" provided)", requestedRealm, providedRealm); + throw new FastUnsupportedCallbackException(callback); + } else { + log.tracef("Handling CredentialCallback: obtained credential for correct realm \"%s\"", providedRealm); + } + } + } + log.tracef("Handling CredentialCallback: obtained credential: %s", credential); + credentialCallback.setCredential(credential); + handleOne(callbacks, idx + 1); + return; + } + + // otherwise just fail out; some mechanisms will try again with different credentials + log.tracef("Handling CredentialCallback: failed to obtain credential"); + throw new FastUnsupportedCallbackException(callback); + } else if (callback instanceof ServerCredentialCallback) { + final ServerCredentialCallback serverCredentialCallback = (ServerCredentialCallback) callback; + + CredentialSource serverCredentialSource = stateRef.get().getMechanismConfiguration().getServerCredentialSource(); + + final Class credentialType = serverCredentialCallback.getCredentialType(); + final String algorithm = serverCredentialCallback.getAlgorithm(); + final AlgorithmParameterSpec parameterSpec = serverCredentialCallback.getParameterSpec(); + + // optimize for some cases + if (serverCredentialSource.getCredentialAcquireSupport(credentialType, algorithm, parameterSpec).mayBeSupported()) { + final Credential credential = serverCredentialSource.getCredential(credentialType, algorithm, parameterSpec); + if (credential != null) { + log.tracef("Handling ServerCredentialCallback: successfully obtained credential type type=%s, algorithm=%s, params=%s", credentialType, algorithm, parameterSpec); + serverCredentialCallback.setCredential(credential); + handleOne(callbacks, idx + 1); + // return here so we don't double-log, or double-handle callbacks + return; + } + } + log.tracef("Handling ServerCredentialCallback: skipping credential type type=%s, algorithm=%s, params=%s", credentialType, algorithm, parameterSpec); + handleOne(callbacks, idx + 1); + } else if (callback instanceof EvidenceVerifyCallback) { + EvidenceVerifyCallback evidenceVerifyCallback = (EvidenceVerifyCallback) callback; + + evidenceVerifyCallback.setVerified(verifyEvidence(evidenceVerifyCallback.getEvidence())); + + handleOne(callbacks, idx + 1); + } else if (callback instanceof EvidenceDecodePrincipalCallback) { + EvidenceDecodePrincipalCallback evidenceDecodePrincipalCallback = (EvidenceDecodePrincipalCallback) callback; + setDecodedEvidencePrincipal(evidenceDecodePrincipalCallback.getEvidence()); + } else if (callback instanceof SSLCallback) { + SSLCallback sslCallback = (SSLCallback) callback; + this.sslConnection = sslCallback.getSslConnection(); + + try { + peerCerts = X500.asX509CertificateArray(sslCallback.getSslConnection().getSession().getPeerCertificates()); + } catch (SSLPeerUnverifiedException e) { + log.trace("Peer unverified", e); + peerCerts = null; + } + handleOne(callbacks, idx + 1); + } else if (callback instanceof ChannelBindingCallback) { + final SSLConnection sslConnection = this.sslConnection; + if (sslConnection != null) { + sslConnection.handleChannelBindingCallback((ChannelBindingCallback) callback); + } + handleOne(callbacks, idx + 1); + } else if (callback instanceof AuthenticationCompleteCallback) { + if (! isDone()) { + if (((AuthenticationCompleteCallback) callback).succeeded()) { + log.tracef("Handling AuthenticationCompleteCallback: succeed"); + succeed(); + } else { + log.tracef("Handling AuthenticationCompleteCallback: fail"); + fail(); + } + } + handleOne(callbacks, idx + 1); + } else if (callback instanceof SocketAddressCallback) { + final SocketAddressCallback socketAddressCallback = (SocketAddressCallback) callback; + log.tracef("Handling SocketAddressCallback"); + if (socketAddressCallback.getKind() == SocketAddressCallback.Kind.PEER) { + Attributes runtimeAttributes = new MapAttributes(); + if ((socketAddressCallback.getAddress() != null) && ((InetSocketAddress) socketAddressCallback.getAddress()).getAddress() != null) { + runtimeAttributes.addFirst(KEY_SOURCE_ADDRESS, ((InetSocketAddress) socketAddressCallback.getAddress()).getAddress().getHostAddress()); + addRuntimeAttributes(runtimeAttributes); + } else { + log.tracef("Client's IP address is unknown."); + } + } + handleOne(callbacks, idx + 1); + } else if (callback instanceof SecurityIdentityCallback) { + SecurityIdentity identity = getAuthorizedIdentity(); + log.tracef("Handling SecurityIdentityCallback: identity = %s", identity); + ((SecurityIdentityCallback) callback).setSecurityIdentity(identity); + handleOne(callbacks, idx + 1); + } else if (callback instanceof AvailableRealmsCallback) { + Collection names = stateRef.get().getMechanismConfiguration().getMechanismRealmNames(); + if (log.isTraceEnabled()) { + log.tracef("Handling AvailableRealmsCallback: realms = [%s]", String.join(", ", names)); + } + if (! names.isEmpty()) { + ((AvailableRealmsCallback) callback).setRealmNames(names.toArray(new String[names.size()])); + } + handleOne(callbacks, idx + 1); + } else if (callback instanceof RealmCallback) { + RealmCallback rcb = (RealmCallback) callback; + String mechanismRealm = rcb.getText(); + if (mechanismRealm == null) { + mechanismRealm = rcb.getDefaultText(); + } + log.tracef("Handling RealmCallback: selected = [%s]", mechanismRealm); + setMechanismRealmName(mechanismRealm); + handleOne(callbacks, idx + 1); + } else if (callback instanceof MechanismInformationCallback) { + MechanismInformationCallback mic = (MechanismInformationCallback) callback; + try { + MechanismInformation mi = mic.getMechanismInformation(); + if (log.isTraceEnabled()) { + log.tracef("Handling MechanismInformationCallback type='%s' name='%s' host-name='%s' protocol='%s'", + mi.getMechanismType(), mi.getMechanismName(), mi.getHostName(), mi.getProtocol()); + } + setMechanismInformation(mi); + handleOne(callbacks, idx + 1); + } catch (Exception e) { + throw new IOException(e); + } + } else if (callback instanceof CredentialUpdateCallback) { + final CredentialUpdateCallback credentialUpdateCallback = (CredentialUpdateCallback) callback; + log.tracef("Handling CredentialUpdateCallback"); + updateCredential(credentialUpdateCallback.getCredential()); + handleOne(callbacks, idx + 1); + } else if (callback instanceof CachedIdentityAuthorizeCallback) { + CachedIdentityAuthorizeCallback authorizeCallback = (CachedIdentityAuthorizeCallback) callback; + authorizeCallback.setSecurityDomain(stateRef.get().getSecurityDomain()); + SecurityIdentity authorizedIdentity = null; + Principal principal = null; + SecurityIdentity identity = authorizeCallback.getIdentity(); + if (identity != null && importIdentity(identity)) { + authorizedIdentity = getAuthorizedIdentity(); + } else { + principal = authorizeCallback.getPrincipal(); + if (principal == null) { + principal = authorizeCallback.getAuthorizationPrincipal(); + } + if (principal != null) { + setAuthenticationPrincipal(principal); + if (authorize()) { + authorizedIdentity = getAuthorizedIdentity(); + } + } + } + log.tracef("Handling CachedIdentityAuthorizeCallback: principal = %s authorizedIdentity = %s", principal, authorizedIdentity); + authorizeCallback.setAuthorized(authorizedIdentity); + handleOne(callbacks, idx + 1); + } else if (callback instanceof IdentityCredentialCallback) { + IdentityCredentialCallback icc = (IdentityCredentialCallback) callback; + Credential credential = icc.getCredential(); + if (icc.isPrivate()) { + addPrivateCredential(credential); + } else { + addPublicCredential(credential); + } + handleOne(callbacks, idx + 1); + } else if (callback instanceof PrincipalAuthorizeCallback) { + PrincipalAuthorizeCallback authorizeCallback = (PrincipalAuthorizeCallback) callback; + Principal principal = authorizeCallback.getPrincipal(); + // always re-set the principal to ensure it hasn't changed. + setAuthenticationPrincipal(principal); + boolean authorized = authorize(); + log.tracef("Handling PrincipalAuthorizeCallback: principal = %s authorized = %b", principal, authorized); + authorizeCallback.setAuthorized(authorized); + handleOne(callbacks, idx + 1); + } else if (callback instanceof AuthenticationConfigurationCallback) { + AuthenticationConfigurationCallback authenticationConfigurationCallback = (AuthenticationConfigurationCallback) callback; + saslSkipCertificateVerification = authenticationConfigurationCallback.getSaslSkipCertificateVerification(); + handleOne(callbacks, idx + 1); + } else if (callback instanceof RequestInformationCallback) { + HashMap props = ((RequestInformationCallback) callback).getProperties(); + Attributes runtimeAttributes = new MapAttributes(); + for (Map.Entry entry : props.entrySet()) { + if (entry.getValue() != null) { + runtimeAttributes.addFirst(entry.getKey(), entry.getValue().toString()); + } + } + addRuntimeAttributes(runtimeAttributes); + handleOne(callbacks, idx + 1); + } + else { + CallbackUtil.unsupported(callback); + handleOne(callbacks, idx + 1); + } + } + + }; + } + + private static Principal rewriteAll(Principal principal, Function r1, Function r2, Function r3) { + principal = r1.apply(principal); + if (principal == null) return null; + principal = r2.apply(principal); + if (principal == null) return null; + principal = r3.apply(principal); + return principal; + } + + static String mapAll(Principal principal, RealmMapper r1, RealmMapper r2, RealmMapper r3, String defaultRealmName, Evidence evidence) { + if (r1 != null) { + return mapRealmName(principal, r1, defaultRealmName, evidence); + } + if (r2 != null) { + return mapRealmName(principal, r2, defaultRealmName, evidence); + } + if (r3 != null) { + return mapRealmName(principal, r3, defaultRealmName, evidence); + } + return defaultRealmName; + } + + private static String mapRealmName(Principal principal, RealmMapper realmMapper, String defaultRealmName, Evidence evidence) { + String realmName = realmMapper.getRealmMapping(principal, evidence); + return realmName != null ? realmName : defaultRealmName; + } + + State assignName(final SecurityIdentity capturedIdentity, final MechanismConfiguration mechanismConfiguration, final MechanismRealmConfiguration mechanismRealmConfiguration, Principal originalPrincipal, final Evidence evidence, final IdentityCredentials privateCredentials, final IdentityCredentials publicCredentials, final Attributes runtimeAttributes) throws RealmUnavailableException { + return assignName(capturedIdentity, mechanismConfiguration, mechanismRealmConfiguration, originalPrincipal, evidence, privateCredentials, publicCredentials, false, runtimeAttributes); + } + + State assignName(final SecurityIdentity capturedIdentity, final MechanismConfiguration mechanismConfiguration, final MechanismRealmConfiguration mechanismRealmConfiguration, Principal originalPrincipal, final Evidence evidence, final IdentityCredentials privateCredentials, final IdentityCredentials publicCredentials, final boolean exclusive, final Attributes runtimeAttributes) throws RealmUnavailableException { + final SecurityDomain domain = capturedIdentity.getSecurityDomain(); + final Principal preRealmPrincipal = rewriteAll(originalPrincipal, mechanismRealmConfiguration.getPreRealmRewriter(), mechanismConfiguration.getPreRealmRewriter(), domain.getPreRealmRewriter()); + if (preRealmPrincipal == null) { + log.tracef("Unable to rewrite principal [%s] by pre-realm rewritters", originalPrincipal); + return new InvalidNameState(capturedIdentity, mechanismConfiguration, mechanismRealmConfiguration, privateCredentials, publicCredentials, runtimeAttributes); + } + String realmName = mapAll(preRealmPrincipal, mechanismRealmConfiguration.getRealmMapper(), mechanismConfiguration.getRealmMapper(), domain.getRealmMapper(), domain.getDefaultRealmName(), evidence); + final RealmInfo realmInfo = domain.getRealmInfo(realmName); + final Principal postRealmPrincipal = rewriteAll(preRealmPrincipal, mechanismRealmConfiguration.getPostRealmRewriter(), mechanismConfiguration.getPostRealmRewriter(), domain.getPostRealmRewriter()); + if (postRealmPrincipal == null) { + log.tracef("Unable to rewrite principal [%s] by post-realm rewritters", preRealmPrincipal); + return new InvalidNameState(capturedIdentity, mechanismConfiguration, mechanismRealmConfiguration, privateCredentials, publicCredentials, runtimeAttributes); + } + final Principal finalPrincipal = rewriteAll(postRealmPrincipal, mechanismRealmConfiguration.getFinalRewriter(), mechanismConfiguration.getFinalRewriter(), realmInfo.getPrincipalRewriter()); + if (finalPrincipal == null) { + log.tracef("Unable to rewrite principal [%s] by final rewritters", postRealmPrincipal); + return new InvalidNameState(capturedIdentity, mechanismConfiguration, mechanismRealmConfiguration, privateCredentials, publicCredentials, runtimeAttributes); + } + + log.tracef("Principal assigning: [%s], pre-realm rewritten: [%s], realm name: [%s], post-realm rewritten: [%s], realm rewritten: [%s]", + originalPrincipal, preRealmPrincipal, realmName, postRealmPrincipal, finalPrincipal); + + final SecurityRealm securityRealm = realmInfo.getSecurityRealm(); + final RealmIdentity realmIdentity; + try { + if (exclusive) { + if (securityRealm instanceof ModifiableSecurityRealm) { + realmIdentity = ((ModifiableSecurityRealm) securityRealm).getRealmIdentityForUpdate(finalPrincipal); + } else { + throw log.unableToObtainExclusiveAccess(); + } + } else { + realmIdentity = securityRealm.getRealmIdentity(finalPrincipal); + } + } catch (RealmUnavailableException e) { + SecurityDomain.safeHandleSecurityEvent(domain, new SecurityRealmUnavailableEvent(capturedIdentity, realmName)); + throw e; + } + + + return new NameAssignedState(capturedIdentity, realmInfo, realmIdentity, preRealmPrincipal, mechanismConfiguration, mechanismRealmConfiguration, privateCredentials, publicCredentials, runtimeAttributes); + } + + abstract static class State { + MechanismConfiguration getMechanismConfiguration() { + throw log.noAuthenticationInProgress(); + } + + MechanismRealmConfiguration getMechanismRealmConfiguration() { + throw log.noAuthenticationInProgress(); + } + + SecurityIdentity getAuthorizedIdentity() { + throw log.noAuthenticationInProgress(); + } + + Principal getAuthenticationPrincipal() { + throw log.noAuthenticationInProgress(); + } + + boolean isSamePrincipal(Principal principal) { + return false; + } + + SupportLevel getCredentialAcquireSupport(Class credentialType, String algorithmName, final AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException { + throw log.noAuthenticationInProgress(); + } + + SupportLevel getEvidenceVerifySupport(Class evidenceType, String algorithmName) throws RealmUnavailableException { + throw log.noAuthenticationInProgress(); + } + + C getCredential(Class credentialType, String algorithmName, final AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException { + throw log.noAuthenticationInProgress(); + } + + boolean verifyEvidence(final Evidence evidence) throws RealmUnavailableException { + throw log.noAuthenticationInProgress(); + } + + boolean importIdentity(final SecurityIdentity identity) throws RealmUnavailableException { + throw log.noAuthenticationInProgress(); + } + + RealmIdentity getRealmIdentity() { + throw log.noAuthenticationInProgress(); + } + + SecurityDomain getSecurityDomain() { + throw log.noAuthenticationInProgress(); + } + + boolean authorizeAnonymous(final boolean requireLoginPermission) { + throw log.noAuthenticationInProgress(); + } + + void setMechanismInformation(final MechanismInformation mechanismInformation) { + throw log.noAuthenticationInProgress(); + } + + void setPrincipal(Principal principal, boolean exclusive) throws RealmUnavailableException { + throw log.noAuthenticationInProgress(); + } + + boolean authorize(final boolean requireLoginPermission) throws RealmUnavailableException { + throw log.noAuthenticationInProgress(); + } + + boolean authorize(Principal authorizationId, final boolean authorizeRunAs) throws RealmUnavailableException { + throw log.noAuthenticationInProgress(); + } + + void setMechanismRealmName(String name) { + throw log.noAuthenticationInProgress(); + } + + void updateCredential(Credential credential) throws RealmUnavailableException { + throw log.noAuthenticationInProgress(); + } + + void succeed() { + throw log.noAuthenticationInProgress(); + } + + void fail(final boolean requireInProgress) { + if (requireInProgress) throw log.noAuthenticationInProgress(); + } + + boolean isDone() { + return false; + } + + void addPublicCredential(final Credential credential) { + throw log.noAuthenticationInProgress(); + } + + void addPrivateCredential(final Credential credential) { + throw log.noAuthenticationInProgress(); + } + + void addRuntimeAttributes(final Attributes runtimeAttributes) { + throw log.noAuthenticationInProgress(); + } + + /** + * Indicate whether or not current state is {@link NameAssignedState}. + * + * @return {@code true} if state is {@link NameAssignedState}. Otherwise, {@code false}. + */ + public boolean isNameAssigned() { + return this instanceof NameAssignedState; + } + + /** + * Indicate whether or not current state is {@link AuthorizedState}. + * + * @return {@code true} if state is {@link AuthorizedState}. Otherwise, {@code false}. + */ + public boolean isAuthorized() { + return this instanceof AuthorizedState; + } + + /** + * Indicate whether or not evidence verification is allowed. + * + * @return {@code true} if evidence verification can be performed. Otherwise, {@code false}. + */ + public boolean canVerifyEvidence() { + return !(this instanceof NameAssignedState || this instanceof AuthorizedState); + } + } + + final class InactiveState extends State { + + private final SecurityIdentity capturedIdentity; + private final MechanismConfigurationSelector mechanismConfigurationSelector; + private final MechanismInformation mechanismInformation; + private final IdentityCredentials privateCredentials; + private final IdentityCredentials publicCredentials; + private final Attributes runtimeAttributes; + + public InactiveState(SecurityIdentity capturedIdentity, MechanismConfigurationSelector mechanismConfigurationSelector, IdentityCredentials privateCredentials, IdentityCredentials publicCredentials, Attributes runtimeAttributes) { + this(capturedIdentity, mechanismConfigurationSelector, MechanismInformation.DEFAULT, privateCredentials, publicCredentials, runtimeAttributes); + } + + public InactiveState(SecurityIdentity capturedIdentity, MechanismConfigurationSelector mechanismConfigurationSelector, + MechanismInformation mechanismInformation, IdentityCredentials privateCredentials, IdentityCredentials publicCredentials, Attributes runtimeAttributes) { + this.capturedIdentity = capturedIdentity; + this.mechanismConfigurationSelector = mechanismConfigurationSelector; + this.mechanismInformation = checkNotNullParam("mechanismInformation", mechanismInformation); + this.privateCredentials = privateCredentials; + this.publicCredentials = publicCredentials; + this.runtimeAttributes = runtimeAttributes; + } + + @Override + void setMechanismInformation(MechanismInformation mechanismInformation) { + InactiveState inactiveState = new InactiveState(capturedIdentity, mechanismConfigurationSelector, mechanismInformation, privateCredentials, publicCredentials, runtimeAttributes); + InitialState nextState = inactiveState.selectMechanismConfiguration(); + if (! stateRef.compareAndSet(this, nextState)) { + stateRef.get().setMechanismInformation(mechanismInformation); + } + } + + @Override + SecurityDomain getSecurityDomain() { + return capturedIdentity.getSecurityDomain(); + } + + boolean authorize(Principal authorizationId, boolean authorizeRunAs) throws RealmUnavailableException { + transition(); + return stateRef.get().authorize(authorizationId, authorizeRunAs); + } + + @Override + void setMechanismRealmName(String name) { + transition(); + stateRef.get().setMechanismRealmName(name); + } + + + @Override + MechanismRealmConfiguration getMechanismRealmConfiguration() { + transition(); + return stateRef.get().getMechanismRealmConfiguration(); + } + + @Override + void fail(final boolean requireInProgress) { + transition(); + stateRef.get().fail(requireInProgress); + } + + @Override + boolean authorizeAnonymous(boolean requireLoginPermission) { + transition(); + return stateRef.get().authorizeAnonymous(requireLoginPermission); + } + + @Override + boolean authorize(boolean requireLoginPermission) throws RealmUnavailableException { + transition(); + return stateRef.get().authorize(requireLoginPermission); + } + + @Override + boolean importIdentity(SecurityIdentity identity) throws RealmUnavailableException { + transition(); + return stateRef.get().importIdentity(identity); + } + + @Override + SupportLevel getEvidenceVerifySupport(final Class evidenceType, final String algorithmName) throws RealmUnavailableException { + return getSecurityDomain().getEvidenceVerifySupport(evidenceType, algorithmName); + } + + @Override + boolean verifyEvidence(Evidence evidence) throws RealmUnavailableException { + transition(); + return stateRef.get().verifyEvidence(evidence); + } + + @Override + void setPrincipal(Principal principal, boolean exclusive) throws RealmUnavailableException { + transition(); + stateRef.get().setPrincipal(principal, exclusive); + } + + @Override + MechanismConfiguration getMechanismConfiguration() { + transition(); + return stateRef.get().getMechanismConfiguration(); + } + + @Override + void addPublicCredential(final Credential credential) { + final InactiveState newState = new InactiveState(capturedIdentity, mechanismConfigurationSelector, mechanismInformation, privateCredentials, publicCredentials.withCredential(credential), runtimeAttributes); + if (! stateRef.compareAndSet(this, newState)) { + stateRef.get().addPublicCredential(credential); + } + } + + @Override + void addPrivateCredential(final Credential credential) { + final InactiveState newState = new InactiveState(capturedIdentity, mechanismConfigurationSelector, mechanismInformation, privateCredentials.withCredential(credential), publicCredentials, runtimeAttributes); + if (! stateRef.compareAndSet(this, newState)) { + stateRef.get().addPrivateCredential(credential); + } + } + + @Override + void addRuntimeAttributes(final Attributes runtimeAttributes) { + final InactiveState newState = new InactiveState(capturedIdentity, mechanismConfigurationSelector, mechanismInformation, privateCredentials, publicCredentials, AggregateAttributes.aggregateOf(this.runtimeAttributes, runtimeAttributes)); + if (! stateRef.compareAndSet(this, newState)) { + stateRef.get().addRuntimeAttributes(runtimeAttributes); + } + } + + private void transition() { + InitialState initialState = selectMechanismConfiguration(); + stateRef.compareAndSet(this, initialState); + } + + private InitialState selectMechanismConfiguration() { + MechanismConfiguration mechanismConfiguration = mechanismConfigurationSelector.selectConfiguration(mechanismInformation); + if (mechanismConfiguration == null) { + throw log.unableToSelectMechanismConfiguration(mechanismInformation.getMechanismType(), + mechanismInformation.getMechanismName(), mechanismInformation.getHostName(), + mechanismInformation.getProtocol()); + } + return new InitialState(capturedIdentity, mechanismConfiguration, mechanismConfigurationSelector, privateCredentials, publicCredentials, runtimeAttributes); + } + + } + + abstract class ActiveState extends State { + + ActiveState() { + } + + boolean authorize(Principal authorizationId, boolean authorizeRunAs) throws RealmUnavailableException { + final AtomicReference stateRef = getStateRef(); + + // get the identity we are authorizing from + final SecurityIdentity sourceIdentity = getSourceIdentity(); + + final State state = assignName(sourceIdentity, getMechanismConfiguration(), getMechanismRealmConfiguration(), authorizationId, null, IdentityCredentials.NONE, IdentityCredentials.NONE, Attributes.EMPTY); + if (!state.isNameAssigned()) { + ElytronMessages.log.tracef("Authorization failed - unable to assign identity name"); + return false; + } + + final NameAssignedState nameAssignedState = (NameAssignedState) state; + final RealmIdentity realmIdentity = nameAssignedState.getRealmIdentity(); + boolean ok = false; + try { + if (! realmIdentity.exists()) { + ElytronMessages.log.tracef("Authorization failed - identity does not exist"); + return false; + } + // check the run-as permission on the old identity + if (authorizeRunAs && ! sourceIdentity.implies(new RunAsPrincipalPermission(nameAssignedState.getAuthenticationPrincipal().getName()))) { + ElytronMessages.log.tracef("Authorization failed - source identity does not have RunAsPrincipalPermission"); + return false; + } + final AuthorizedAuthenticationState newState = nameAssignedState.doAuthorization(false); + if (newState == null) { + return false; + } + if (! stateRef.compareAndSet(this, newState)) { + // try again + return stateRef.get().authorize(authorizationId, authorizeRunAs); + } + ok = true; + return true; + } finally { + if (! ok) realmIdentity.dispose(); + } + } + + @Override + void setMechanismRealmName(final String realmName) { + final MechanismRealmConfiguration currentConfiguration = getMechanismRealmConfiguration(); + final MechanismConfiguration mechanismConfiguration = getMechanismConfiguration(); + if (mechanismConfiguration.getMechanismRealmNames().isEmpty()) { + // no realms are configured + throw log.invalidMechRealmSelection(realmName); + } + final MechanismRealmConfiguration configuration = mechanismConfiguration.getMechanismRealmConfiguration(realmName); + if (configuration == null) { + throw log.invalidMechRealmSelection(realmName); + } + if (currentConfiguration != configuration) { + throw log.mechRealmAlreadySelected(); + } + } + + @Override + void setMechanismInformation(MechanismInformation mechanismInformation) { + throw log.tooLateToSetMechanismInformation(); + } + + abstract SecurityIdentity getSourceIdentity(); + } + + /** + * State shared among both the initial state and the realm-assigned state, where no authentication name is yet set. + */ + abstract class UnassignedState extends ActiveState { + final SecurityIdentity capturedIdentity; + final MechanismConfiguration mechanismConfiguration; + final IdentityCredentials privateCredentials; + final IdentityCredentials publicCredentials; + final Attributes runtimeAttributes; + + UnassignedState(final SecurityIdentity capturedIdentity, final MechanismConfiguration mechanismConfiguration, final IdentityCredentials privateCredentials, final IdentityCredentials publicCredentials, final Attributes runtimeAttributes) { + this.capturedIdentity = capturedIdentity; + this.mechanismConfiguration = mechanismConfiguration; + this.privateCredentials = privateCredentials; + this.publicCredentials = publicCredentials; + this.runtimeAttributes = runtimeAttributes; + } + + SecurityIdentity getSourceIdentity() { + return capturedIdentity; + } + + @Override + SecurityDomain getSecurityDomain() { + return capturedIdentity.getSecurityDomain(); + } + + @Override + void fail(final boolean requireInProgress) { + final AtomicReference stateRef = getStateRef(); + if (! stateRef.compareAndSet(this, FAILED)) { + // recurse & retry + stateRef.get().fail(requireInProgress); + } + } + + @Override + boolean authorizeAnonymous(final boolean requireLoginPermission) { + final AtomicReference stateRef = getStateRef(); + final SecurityIdentity anonymousIdentity = getSecurityDomain().getAnonymousSecurityIdentity(); + return (! requireLoginPermission || anonymousIdentity.implies(LoginPermission.getInstance())) && (stateRef.compareAndSet(this, new AnonymousAuthorizedState(anonymousIdentity)) || stateRef.get().authorizeAnonymous(requireLoginPermission)); + } + + @Override + boolean authorize(final boolean requireLoginPermission) throws RealmUnavailableException { + final SecurityIdentity capturedIdentity = this.capturedIdentity; + if (capturedIdentity.isAnonymous()) { + return authorizeAnonymous(requireLoginPermission); + } + final AtomicReference stateRef = getStateRef(); + return (! requireLoginPermission || capturedIdentity.implies(LoginPermission.getInstance())) && (stateRef.compareAndSet(this, new AuthorizedState(capturedIdentity, capturedIdentity.getPrincipal(), capturedIdentity.getRealmInfo(), mechanismConfiguration, getMechanismRealmConfiguration())) || stateRef.get().authorize(requireLoginPermission)); + } + + @Override + boolean importIdentity(final SecurityIdentity importedIdentity) throws RealmUnavailableException { + // As long as a name is not yet assigned, we can authorize an imported identity + final RealmInfo evidenceRealmInfo = importedIdentity.getRealmInfo(); + final SecurityRealm evidenceSecurityRealm = evidenceRealmInfo.getSecurityRealm(); + final SecurityDomain evidenceSecurityDomain = importedIdentity.getSecurityDomain(); + final AtomicReference stateRef = getStateRef(); + final SecurityIdentity sourceIdentity = getSourceIdentity(); + final SecurityDomain domain = sourceIdentity.getSecurityDomain(); + // Check that the given security identity evidence either corresponds to the same realm that created the + // current authentication identity or it corresponds to a domain that is trusted by the current domain + if (importedIdentity.isAnonymous()) { + AnonymousAuthorizedState newState = new AnonymousAuthorizedState(domain.getAnonymousSecurityIdentity()); + return stateRef.compareAndSet(this, newState) || stateRef.get().importIdentity(importedIdentity); + } + final Principal importedPrincipal = importedIdentity.getPrincipal(); + if (domain == importedIdentity.getSecurityDomain()) { + // it's authorized already because it's the same domain + AuthorizedState newState = new AuthorizedState(importedIdentity, importedPrincipal, importedIdentity.getRealmInfo(), mechanismConfiguration, getMechanismRealmConfiguration()); + return stateRef.compareAndSet(this, newState) || stateRef.get().importIdentity(importedIdentity); + } + + boolean trusted = false; + // it didn't come from our domain. Check to see if it came from a trusted domain. + if (domain.trustsDomain(evidenceSecurityDomain)) { + trusted = true; + } + + // Finally, run the identity through the normal name selection process. + final State state = assignName(sourceIdentity, mechanismConfiguration, getMechanismRealmConfiguration(), importedPrincipal, null, privateCredentials, publicCredentials, runtimeAttributes); + if (!state.isNameAssigned()) { + return false; + } + final NameAssignedState nameState = (NameAssignedState) state; + final RealmIdentity realmIdentity = nameState.getRealmIdentity(); + boolean ok = false; + try { + if (! trusted) { + if (nameState.getRealmInfo().getSecurityRealm() != evidenceSecurityRealm) { + // mapped realm does not correspond with the imported realm name + return false; + } + } + + // with the name we have now, try and authorize + final AuthorizedAuthenticationState authzState = nameState.doAuthorization(false); + if (authzState == null) { + return false; + } + + if (! stateRef.compareAndSet(this, authzState)) { + return stateRef.get().importIdentity(importedIdentity); + } + + ok = true; + return true; + } finally { + if (! ok) realmIdentity.dispose(); + } + } + + @Override + SupportLevel getEvidenceVerifySupport(final Class evidenceType, final String algorithmName) throws RealmUnavailableException { + return getSecurityDomain().getEvidenceVerifySupport(evidenceType, algorithmName); + } + + @Override + boolean verifyEvidence(final Evidence evidence) throws RealmUnavailableException { + // TODO: this method probably should never cause a state change... consider setEvidence or something instead? + final AtomicReference stateRef = getStateRef(); + setDecodedEvidencePrincipal(evidence); + Principal evidencePrincipal = evidence.getDecodedPrincipal(); + log.tracef("Evidence verification: evidence = %s evidencePrincipal = %s", evidence, evidencePrincipal); + final MechanismRealmConfiguration mechanismRealmConfiguration = getMechanismRealmConfiguration(); + if (evidencePrincipal != null) { + final State newState = assignName(getSourceIdentity(), mechanismConfiguration, mechanismRealmConfiguration, evidencePrincipal, evidence, privateCredentials, publicCredentials, runtimeAttributes); + if (! newState.verifyEvidence(evidence)) { + if (newState.isNameAssigned()) { + ((NameAssignedState)newState).realmIdentity.dispose(); + } + return false; + } + if (! stateRef.compareAndSet(this, newState)) { + if (newState.isNameAssigned()) { + ((NameAssignedState)newState).realmIdentity.dispose(); + } + return stateRef.get().verifyEvidence(evidence); + } + return true; + } + Class evidenceType = evidence.getClass(); + String algorithm = evidence instanceof AlgorithmEvidence ? ((AlgorithmEvidence) evidence).getAlgorithm() : null; + + // verify evidence with no name set: use the realms to find a match (SSO scenario, etc.) + final SecurityDomain domain = getSecurityDomain(); + final Collection realmInfos = domain.getRealmInfos(); + RealmIdentity realmIdentity = null; + RealmInfo realmInfo = null; + for (RealmInfo info : realmInfos) { + try { + realmIdentity = info.getSecurityRealm().getRealmIdentity(evidence); + if (realmIdentity.getEvidenceVerifySupport(evidenceType, algorithm).mayBeSupported()) { + realmInfo = info; + break; + } else { + realmIdentity.dispose(); + } + } catch (RealmUnavailableException e) { + SecurityDomain.safeHandleSecurityEvent(domain, new SecurityRealmUnavailableEvent(domain.getCurrentSecurityIdentity(), info.getName())); + throw e; + } + } + if (realmInfo == null) { + // no verification possible, no identity found + return false; + } + if (! realmIdentity.verifyEvidence(evidence)) { + realmIdentity.dispose(); + return false; + } + final Principal resolvedPrincipal = realmIdentity.getRealmIdentityPrincipal(); + if (resolvedPrincipal == null) { + // we have to have a principal + realmIdentity.dispose(); + return false; + } + final NameAssignedState newState = new NameAssignedState(getSourceIdentity(), realmInfo, realmIdentity, resolvedPrincipal, mechanismConfiguration, mechanismRealmConfiguration, privateCredentials, publicCredentials, runtimeAttributes); + if (! stateRef.compareAndSet(this, newState)) { + realmIdentity.dispose(); + return stateRef.get().verifyEvidence(evidence); + } + return true; + } + + @Override + void setPrincipal(final Principal principal, final boolean exclusive) throws RealmUnavailableException { + Assert.checkNotNullParam("principal", principal); + final AtomicReference stateRef = getStateRef(); + final State newState = assignName(capturedIdentity, mechanismConfiguration, getMechanismRealmConfiguration(), principal, null, privateCredentials, publicCredentials, exclusive, runtimeAttributes); + if (! stateRef.compareAndSet(this, newState)) { + if (newState.isNameAssigned()) { + ((NameAssignedState)newState).realmIdentity.dispose(); + } + stateRef.get().setPrincipal(principal, exclusive); + } + } + + @Override + MechanismConfiguration getMechanismConfiguration() { + return mechanismConfiguration; + } + + IdentityCredentials getPrivateCredentials() { + return privateCredentials; + } + + IdentityCredentials getPublicCredentials() { + return publicCredentials; + } + + Attributes getRuntimeAttributes() { + return runtimeAttributes; + } + } + + final class InitialState extends UnassignedState { + + private final MechanismConfigurationSelector mechanismConfigurationSelector; + + InitialState(final SecurityIdentity capturedIdentity, final MechanismConfiguration mechanismConfiguration, final MechanismConfigurationSelector mechanismConfigurationSelector, final IdentityCredentials privateCredentials, final IdentityCredentials publicCredentials, final Attributes runtimeAttributes) { + super(capturedIdentity, mechanismConfiguration, privateCredentials, publicCredentials, runtimeAttributes); + this.mechanismConfigurationSelector = mechanismConfigurationSelector; + } + + @Override + void setMechanismRealmName(final String realmName) { + final MechanismConfiguration mechanismConfiguration = getMechanismConfiguration(); + if (mechanismConfiguration.getMechanismRealmNames().isEmpty()) { + // no realms are configured + throw log.invalidMechRealmSelection(realmName); + } + final MechanismRealmConfiguration configuration = mechanismConfiguration.getMechanismRealmConfiguration(realmName); + if (configuration == null) { + throw log.invalidMechRealmSelection(realmName); + } + final AtomicReference stateRef = getStateRef(); + if (! stateRef.compareAndSet(this, new RealmAssignedState(capturedIdentity, mechanismConfiguration, configuration, privateCredentials, publicCredentials, runtimeAttributes))) { + stateRef.get().setMechanismRealmName(realmName); + } + } + + @Override + MechanismRealmConfiguration getMechanismRealmConfiguration() { + final Collection mechanismRealmNames = mechanismConfiguration.getMechanismRealmNames(); + final Iterator iterator = mechanismRealmNames.iterator(); + if (iterator.hasNext()) { + // use the default realm + return mechanismConfiguration.getMechanismRealmConfiguration(iterator.next()); + } else { + return MechanismRealmConfiguration.NO_REALM; + } + } + + @Override + void setMechanismInformation(MechanismInformation mechanismInformation) { + InactiveState inactiveState = new InactiveState(capturedIdentity, mechanismConfigurationSelector, mechanismInformation, privateCredentials, publicCredentials, runtimeAttributes); + InitialState newState = inactiveState.selectMechanismConfiguration(); + if (! stateRef.compareAndSet(this, newState)) { + stateRef.get().setMechanismInformation(mechanismInformation); + } + } + + void addPublicCredential(final Credential credential) { + final InitialState newState = new InitialState(getSourceIdentity(), getMechanismConfiguration(), mechanismConfigurationSelector, getPrivateCredentials(), getPublicCredentials().withCredential(credential), runtimeAttributes); + if (! stateRef.compareAndSet(this, newState)) { + stateRef.get().addPublicCredential(credential); + } + } + + @Override + void addPrivateCredential(final Credential credential) { + final InitialState newState = new InitialState(getSourceIdentity(), getMechanismConfiguration(), mechanismConfigurationSelector, getPrivateCredentials().withCredential(credential), getPublicCredentials(), runtimeAttributes); + if (! stateRef.compareAndSet(this, newState)) { + stateRef.get().addPublicCredential(credential); + } + } + + void addRuntimeAttributes(final Attributes runtimeAttributes) { + final InitialState newState = new InitialState(getSourceIdentity(), getMechanismConfiguration(), mechanismConfigurationSelector, getPrivateCredentials(), getPublicCredentials(), AggregateAttributes.aggregateOf(getRuntimeAttributes(), runtimeAttributes)); + if (! stateRef.compareAndSet(this, newState)) { + stateRef.get().addRuntimeAttributes(runtimeAttributes); + } + } + } + + final class RealmAssignedState extends UnassignedState { + final MechanismRealmConfiguration mechanismRealmConfiguration; + + RealmAssignedState(final SecurityIdentity capturedIdentity, final MechanismConfiguration mechanismConfiguration, final MechanismRealmConfiguration mechanismRealmConfiguration, final IdentityCredentials privateCredentials, final IdentityCredentials publicCredentials, final Attributes runtimeAttributes) { + super(capturedIdentity, mechanismConfiguration, privateCredentials, publicCredentials, runtimeAttributes); + this.mechanismRealmConfiguration = mechanismRealmConfiguration; + } + + @Override + MechanismRealmConfiguration getMechanismRealmConfiguration() { + return mechanismRealmConfiguration; + } + + @Override + void addPublicCredential(final Credential credential) { + final RealmAssignedState newState = new RealmAssignedState(getSourceIdentity(), getMechanismConfiguration(), getMechanismRealmConfiguration(), getPrivateCredentials(), getPublicCredentials().withCredential(credential), runtimeAttributes); + if (! stateRef.compareAndSet(this, newState)) { + stateRef.get().addPublicCredential(credential); + } + } + + @Override + void addPrivateCredential(final Credential credential) { + final RealmAssignedState newState = new RealmAssignedState(getSourceIdentity(), getMechanismConfiguration(), getMechanismRealmConfiguration(), getPrivateCredentials().withCredential(credential), getPublicCredentials(), runtimeAttributes); + if (! stateRef.compareAndSet(this, newState)) { + stateRef.get().addPublicCredential(credential); + } + } + + @Override + void addRuntimeAttributes(final Attributes runtimeAttributes) { + final RealmAssignedState newState = new RealmAssignedState(getSourceIdentity(), getMechanismConfiguration(), getMechanismRealmConfiguration(), getPrivateCredentials(), getPublicCredentials(), AggregateAttributes.aggregateOf(getRuntimeAttributes(), runtimeAttributes)); + if (! stateRef.compareAndSet(this, newState)) { + stateRef.get().addRuntimeAttributes(runtimeAttributes); + } + } + } + + final class InvalidNameState extends UnassignedState { + + final MechanismRealmConfiguration mechanismRealmConfiguration; + + InvalidNameState(final SecurityIdentity capturedIdentity, final MechanismConfiguration mechanismConfiguration, final MechanismRealmConfiguration mechanismRealmConfiguration, final IdentityCredentials privateCredentials, final IdentityCredentials publicCredentials, final Attributes runtimeAttributes) { + super(capturedIdentity, mechanismConfiguration, privateCredentials, publicCredentials, runtimeAttributes); + this.mechanismRealmConfiguration = mechanismRealmConfiguration; + } + + @Override + MechanismRealmConfiguration getMechanismRealmConfiguration() { + return mechanismRealmConfiguration; + } + + @Override + RealmIdentity getRealmIdentity() { + return RealmIdentity.NON_EXISTENT; + } + + @Override + void fail(final boolean requireInProgress) { + final AtomicReference stateRef = getStateRef(); + if (! stateRef.compareAndSet(this, FAILED)) { + // recurse & retry + stateRef.get().fail(requireInProgress); + } + } + + @Override + boolean isDone() { + return true; + } + } + + final class NameAssignedState extends ActiveState { + private final SecurityIdentity capturedIdentity; + private final RealmInfo realmInfo; + private final RealmIdentity realmIdentity; + private final Principal authenticationPrincipal; + private final MechanismConfiguration mechanismConfiguration; + private final MechanismRealmConfiguration mechanismRealmConfiguration; + private final IdentityCredentials privateCredentials; + private final IdentityCredentials publicCredentials; + private final Attributes runtimeAttributes; + + NameAssignedState(final SecurityIdentity capturedIdentity, final RealmInfo realmInfo, final RealmIdentity realmIdentity, final Principal authenticationPrincipal, final MechanismConfiguration mechanismConfiguration, final MechanismRealmConfiguration mechanismRealmConfiguration, final IdentityCredentials privateCredentials, final IdentityCredentials publicCredentials, final Attributes runtimeAttributes) { + this.capturedIdentity = capturedIdentity; + this.realmInfo = realmInfo; + this.realmIdentity = realmIdentity; + this.authenticationPrincipal = authenticationPrincipal; + this.mechanismConfiguration = mechanismConfiguration; + this.mechanismRealmConfiguration = mechanismRealmConfiguration; + this.privateCredentials = privateCredentials; + this.publicCredentials = publicCredentials; + this.runtimeAttributes = runtimeAttributes; + } + + @Override + MechanismConfiguration getMechanismConfiguration() { + return mechanismConfiguration; + } + + @Override + MechanismRealmConfiguration getMechanismRealmConfiguration() { + return mechanismRealmConfiguration; + } + + @Override + Principal getAuthenticationPrincipal() { + return authenticationPrincipal; + } + + @Override + RealmIdentity getRealmIdentity() { + return realmIdentity; + } + + @Override + SecurityDomain getSecurityDomain() { + return capturedIdentity.getSecurityDomain(); + } + + @Override + SupportLevel getCredentialAcquireSupport(final Class credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException { + return realmIdentity.getCredentialAcquireSupport(credentialType, algorithmName, parameterSpec); + } + + @Override + SupportLevel getEvidenceVerifySupport(final Class evidenceType, final String algorithmName) throws RealmUnavailableException { + return realmIdentity.getEvidenceVerifySupport(evidenceType, algorithmName); + } + + @Override + C getCredential(final Class credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException { + return realmIdentity.getCredential(credentialType, algorithmName, parameterSpec); + } + + @Override + boolean authorize(final boolean requireLoginPermission) throws RealmUnavailableException { + AuthorizedAuthenticationState newState = doAuthorization(requireLoginPermission); + if (newState == null) { + return false; + } + final AtomicReference stateRef = getStateRef(); + // retry if necessary + return stateRef.compareAndSet(this, newState) || stateRef.get().authorize(requireLoginPermission); + } + + AuthorizedAuthenticationState doAuthorization(final boolean requireLoginPermission) throws RealmUnavailableException { + final RealmIdentity realmIdentity = this.realmIdentity; + + if (! realmIdentity.exists()) { + ElytronMessages.log.trace("Authorization failed - realm identity does not exists"); + return null; + } + + final RealmInfo realmInfo = this.realmInfo; + final Principal authenticationPrincipal = this.authenticationPrincipal; + final AuthorizationIdentity authorizationIdentity = runtimeAttributes == Attributes.EMPTY ? realmIdentity.getAuthorizationIdentity() + : AuthorizationIdentity.basicIdentity(realmIdentity.getAuthorizationIdentity(), runtimeAttributes); + final SecurityDomain domain = capturedIdentity.getSecurityDomain(); + + SecurityIdentity authorizedIdentity = Assert.assertNotNull(domain.transform(new SecurityIdentity(domain, authenticationPrincipal, realmInfo, authorizationIdentity, domain.getCategoryRoleMappers(), IdentityCredentials.NONE, IdentityCredentials.NONE))); + authorizedIdentity = authorizedIdentity.withPublicCredentials(publicCredentials).withPrivateCredentials(privateCredentials); + + if (log.isTraceEnabled()) { + log.tracef("Authorizing principal %s.", authenticationPrincipal.getName()); + if (authorizationIdentity != null) { + log.tracef("Authorizing against the following attributes: %s => %s", + authorizationIdentity.getAttributes().keySet(), authorizationIdentity.getAttributes().values()); + log.tracef("Authorizing against the following runtime attributes: %s => %s", + authorizationIdentity.getRuntimeAttributes().keySet(), authorizationIdentity.getRuntimeAttributes().values()); + } else { + log.tracef("Authorizing against the following attributes: Cannot obtain the attributes. Authorization Identity is null."); + } + } + if (requireLoginPermission) { + if (! authorizedIdentity.implies(LoginPermission.getInstance())) { + SecurityRealm.safeHandleRealmEvent(realmInfo.getSecurityRealm(), new RealmIdentityFailedAuthorizationEvent(authorizedIdentity.getAuthorizationIdentity(), authorizedIdentity.getPrincipal(), authenticationPrincipal)); + ElytronMessages.log.trace("Authorization failed - identity does not have required LoginPermission"); + return null; + } else { + SecurityRealm.safeHandleRealmEvent(realmInfo.getSecurityRealm(), new RealmIdentitySuccessfulAuthorizationEvent(authorizedIdentity.getAuthorizationIdentity(), authorizedIdentity.getPrincipal(), authenticationPrincipal)); + } + } + ElytronMessages.log.trace("Authorization succeed"); + return new AuthorizedAuthenticationState(authorizedIdentity, authenticationPrincipal, realmInfo, realmIdentity, mechanismRealmConfiguration, mechanismConfiguration); + } + + @Override + boolean authorize(final Principal authorizationId, final boolean authorizeRunAs) throws RealmUnavailableException { + final AuthorizedAuthenticationState authzState = doAuthorization(true); + if (authzState == null) { + return false; + } + final AuthorizedState newState = authzState.authorizeRunAs(authorizationId, authorizeRunAs); + if (newState == null) { + return false; + } + final AtomicReference stateRef = getStateRef(); + if (! stateRef.compareAndSet(this, newState)) { + return stateRef.get().authorize(authorizationId, authorizeRunAs); + } + if (newState != authzState) getRealmIdentity().dispose(); + return true; + } + + @Override + SecurityIdentity getSourceIdentity() { + return capturedIdentity; + } + + @Override + boolean verifyEvidence(final Evidence evidence) throws RealmUnavailableException { + // At this stage, we just verify that the evidence principal matches, and verify it with the realm. + final Principal evidencePrincipal = evidence.getDecodedPrincipal(); + return (evidencePrincipal == null || isSamePrincipal(evidencePrincipal)) && getRealmIdentity().verifyEvidence(evidence); + } + + @Override + void updateCredential(Credential credential) throws RealmUnavailableException { + realmIdentity.updateCredential(credential); + } + + @Override + void succeed() { + throw log.cannotSucceedNotAuthorized(); + } + + @Override + void fail(final boolean requireInProgress) { + final SecurityIdentity capturedIdentity = getSourceIdentity(); + final AtomicReference stateRef = getStateRef(); + if (!stateRef.compareAndSet(this, FAILED)) { + stateRef.get().fail(requireInProgress); + return; + } + SecurityRealm.safeHandleRealmEvent(getRealmInfo().getSecurityRealm(), new RealmFailedAuthenticationEvent(getRealmIdentityWithRuntimeAttributes(), null, null)); + SecurityDomain.safeHandleSecurityEvent(capturedIdentity.getSecurityDomain(), new SecurityAuthenticationFailedEvent(capturedIdentity, realmIdentity.getRealmIdentityPrincipal())); + realmIdentity.dispose(); + } + + private RealmIdentity getRealmIdentityWithRuntimeAttributes() { + return new RealmIdentity() { + @Override + public Principal getRealmIdentityPrincipal() { + return realmIdentity.getRealmIdentityPrincipal(); + } + + @Override + public SupportLevel getCredentialAcquireSupport(Class credentialType, String algorithmName, AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException { + return realmIdentity.getCredentialAcquireSupport(credentialType, algorithmName, parameterSpec); + } + + @Override + public C getCredential(Class credentialType) throws RealmUnavailableException { + return realmIdentity.getCredential(credentialType); + } + + @Override + public SupportLevel getEvidenceVerifySupport(Class evidenceType, String algorithmName) throws RealmUnavailableException { + return realmIdentity.getEvidenceVerifySupport(evidenceType, algorithmName); + } + + @Override + public boolean verifyEvidence(Evidence evidence) throws RealmUnavailableException { + return realmIdentity.verifyEvidence(evidence); + } + + @Override + public boolean exists() throws RealmUnavailableException { + return realmIdentity.exists(); + } + + @Override + public AuthorizationIdentity getAuthorizationIdentity() throws RealmUnavailableException { + if (realmIdentity.exists()) { + return AuthorizationIdentity.basicIdentity(realmIdentity.getAuthorizationIdentity(), runtimeAttributes); + } else { + return AuthorizationIdentity.basicIdentity(AuthorizationIdentity.EMPTY, runtimeAttributes); + } + } + }; + } + + @Override + void setPrincipal(final Principal principal, final boolean exclusive) { + if (isSamePrincipal(principal)) { + return; + } + throw log.nameAlreadySet(); + } + + @Override + boolean isSamePrincipal(Principal principal) { + final SecurityDomain domain = capturedIdentity.getSecurityDomain(); + principal = rewriteAll(principal, mechanismRealmConfiguration.getPreRealmRewriter(), mechanismConfiguration.getPreRealmRewriter(), domain.getPreRealmRewriter()); + return authenticationPrincipal.equals(principal); + } + + @Override + void addPublicCredential(final Credential credential) { + final NameAssignedState newState = new NameAssignedState(getSourceIdentity(), getRealmInfo(), getRealmIdentity(), getAuthenticationPrincipal(), getMechanismConfiguration(), getMechanismRealmConfiguration(), privateCredentials, publicCredentials.withCredential(credential), runtimeAttributes); + if (! stateRef.compareAndSet(this, newState)) { + stateRef.get().addPublicCredential(credential); + } + } + + @Override + void addPrivateCredential(final Credential credential) { + final NameAssignedState newState = new NameAssignedState(getSourceIdentity(), getRealmInfo(), getRealmIdentity(), getAuthenticationPrincipal(), getMechanismConfiguration(), getMechanismRealmConfiguration(), privateCredentials.withCredential(credential), publicCredentials, runtimeAttributes); + if (! stateRef.compareAndSet(this, newState)) { + stateRef.get().addPublicCredential(credential); + } + } + + @Override + void addRuntimeAttributes(final Attributes runtimeAttributes) { + final NameAssignedState newState = new NameAssignedState(getSourceIdentity(), getRealmInfo(), getRealmIdentity(), getAuthenticationPrincipal(), getMechanismConfiguration(), getMechanismRealmConfiguration(), privateCredentials, publicCredentials, AggregateAttributes.aggregateOf(this.runtimeAttributes, runtimeAttributes)); + if (! stateRef.compareAndSet(this, newState)) { + stateRef.get().addRuntimeAttributes(runtimeAttributes); + } + } + + RealmInfo getRealmInfo() { + return realmInfo; + } + } + + final class AnonymousAuthorizedState extends ActiveState { + private final SecurityIdentity anonymousIdentity; + + AnonymousAuthorizedState(final SecurityIdentity anonymousIdentity) { + this.anonymousIdentity = anonymousIdentity; + } + + @Override + MechanismConfiguration getMechanismConfiguration() { + return MechanismConfiguration.EMPTY; + } + + @Override + MechanismRealmConfiguration getMechanismRealmConfiguration() { + return MechanismRealmConfiguration.NO_REALM; + } + + @Override + SecurityIdentity getAuthorizedIdentity() { + return anonymousIdentity; + } + + @Override + Principal getAuthenticationPrincipal() { + return AnonymousPrincipal.getInstance(); + } + + @Override + boolean isSamePrincipal(final Principal principal) { + return principal instanceof AnonymousPrincipal; + } + + @Override + SupportLevel getCredentialAcquireSupport(final Class credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException { + return SupportLevel.UNSUPPORTED; + } + + @Override + SupportLevel getEvidenceVerifySupport(final Class evidenceType, final String algorithmName) throws RealmUnavailableException { + return SupportLevel.UNSUPPORTED; + } + + @Override + C getCredential(final Class credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException { + return null; + } + + @Override + boolean verifyEvidence(final Evidence evidence) throws RealmUnavailableException { + return false; + } + + @Override + RealmIdentity getRealmIdentity() { + return RealmIdentity.ANONYMOUS; + } + + @Override + SecurityDomain getSecurityDomain() { + return anonymousIdentity.getSecurityDomain(); + } + + @Override + boolean authorizeAnonymous(final boolean requireLoginPermission) { + return true; + } + + @Override + void setPrincipal(final Principal principal, final boolean exclusive) throws RealmUnavailableException { + if (! (principal instanceof AnonymousPrincipal)) { + super.setPrincipal(principal, exclusive); + } + } + + @Override + boolean authorize(final boolean requireLoginPermission) throws RealmUnavailableException { + return ! requireLoginPermission || anonymousIdentity.implies(LoginPermission.getInstance()); + } + + @Override + void updateCredential(Credential credential) throws RealmUnavailableException { + // no-op + } + + @Override + void succeed() { + final AtomicReference stateRef = getStateRef(); + if (! stateRef.compareAndSet(this, new CompleteState(anonymousIdentity))) { + stateRef.get().succeed(); + } + } + + @Override + void fail(final boolean requireInProgress) { + final AtomicReference stateRef = getStateRef(); + if (! stateRef.compareAndSet(this, FAILED)) { + stateRef.get().fail(requireInProgress); + } + } + + @Override + SecurityIdentity getSourceIdentity() { + return anonymousIdentity; + } + } + + class AuthorizedState extends ActiveState { + private final SecurityIdentity authorizedIdentity; + private final Principal authenticationPrincipal; + private final RealmInfo realmInfo; + private final MechanismConfiguration mechanismConfiguration; + private final MechanismRealmConfiguration mechanismRealmConfiguration; + + AuthorizedState(final SecurityIdentity authorizedIdentity, final Principal authenticationPrincipal, final RealmInfo realmInfo, final MechanismConfiguration mechanismConfiguration, final MechanismRealmConfiguration mechanismRealmConfiguration) { + this.authorizedIdentity = authorizedIdentity; + this.authenticationPrincipal = authenticationPrincipal; + this.realmInfo = realmInfo; + this.mechanismConfiguration = mechanismConfiguration; + this.mechanismRealmConfiguration = mechanismRealmConfiguration; + } + + @Override + MechanismRealmConfiguration getMechanismRealmConfiguration() { + return mechanismRealmConfiguration; + } + + @Override + MechanismConfiguration getMechanismConfiguration() { + return mechanismConfiguration; + } + + @Override + SecurityIdentity getAuthorizedIdentity() { + return authorizedIdentity; + } + + @Override + Principal getAuthenticationPrincipal() { + return authenticationPrincipal; + } + + @Override + SecurityDomain getSecurityDomain() { + return authorizedIdentity.getSecurityDomain(); + } + + @Override + SecurityIdentity getSourceIdentity() { + return authorizedIdentity; + } + + @Override + boolean isSamePrincipal(Principal principal) { + final SecurityDomain domain = authorizedIdentity.getSecurityDomain(); + principal = rewriteAll(principal, mechanismRealmConfiguration.getPreRealmRewriter(), mechanismConfiguration.getPreRealmRewriter(), domain.getPreRealmRewriter()); + return authenticationPrincipal.equals(principal); + } + + RealmInfo getRealmInfo() { + return realmInfo; + } + + @Override + boolean authorize(final boolean requireLoginPermission) throws RealmUnavailableException { + return ! requireLoginPermission || authorizedIdentity.implies(LoginPermission.getInstance()); + } + + AuthorizedState authorizeRunAs(final Principal authorizationId, final boolean authorizeRunAs) throws RealmUnavailableException { + if (isSamePrincipal(authorizationId)) { + ElytronMessages.log.trace("RunAs authorization succeed - the same identity"); + return this; + } + final State state = assignName(authorizedIdentity, getMechanismConfiguration(), getMechanismRealmConfiguration(), authorizationId, null, IdentityCredentials.NONE, IdentityCredentials.NONE, Attributes.EMPTY); + if (!state.isNameAssigned()) { + ElytronMessages.log.tracef("RunAs authorization failed - unable to assign identity name"); + return null; + } + + final NameAssignedState nameAssignedState = (NameAssignedState) state; + final RealmIdentity realmIdentity = nameAssignedState.getRealmIdentity(); + boolean ok = false; + try { + String targetName = nameAssignedState.getAuthenticationPrincipal().getName(); + if (authorizeRunAs && ! authorizedIdentity.implies(new RunAsPrincipalPermission(targetName))) { + ElytronMessages.log.tracef("RunAs authorization failed - identity does not have required RunAsPrincipalPermission(%s)", targetName); + return null; + } + final AuthorizedAuthenticationState newState = nameAssignedState.doAuthorization(false); + if (newState == null) { + ElytronMessages.log.trace("RunAs authorization failed"); + return null; + } + ok = true; + ElytronMessages.log.trace("RunAs authorization succeed"); + return newState; + } finally { + if (! ok) realmIdentity.dispose(); + } + } + + @Override + void succeed() { + if (authorizedIdentity != null) { + return; + } + super.succeed(); + } + + void addPublicCredential(final Credential credential) { + final SecurityIdentity sourceIdentity = getSourceIdentity(); + final AuthorizedState newState = new AuthorizedState(sourceIdentity.withPublicCredential(credential), getAuthenticationPrincipal(), getRealmInfo(), getMechanismConfiguration(), getMechanismRealmConfiguration()); + if (! stateRef.compareAndSet(this, newState)) { + stateRef.get().addPublicCredential(credential); + } + } + + @Override + void addPrivateCredential(final Credential credential) { + final SecurityIdentity sourceIdentity = getSourceIdentity(); + final AuthorizedState newState = new AuthorizedState(sourceIdentity.withPrivateCredential(credential), getAuthenticationPrincipal(), getRealmInfo(), getMechanismConfiguration(), getMechanismRealmConfiguration()); + if (! stateRef.compareAndSet(this, newState)) { + stateRef.get().addPrivateCredential(credential); + } + } + + @Override + void addRuntimeAttributes(final Attributes runtimeAttributes) { + final SecurityIdentity sourceIdentity = getSourceIdentity(); + final AuthorizedState newState = new AuthorizedState(sourceIdentity.withRuntimeAttributes(runtimeAttributes), getAuthenticationPrincipal(), getRealmInfo(), getMechanismConfiguration(), getMechanismRealmConfiguration()); + if (! stateRef.compareAndSet(this, newState)) { + stateRef.get().addRuntimeAttributes(runtimeAttributes); + } + } + } + + final class AuthorizedAuthenticationState extends AuthorizedState { + private final RealmIdentity realmIdentity; + + AuthorizedAuthenticationState(final SecurityIdentity authorizedIdentity, final Principal authenticationPrincipal, final RealmInfo realmInfo, final RealmIdentity realmIdentity, final MechanismRealmConfiguration mechanismRealmConfiguration, final MechanismConfiguration mechanismConfiguration) { + super(authorizedIdentity, authenticationPrincipal, realmInfo, mechanismConfiguration, mechanismRealmConfiguration); + this.realmIdentity = realmIdentity; + } + + @Override + SupportLevel getCredentialAcquireSupport(final Class credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException { + return realmIdentity.getCredentialAcquireSupport(credentialType, algorithmName, parameterSpec); + } + + @Override + SupportLevel getEvidenceVerifySupport(final Class evidenceType, final String algorithmName) throws RealmUnavailableException { + return realmIdentity.getEvidenceVerifySupport(evidenceType, algorithmName); + } + + @Override + C getCredential(final Class credentialType, String algorithmName, final AlgorithmParameterSpec parameterSpec) throws RealmUnavailableException { + return realmIdentity.getCredential(credentialType, algorithmName, parameterSpec); + } + + @Override + boolean verifyEvidence(final Evidence evidence) throws RealmUnavailableException { + return realmIdentity.verifyEvidence(evidence); + } + + @Override + RealmIdentity getRealmIdentity() { + return realmIdentity; + } + + @Override + void updateCredential(Credential credential) throws RealmUnavailableException { + realmIdentity.updateCredential(credential); + } + + @Override + void succeed() { + final SecurityIdentity authorizedIdentity = getSourceIdentity(); + final AtomicReference stateRef = getStateRef(); + if (! stateRef.compareAndSet(this, new CompleteState(authorizedIdentity))) { + stateRef.get().succeed(); + return; + } + SecurityRealm.safeHandleRealmEvent(getRealmInfo().getSecurityRealm(), new RealmSuccessfulAuthenticationEvent(realmIdentity, authorizedIdentity.getAuthorizationIdentity(), null, null)); + SecurityDomain.safeHandleSecurityEvent(authorizedIdentity.getSecurityDomain(), new SecurityAuthenticationSuccessfulEvent(authorizedIdentity)); + realmIdentity.dispose(); + } + + @Override + void fail(final boolean requireInProgress) { + final SecurityIdentity authorizedIdentity = getSourceIdentity(); + final AtomicReference stateRef = getStateRef(); + if (! stateRef.compareAndSet(this, FAILED)) { + stateRef.get().fail(requireInProgress); + return; + } + SecurityRealm.safeHandleRealmEvent(getRealmInfo().getSecurityRealm(), new RealmFailedAuthenticationEvent(realmIdentity, null, null)); + SecurityDomain.safeHandleSecurityEvent(authorizedIdentity.getSecurityDomain(), new SecurityAuthenticationFailedEvent(authorizedIdentity, realmIdentity.getRealmIdentityPrincipal())); + realmIdentity.dispose(); + } + + @Override + void addPublicCredential(final Credential credential) { + final SecurityIdentity sourceIdentity = getSourceIdentity(); + final AuthorizedAuthenticationState newState = new AuthorizedAuthenticationState(sourceIdentity.withPublicCredential(credential), getAuthenticationPrincipal(), getRealmInfo(), getRealmIdentity(), getMechanismRealmConfiguration(), getMechanismConfiguration()); + if (! stateRef.compareAndSet(this, newState)) { + stateRef.get().addPublicCredential(credential); + } + } + + @Override + void addPrivateCredential(final Credential credential) { + final SecurityIdentity sourceIdentity = getSourceIdentity(); + final AuthorizedAuthenticationState newState = new AuthorizedAuthenticationState(sourceIdentity.withPrivateCredential(credential), getAuthenticationPrincipal(), getRealmInfo(), getRealmIdentity(), getMechanismRealmConfiguration(), getMechanismConfiguration()); + if (! stateRef.compareAndSet(this, newState)) { + stateRef.get().addPrivateCredential(credential); + } + } + + @Override + void addRuntimeAttributes(final Attributes runtimeAttributes) { + final SecurityIdentity sourceIdentity = getSourceIdentity(); + final AuthorizedAuthenticationState newState = new AuthorizedAuthenticationState(sourceIdentity.withRuntimeAttributes(runtimeAttributes), getAuthenticationPrincipal(), getRealmInfo(), getRealmIdentity(), getMechanismRealmConfiguration(), getMechanismConfiguration()); + if (! stateRef.compareAndSet(this, newState)) { + stateRef.get().addRuntimeAttributes(runtimeAttributes); + } + } + + } + + static final class CompleteState extends State { + private final SecurityIdentity identity; + + public CompleteState(final SecurityIdentity identity) { + this.identity = identity; + } + + @Override + SecurityIdentity getAuthorizedIdentity() { + return identity; + } + + @Override + boolean isDone() { + return true; + } + + void succeed() { + // always works + } + } + + private static final State FAILED = new State() { + @Override + void fail(final boolean requireInProgress) { + } + + @Override + boolean isDone() { + return true; + } + }; +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/server/_private/ElytronMessages.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/server/_private/ElytronMessages.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/server/_private/ElytronMessages.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,150 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2014 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.auth.server._private; + +import static org.jboss.logging.Logger.Level.ERROR; + +import java.io.IOException; +import java.security.GeneralSecurityException; +import java.security.Principal; + +import org.jboss.logging.BasicLogger; +import org.jboss.logging.Logger; +import org.jboss.logging.annotations.Cause; +import org.jboss.logging.annotations.LogMessage; +import org.jboss.logging.annotations.Message; +import org.jboss.logging.annotations.MessageLogger; +import org.jboss.logging.annotations.Param; + +import org.jboss.logging.annotations.ValidIdRange; +import org.jboss.logging.annotations.ValidIdRanges; +import org.wildfly.security.auth.server.RealmUnavailableException; +import org.wildfly.security.authz.AuthorizationFailureException; + +/** + * Log messages and exceptions for Elytron. + * + * @author David M. Lloyd + * @author Darran Lofthouse + */ +@MessageLogger(projectCode = "ELY", length = 5) +@ValidIdRanges({ + @ValidIdRange(min = 3, max = 3), + @ValidIdRange(min = 8, max = 8), + @ValidIdRange(min = 1000, max = 1156), + @ValidIdRange(min = 8510, max = 8511), + @ValidIdRange(min = 16000, max = 16999) +}) +public interface ElytronMessages extends BasicLogger { + + ElytronMessages log = Logger.getMessageLogger(ElytronMessages.class, "org.wildfly.security"); + + @Message(id = 3, value = "This builder has already been built") + IllegalStateException builderAlreadyBuilt(); + + @Message(id = 8, value = "The given credential is not supported here") + IllegalArgumentException credentialNotSupported(); + + @Message(id = 1000, value = "Authentication name was already set on this context") + IllegalStateException nameAlreadySet(); + + @Message(id = 1003, value = "No authentication is in progress") + IllegalStateException noAuthenticationInProgress(); + + @Message(id = 1005, value = "Realm map does not contain mapping for default realm '%s'") + IllegalArgumentException realmMapDoesNotContainDefault(String defaultRealm); + + @Message(id = 1019, value = "Unable to obtain exclusive access to backing identity") + RealmUnavailableException unableToObtainExclusiveAccess(); + + @Message(id = 1033, value = "User does not exist") + IllegalStateException userDoesNotExist(); + + @Message(id = 1034, value = "Invalid credential type specified") + IllegalStateException invalidCredentialTypeSpecified(); + + @Message(id = 1064, value = "Invalid identity name") + IllegalArgumentException invalidName(); + + @Message(id = 1088, value = "Attempting to run as \"%s\" authorization operation failed") + AuthorizationFailureException runAsAuthorizationFailed(@Param Principal principal, Principal targetPrincipal, + @Cause Throwable cause); + + @Message(id = 1092, value = "Invalid mechanism realm selection \"%s\"") + IllegalArgumentException invalidMechRealmSelection(String realmName); + + @Message(id = 1093, value = "Mechanism realm was already selected") + IllegalStateException mechRealmAlreadySelected(); + + @Message(id = 1095, value = "Unable to create identity") + RealmUnavailableException unableToCreateIdentity(); + + @Message(id = 1096, value = "No such identity") + RealmUnavailableException noSuchIdentity(); + + @Message(id = 1112, value = "Authentication cannot succeed; not authorized") + IllegalStateException cannotSucceedNotAuthorized(); + + @Message(id = 1119, value = "Unable to resolve MechanismConfiguration for mechanismType='%s', mechanismName='%s', hostName='%s', protocol='%s'.") + IllegalStateException unableToSelectMechanismConfiguration(String mechanismType, String mechanismName, + String hostName, String protocol); + + @Message(id = 1120, value = "Too late to set mechanism information as authentication has already begun.") + IllegalStateException tooLateToSetMechanismInformation(); + + @Message(id = 1124, value = "The security realm does not support updating a credential") + UnsupportedOperationException credentialUpdateNotSupportedByRealm(); + + @Message(id = 1148, value = "A SecurityDomain has already been associated with the specified ClassLoader") + IllegalStateException classLoaderSecurityDomainExists(); + + @Message(id = 1149, value = "Can not use SecurityIdentity with SecurityIdentity from same SecurityDomain") + IllegalArgumentException cantWithSameSecurityDomainDomain(); + + @Message(id = 1151, value = "Evidence Verification Failed.") + SecurityException authenticationFailedEvidenceVerification(); + + @Message(id = 1152, value = "Authorization Check Failed.") + SecurityException authenticationFailedAuthorization(); + + @Message(id = 1155, value = "Security domain mismatch") + IllegalArgumentException securityDomainMismatch(); + + @Message(id = 1156, value = "Cannot obtain a credential from a security factory") + IOException cannotObtainCredentialFromFactory(@Cause GeneralSecurityException e); + + @LogMessage(level = ERROR) + @Message(id = 1094, value = "An event handler threw an exception") + void eventHandlerFailed(@Cause Throwable cause); + + @Message(id = 8510, value = "Role mapper has already been initialized.") + IllegalStateException roleMappedAlreadyInitialized(); + + @Message(id = 8511, value = "Role mapper hasn't been initialized yet.") + IllegalStateException roleMappedNotInitialized(); + + @Message(id = 16000, value = "Invalid replacement in regex role mapper.") + IllegalArgumentException invalidReplacementInRegexRoleMapper(); + + @Message(id = 16001, value = "Invalid pattern in regex role mapper.") + IllegalArgumentException invalidPatternInRegexRoleMapper(); + + @Message(id = 16002, value = "Can not handle SecurityEvent with SecurityIdentity from other SecurityDomain") + IllegalArgumentException securityEventIdentityWrongDomain(); +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/server/_private/ElytronMessages_$logger.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/server/_private/ElytronMessages_$logger.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/server/_private/ElytronMessages_$logger.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,311 @@ +package org.wildfly.security.auth.server._private; + +import java.util.Locale; +import java.lang.IllegalStateException; +import org.wildfly.security.authz.AuthorizationFailureException; +import java.io.Serializable; +import javax.annotation.Generated; +import org.jboss.logging.DelegatingBasicLogger; +import java.security.GeneralSecurityException; +import org.wildfly.security.auth.server.RealmUnavailableException; +import java.lang.SecurityException; +import java.lang.String; +import java.io.IOException; +import org.jboss.logging.Logger; +import org.jboss.logging.BasicLogger; +import java.lang.Throwable; +import java.util.Arrays; +import java.lang.IllegalArgumentException; +import java.security.Principal; +import java.lang.UnsupportedOperationException; + + +import static org.jboss.logging.Logger.Level.ERROR; + +/** + * Warning this class consists of generated code. + */ +@Generated(value = "org.jboss.logging.processor.generator.model.MessageLoggerImplementor", date = "2024-01-15T21:44:24+0100") +public class ElytronMessages_$logger extends DelegatingBasicLogger implements ElytronMessages, BasicLogger, Serializable { + private static final long serialVersionUID = 1L; + private static final String FQCN = ElytronMessages_$logger.class.getName(); + public ElytronMessages_$logger(final Logger log) { + super(log); + } + private static final Locale LOCALE = Locale.ROOT; + protected Locale getLoggingLocale() { + return LOCALE; + } + protected String builderAlreadyBuilt$str() { + return "ELY00003: This builder has already been built"; + } + @Override + public final IllegalStateException builderAlreadyBuilt() { + final IllegalStateException result = new IllegalStateException(String.format(getLoggingLocale(), builderAlreadyBuilt$str())); + _copyStackTraceMinusOne(result); + return result; + } + private static void _copyStackTraceMinusOne(final Throwable e) { + final StackTraceElement[] st = e.getStackTrace(); + e.setStackTrace(Arrays.copyOfRange(st, 1, st.length)); + } + protected String credentialNotSupported$str() { + return "ELY00008: The given credential is not supported here"; + } + @Override + public final IllegalArgumentException credentialNotSupported() { + final IllegalArgumentException result = new IllegalArgumentException(String.format(getLoggingLocale(), credentialNotSupported$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String nameAlreadySet$str() { + return "ELY01000: Authentication name was already set on this context"; + } + @Override + public final IllegalStateException nameAlreadySet() { + final IllegalStateException result = new IllegalStateException(String.format(getLoggingLocale(), nameAlreadySet$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String noAuthenticationInProgress$str() { + return "ELY01003: No authentication is in progress"; + } + @Override + public final IllegalStateException noAuthenticationInProgress() { + final IllegalStateException result = new IllegalStateException(String.format(getLoggingLocale(), noAuthenticationInProgress$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String realmMapDoesNotContainDefault$str() { + return "ELY01005: Realm map does not contain mapping for default realm '%s'"; + } + @Override + public final IllegalArgumentException realmMapDoesNotContainDefault(final String defaultRealm) { + final IllegalArgumentException result = new IllegalArgumentException(String.format(getLoggingLocale(), realmMapDoesNotContainDefault$str(), defaultRealm)); + _copyStackTraceMinusOne(result); + return result; + } + protected String unableToObtainExclusiveAccess$str() { + return "ELY01019: Unable to obtain exclusive access to backing identity"; + } + @Override + public final RealmUnavailableException unableToObtainExclusiveAccess() { + final RealmUnavailableException result = new RealmUnavailableException(String.format(getLoggingLocale(), unableToObtainExclusiveAccess$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String userDoesNotExist$str() { + return "ELY01033: User does not exist"; + } + @Override + public final IllegalStateException userDoesNotExist() { + final IllegalStateException result = new IllegalStateException(String.format(getLoggingLocale(), userDoesNotExist$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String invalidCredentialTypeSpecified$str() { + return "ELY01034: Invalid credential type specified"; + } + @Override + public final IllegalStateException invalidCredentialTypeSpecified() { + final IllegalStateException result = new IllegalStateException(String.format(getLoggingLocale(), invalidCredentialTypeSpecified$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String invalidName$str() { + return "ELY01064: Invalid identity name"; + } + @Override + public final IllegalArgumentException invalidName() { + final IllegalArgumentException result = new IllegalArgumentException(String.format(getLoggingLocale(), invalidName$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String runAsAuthorizationFailed$str() { + return "ELY01088: Attempting to run as \"%s\" authorization operation failed"; + } + @Override + public final AuthorizationFailureException runAsAuthorizationFailed(final Principal principal, final Principal targetPrincipal, final Throwable cause) { + final AuthorizationFailureException result = new AuthorizationFailureException(String.format(getLoggingLocale(), runAsAuthorizationFailed$str(), targetPrincipal), cause, principal); + _copyStackTraceMinusOne(result); + return result; + } + protected String invalidMechRealmSelection$str() { + return "ELY01092: Invalid mechanism realm selection \"%s\""; + } + @Override + public final IllegalArgumentException invalidMechRealmSelection(final String realmName) { + final IllegalArgumentException result = new IllegalArgumentException(String.format(getLoggingLocale(), invalidMechRealmSelection$str(), realmName)); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechRealmAlreadySelected$str() { + return "ELY01093: Mechanism realm was already selected"; + } + @Override + public final IllegalStateException mechRealmAlreadySelected() { + final IllegalStateException result = new IllegalStateException(String.format(getLoggingLocale(), mechRealmAlreadySelected$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String unableToCreateIdentity$str() { + return "ELY01095: Unable to create identity"; + } + @Override + public final RealmUnavailableException unableToCreateIdentity() { + final RealmUnavailableException result = new RealmUnavailableException(String.format(getLoggingLocale(), unableToCreateIdentity$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String noSuchIdentity$str() { + return "ELY01096: No such identity"; + } + @Override + public final RealmUnavailableException noSuchIdentity() { + final RealmUnavailableException result = new RealmUnavailableException(String.format(getLoggingLocale(), noSuchIdentity$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String cannotSucceedNotAuthorized$str() { + return "ELY01112: Authentication cannot succeed; not authorized"; + } + @Override + public final IllegalStateException cannotSucceedNotAuthorized() { + final IllegalStateException result = new IllegalStateException(String.format(getLoggingLocale(), cannotSucceedNotAuthorized$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String unableToSelectMechanismConfiguration$str() { + return "ELY01119: Unable to resolve MechanismConfiguration for mechanismType='%s', mechanismName='%s', hostName='%s', protocol='%s'."; + } + @Override + public final IllegalStateException unableToSelectMechanismConfiguration(final String mechanismType, final String mechanismName, final String hostName, final String protocol) { + final IllegalStateException result = new IllegalStateException(String.format(getLoggingLocale(), unableToSelectMechanismConfiguration$str(), mechanismType, mechanismName, hostName, protocol)); + _copyStackTraceMinusOne(result); + return result; + } + protected String tooLateToSetMechanismInformation$str() { + return "ELY01120: Too late to set mechanism information as authentication has already begun."; + } + @Override + public final IllegalStateException tooLateToSetMechanismInformation() { + final IllegalStateException result = new IllegalStateException(String.format(getLoggingLocale(), tooLateToSetMechanismInformation$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String credentialUpdateNotSupportedByRealm$str() { + return "ELY01124: The security realm does not support updating a credential"; + } + @Override + public final UnsupportedOperationException credentialUpdateNotSupportedByRealm() { + final UnsupportedOperationException result = new UnsupportedOperationException(String.format(getLoggingLocale(), credentialUpdateNotSupportedByRealm$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String classLoaderSecurityDomainExists$str() { + return "ELY01148: A SecurityDomain has already been associated with the specified ClassLoader"; + } + @Override + public final IllegalStateException classLoaderSecurityDomainExists() { + final IllegalStateException result = new IllegalStateException(String.format(getLoggingLocale(), classLoaderSecurityDomainExists$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String cantWithSameSecurityDomainDomain$str() { + return "ELY01149: Can not use SecurityIdentity with SecurityIdentity from same SecurityDomain"; + } + @Override + public final IllegalArgumentException cantWithSameSecurityDomainDomain() { + final IllegalArgumentException result = new IllegalArgumentException(String.format(getLoggingLocale(), cantWithSameSecurityDomainDomain$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String authenticationFailedEvidenceVerification$str() { + return "ELY01151: Evidence Verification Failed."; + } + @Override + public final SecurityException authenticationFailedEvidenceVerification() { + final SecurityException result = new SecurityException(String.format(getLoggingLocale(), authenticationFailedEvidenceVerification$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String authenticationFailedAuthorization$str() { + return "ELY01152: Authorization Check Failed."; + } + @Override + public final SecurityException authenticationFailedAuthorization() { + final SecurityException result = new SecurityException(String.format(getLoggingLocale(), authenticationFailedAuthorization$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String securityDomainMismatch$str() { + return "ELY01155: Security domain mismatch"; + } + @Override + public final IllegalArgumentException securityDomainMismatch() { + final IllegalArgumentException result = new IllegalArgumentException(String.format(getLoggingLocale(), securityDomainMismatch$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String cannotObtainCredentialFromFactory$str() { + return "ELY01156: Cannot obtain a credential from a security factory"; + } + @Override + public final IOException cannotObtainCredentialFromFactory(final GeneralSecurityException e) { + final IOException result = new IOException(String.format(getLoggingLocale(), cannotObtainCredentialFromFactory$str()), e); + _copyStackTraceMinusOne(result); + return result; + } + @Override + public final void eventHandlerFailed(final Throwable cause) { + super.log.logf(FQCN, ERROR, cause, eventHandlerFailed$str()); + } + protected String eventHandlerFailed$str() { + return "ELY01094: An event handler threw an exception"; + } + protected String roleMappedAlreadyInitialized$str() { + return "ELY08510: Role mapper has already been initialized."; + } + @Override + public final IllegalStateException roleMappedAlreadyInitialized() { + final IllegalStateException result = new IllegalStateException(String.format(getLoggingLocale(), roleMappedAlreadyInitialized$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String roleMappedNotInitialized$str() { + return "ELY08511: Role mapper hasn't been initialized yet."; + } + @Override + public final IllegalStateException roleMappedNotInitialized() { + final IllegalStateException result = new IllegalStateException(String.format(getLoggingLocale(), roleMappedNotInitialized$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String invalidReplacementInRegexRoleMapper$str() { + return "ELY16000: Invalid replacement in regex role mapper."; + } + @Override + public final IllegalArgumentException invalidReplacementInRegexRoleMapper() { + final IllegalArgumentException result = new IllegalArgumentException(String.format(getLoggingLocale(), invalidReplacementInRegexRoleMapper$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String invalidPatternInRegexRoleMapper$str() { + return "ELY16001: Invalid pattern in regex role mapper."; + } + @Override + public final IllegalArgumentException invalidPatternInRegexRoleMapper() { + final IllegalArgumentException result = new IllegalArgumentException(String.format(getLoggingLocale(), invalidPatternInRegexRoleMapper$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String securityEventIdentityWrongDomain$str() { + return "ELY16002: Can not handle SecurityEvent with SecurityIdentity from other SecurityDomain"; + } + @Override + public final IllegalArgumentException securityEventIdentityWrongDomain() { + final IllegalArgumentException result = new IllegalArgumentException(String.format(getLoggingLocale(), securityEventIdentityWrongDomain$str())); + _copyStackTraceMinusOne(result); + return result; + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/server/event/RealmAbandonedAuthenticationEvent.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/server/event/RealmAbandonedAuthenticationEvent.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/server/event/RealmAbandonedAuthenticationEvent.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,49 @@ +/* + * 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.auth.server.event; + +import org.wildfly.security.auth.server.RealmIdentity; + +/** + * An event indicating that authentication was abandoned before it could complete. + * + * @author David M. Lloyd + */ +public final class RealmAbandonedAuthenticationEvent extends RealmAuthenticationEvent { + /** + * Construct a new instance. + * + * @param realmIdentity the realm identity of the authentication event + */ + public RealmAbandonedAuthenticationEvent(final RealmIdentity realmIdentity) { + super(realmIdentity); + } + + public R accept(final RealmEventVisitor visitor, final P param) { + return visitor.handleAbandonedAuthenticationEvent(this, param); + } + + public boolean isSuccess() { + return false; + } + + public boolean isFailure() { + return false; + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/server/event/RealmAuthenticationEvent.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/server/event/RealmAuthenticationEvent.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/server/event/RealmAuthenticationEvent.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,69 @@ +/* + * 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.auth.server.event; + +import org.wildfly.security.auth.server.RealmIdentity; + +/** + * A realm authentication event. The realm identity may be destroyed at some point after the event is handled. + * + * @author David M. Lloyd + */ +public abstract class RealmAuthenticationEvent extends RealmEvent { + + private final RealmIdentity realmIdentity; + + /** + * Construct a new instance. + * + * @param realmIdentity the realm identity of the authentication event + */ + protected RealmAuthenticationEvent(final RealmIdentity realmIdentity) { + this.realmIdentity = realmIdentity; + } + + /** + * Get the realm identity. + * + * @return the realm identity + */ + public final RealmIdentity getRealmIdentity() { + return realmIdentity; + } + + public R accept(final RealmEventVisitor visitor, final P param) { + return visitor.handleAuthenticationEvent(this, param); + } + + /** + * Determine if this authentication was definitely successful. + * + * @return {@code true} if the authentication was definitely successful, {@code false} if it was not definitely + * successful + */ + public abstract boolean isSuccess(); + + /** + * Determine if this authentication definitely failed. + * + * @return {@code true} if the authentication definitely failed, {@code false} if it did not definitely + * fail + */ + public abstract boolean isFailure(); +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/server/event/RealmAuthorizationEvent.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/server/event/RealmAuthorizationEvent.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/server/event/RealmAuthorizationEvent.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,75 @@ +/* + * 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.auth.server.event; + +import java.security.Principal; + +import org.wildfly.security.authz.AuthorizationIdentity; + +/** + * A realm authorization event. The realm identity may be destroyed at some point after the event is handled. + * + * @author David M. Lloyd + */ +public abstract class RealmAuthorizationEvent extends RealmEvent { + + private final AuthorizationIdentity authorizationIdentity; + private final Principal principal; + + /** + * Construct a new instance. + * + * @param authorizationIdentity the authorization identity + * @param principal the authorization principal + */ + protected RealmAuthorizationEvent(final AuthorizationIdentity authorizationIdentity, final Principal principal) { + this.authorizationIdentity = authorizationIdentity; + this.principal = principal; + } + + /** + * Get the authorization identity of this event. + * + * @return the authorization identity of this event + */ + public AuthorizationIdentity getAuthorizationIdentity() { + return authorizationIdentity; + } + + /** + * Get the authorization principal. This principal is the result of the application of the security domain's + * principal rewriting policies and may not correspond to the name used to locate the identity in the realm. + * + * @return the authorization principal + */ + public Principal getPrincipal() { + return principal; + } + + public R accept(final RealmEventVisitor visitor, final P param) { + return visitor.handleAuthorizationEvent(this, param); + } + + /** + * Determine if this authorization was successful. + * + * @return {@code true} if the authentication was successful, {@code false} if it failed + */ + public abstract boolean isAuthorized(); +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/server/event/RealmDefiniteOutcomeAuthenticationEvent.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/server/event/RealmDefiniteOutcomeAuthenticationEvent.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/server/event/RealmDefiniteOutcomeAuthenticationEvent.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,72 @@ +/* + * 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.auth.server.event; + +import org.wildfly.security.auth.server.RealmIdentity; +import org.wildfly.security.credential.Credential; +import org.wildfly.security.evidence.Evidence; + +/** + * An authentication event with a definite outcome. + * + * @author David M. Lloyd + */ +public abstract class RealmDefiniteOutcomeAuthenticationEvent extends RealmAuthenticationEvent { + private final Credential credential; + private final Evidence evidence; + + /** + * Construct a new instance. + * + * @param realmIdentity the authenticated realm identity + * @param credential the actual credential value from the authentication (may be {@code null} if not known) + * @param evidence the evidence used to authenticate (may be {@code null} if not known or not applicable) + */ + RealmDefiniteOutcomeAuthenticationEvent(final RealmIdentity realmIdentity, final Credential credential, final Evidence evidence) { + super(realmIdentity); + this.credential = credential; + this.evidence = evidence; + } + + /** + * Get the actual credential used. + * + * @return the actual credential used, or {@code null} if it is not known or none was used + */ + public Credential getCredential() { + return credential; + } + + /** + * Get the actual credential guess used. + * + * @return the actual credential guess used, or {@code null} if there was no guess, it is not known, or no credential was used + */ + public Evidence getEvidence() { + return evidence; + } + + public R accept(final RealmEventVisitor visitor, final P param) { + return visitor.handleDefiniteOutcomeAuthenticationEvent(this, param); + } + + public final boolean isFailure() { + return ! isSuccess(); + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/server/event/RealmEvent.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/server/event/RealmEvent.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/server/event/RealmEvent.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,46 @@ +/* + * 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.auth.server.event; + +/** + * An event which is potentially relevant to a realm. + * + * @author David M. Lloyd + */ +public abstract class RealmEvent { + + /** + * Construct a new instance. + */ + protected RealmEvent() { + } + + /** + * Accept the given visitor, calling the method which is most applicable to this event type. + * + * @param visitor the visitor + * @param param the parameter to pass to the visitor {@code handleXxx} method + * @param

the visitor parameter type + * @param the visitor return type + * @return the value returned from the visitor {@code handleXxx} method + */ + public R accept(RealmEventVisitor visitor, P param) { + return visitor.handleUnknownEvent(this, param); + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/server/event/RealmEventVisitor.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/server/event/RealmEventVisitor.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/server/event/RealmEventVisitor.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,146 @@ +/* + * 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.auth.server.event; + +/** + * A class which provides an easy way to handle realm events based on the type of the event. The visitor can accept + * a parameter and return a value. To invoke the appropriate visitor method based on the event type, use the + * {@link RealmEvent#accept(RealmEventVisitor, Object) <P, R> R RealmEvent.accept(RealmEventVisitor<P, R>, P)} method. + * + * @param

the visitor's parameter type (may be {@link Void}) + * @param the visitor's return type (may be {@link Void}) + * @author David M. Lloyd + */ +public abstract class RealmEventVisitor { + /** + * Construct a new instance. + */ + protected RealmEventVisitor() { + } + + /** + * Handle any unhandled realm event. + * + * @param event the realm event + * @param param the visitor parameter + * @return the visitor return value + */ + public R handleUnknownEvent(final RealmEvent event, final P param) { + return null; + } + + /** + * Handle any authentication-related realm event. + * + * @param event the realm event + * @param param the visitor parameter + * @return the visitor return value + */ + public R handleAuthenticationEvent(final RealmAuthenticationEvent event, final P param) { + return handleUnknownEvent(event, param); + } + + /** + * Handle an abandoned authentication realm event. + * + * @param event the realm event + * @param param the visitor parameter + * @return the visitor return value + */ + public R handleAbandonedAuthenticationEvent(final RealmAbandonedAuthenticationEvent event, final P param) { + return handleAuthenticationEvent(event, param); + } + + /** + * Handle a definite-outcome authentication realm event. + * + * @param event the realm event + * @param param the visitor parameter + * @return the visitor return value + */ + public R handleDefiniteOutcomeAuthenticationEvent(final RealmDefiniteOutcomeAuthenticationEvent event, final P param) { + return handleAuthenticationEvent(event, param); + } + + /** + * Handle a successful authentication realm event. + * + * @param event the realm event + * @param param the visitor parameter + * @return the visitor return value + */ + public R handleSuccessfulAuthenticationEvent(final RealmSuccessfulAuthenticationEvent event, final P param) { + return handleDefiniteOutcomeAuthenticationEvent(event, param); + } + + /** + * Handle a failed authentication realm event. + * + * @param event the realm event + * @param param the visitor parameter + * @return the visitor return value + */ + public R handleFailedAuthenticationEvent(final RealmFailedAuthenticationEvent event, final P param) { + return handleDefiniteOutcomeAuthenticationEvent(event, param); + } + + /** + * Handle any authorization-related realm event. + * + * @param event the realm event + * @param param the visitor parameter + * @return the visitor return value + */ + public R handleAuthorizationEvent(final RealmAuthorizationEvent event, final P param) { + return handleUnknownEvent(event, param); + } + + /** + * Handle an identity authorization realm event. + * + * @param event the realm event + * @param param the visitor parameter + * @return the visitor return value + */ + public R handleIdentityAuthorizationEvent(final RealmIdentityAuthorizationEvent event, final P param) { + return handleAuthorizationEvent(event, param); + } + + /** + * Handle an identity successful authorization realm event. + * + * @param event the realm event + * @param param the visitor parameter + * @return the visitor return value + */ + public R handleIdentitySuccessfulAuthorizationEvent(final RealmIdentitySuccessfulAuthorizationEvent event, final P param) { + return handleIdentityAuthorizationEvent(event, param); + } + + /** + * Handle an identity failed authorization realm event. + * + * @param event the realm event + * @param param the visitor parameter + * @return the visitor return value + */ + public R handleIdentityFailedAuthorizationEvent(final RealmIdentityFailedAuthorizationEvent event, final P param) { + return handleIdentityAuthorizationEvent(event, param); + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/server/event/RealmFailedAuthenticationEvent.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/server/event/RealmFailedAuthenticationEvent.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/server/event/RealmFailedAuthenticationEvent.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,50 @@ +/* + * 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.auth.server.event; + +import org.wildfly.security.auth.server.RealmIdentity; +import org.wildfly.security.credential.Credential; +import org.wildfly.security.evidence.Evidence; + +/** + * A realm event signifying a failed authentication. + * + * @author David M. Lloyd + */ +public final class RealmFailedAuthenticationEvent extends RealmDefiniteOutcomeAuthenticationEvent { + + /** + * Construct a new instance. + * + * @param realmIdentity the realm identity of the failed authentication + * @param credential the actual credential value from the authentication (may be {@code null} if not known) + * @param evidence the evidence used to authenticate (may be {@code null} if not known or not applicable) + */ + public RealmFailedAuthenticationEvent(final RealmIdentity realmIdentity, final Credential credential, final Evidence evidence) { + super(realmIdentity, credential, evidence); + } + + public R accept(final RealmEventVisitor visitor, final P param) { + return visitor.handleFailedAuthenticationEvent(this, param); + } + + public boolean isSuccess() { + return false; + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/server/event/RealmIdentityAuthorizationEvent.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/server/event/RealmIdentityAuthorizationEvent.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/server/event/RealmIdentityAuthorizationEvent.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,58 @@ +/* + * 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.auth.server.event; + +import java.security.Principal; + +import org.wildfly.security.authz.AuthorizationIdentity; + +/** + * An event indicating that one identity attempted to authorize as another identity. + * + * @author David M. Lloyd + */ +public abstract class RealmIdentityAuthorizationEvent extends RealmAuthorizationEvent { + + private final Principal newPrincipal; + + /** + * Construct a new instance. + * + * @param authorizationIdentity the identity of the authorizing party + * @param principal the authorization principal + * @param newPrincipal the authorize-as principal + */ + protected RealmIdentityAuthorizationEvent(final AuthorizationIdentity authorizationIdentity, final Principal principal, final Principal newPrincipal) { + super(authorizationIdentity, principal); + this.newPrincipal = newPrincipal; + } + + /** + * Get the principal to which the existing identity is being authorized to act. + * + * @return the new principal + */ + public Principal getNewPrincipal() { + return newPrincipal; + } + + public R accept(final RealmEventVisitor visitor, final P param) { + return visitor.handleIdentityAuthorizationEvent(this, param); + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/server/event/RealmIdentityFailedAuthorizationEvent.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/server/event/RealmIdentityFailedAuthorizationEvent.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/server/event/RealmIdentityFailedAuthorizationEvent.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,50 @@ +/* + * 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.auth.server.event; + +import java.security.Principal; + +import org.wildfly.security.authz.AuthorizationIdentity; + +/** + * An event indicating that one identity attempted to authorize as another identity. + * + * @author David M. Lloyd + */ +public final class RealmIdentityFailedAuthorizationEvent extends RealmIdentityAuthorizationEvent { + + /** + * Construct a new instance. + * + * @param authorizationIdentity the identity of the authorizing party + * @param principal the authorization principal + * @param newPrincipal the authorize-as principal + */ + public RealmIdentityFailedAuthorizationEvent(final AuthorizationIdentity authorizationIdentity, final Principal principal, final Principal newPrincipal) { + super(authorizationIdentity, principal, newPrincipal); + } + + public R accept(final RealmEventVisitor visitor, final P param) { + return visitor.handleIdentityFailedAuthorizationEvent(this, param); + } + + public boolean isAuthorized() { + return false; + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/server/event/RealmIdentitySuccessfulAuthorizationEvent.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/server/event/RealmIdentitySuccessfulAuthorizationEvent.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/server/event/RealmIdentitySuccessfulAuthorizationEvent.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,50 @@ +/* + * 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.auth.server.event; + +import java.security.Principal; + +import org.wildfly.security.authz.AuthorizationIdentity; + +/** + * An event indicating that one identity attempted to authorize as another identity. + * + * @author David M. Lloyd + */ +public final class RealmIdentitySuccessfulAuthorizationEvent extends RealmIdentityAuthorizationEvent { + + /** + * Construct a new instance. + * + * @param authorizationIdentity the identity of the authorizing party + * @param principal the authorization principal + * @param newPrincipal the authorized-as principal + */ + public RealmIdentitySuccessfulAuthorizationEvent(final AuthorizationIdentity authorizationIdentity, final Principal principal, final Principal newPrincipal) { + super(authorizationIdentity, principal, newPrincipal); + } + + public R accept(final RealmEventVisitor visitor, final P param) { + return visitor.handleIdentitySuccessfulAuthorizationEvent(this, param); + } + + public boolean isAuthorized() { + return true; + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/server/event/RealmSuccessfulAuthenticationEvent.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/server/event/RealmSuccessfulAuthenticationEvent.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/server/event/RealmSuccessfulAuthenticationEvent.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,63 @@ +/* + * 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.auth.server.event; + +import org.wildfly.security.auth.server.RealmIdentity; +import org.wildfly.security.authz.AuthorizationIdentity; +import org.wildfly.security.credential.Credential; +import org.wildfly.security.evidence.Evidence; + +/** + * A realm event signifying authentication success. + * + * @author David M. Lloyd + */ +public final class RealmSuccessfulAuthenticationEvent extends RealmDefiniteOutcomeAuthenticationEvent { + private final AuthorizationIdentity authorizationIdentity; + + /** + * Construct a new instance. + * + * @param realmIdentity the realm identity that successfully authenticated + * @param authorizationIdentity the authenticated authorization identity + * @param credential the actual credential value from the authentication (may be {@code null} if not known) + * @param evidence the evidence used to authenticate (may be {@code null} if not known or not applicable) + */ + public RealmSuccessfulAuthenticationEvent(final RealmIdentity realmIdentity, final AuthorizationIdentity authorizationIdentity, final Credential credential, final Evidence evidence) { + super(realmIdentity, credential, evidence); + this.authorizationIdentity = authorizationIdentity; + } + + /** + * Get the authorization identity. + * + * @return the authorization identity + */ + public AuthorizationIdentity getAuthorizationIdentity() { + return authorizationIdentity; + } + + public R accept(final RealmEventVisitor visitor, final P param) { + return visitor.handleSuccessfulAuthenticationEvent(this, param); + } + + public boolean isSuccess() { + return true; + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/server/event/Rfc3164SyslogEvent.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/server/event/Rfc3164SyslogEvent.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/server/event/Rfc3164SyslogEvent.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,38 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2019 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.auth.server.event; + +import org.jboss.logmanager.handlers.SyslogHandler; +import org.wildfly.security.auth.server.SecurityIdentity; + +/** + * A security audit event indicating that a log with RFC 3164 syslog format is occurring + * + * @author Justin Cook + */ +public class Rfc3164SyslogEvent extends SyslogAuditEvent { + + /** + * Constructor for a new instance. + * + * @param securityIdentity the {@link SecurityIdentity} that corresponds to this event that is to be logged with RFC3164 + */ + public Rfc3164SyslogEvent(SecurityIdentity securityIdentity){ + super(securityIdentity, SyslogHandler.SyslogType.RFC3164); + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/server/event/Rfc5424SyslogEvent.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/server/event/Rfc5424SyslogEvent.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/server/event/Rfc5424SyslogEvent.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,38 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2019 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.auth.server.event; + +import org.jboss.logmanager.handlers.SyslogHandler; +import org.wildfly.security.auth.server.SecurityIdentity; + +/** + * A security audit event indicating that a log with RFC 3164 syslog format is occurring + * + * @author Justin Cook + */ +public class Rfc5424SyslogEvent extends SyslogAuditEvent { + + /** + * Constructor for a new instance. + * + * @param securityIdentity the {@link SecurityIdentity} that corresponds to this event that is to be logged with RFC5424 + */ + public Rfc5424SyslogEvent(SecurityIdentity securityIdentity){ + super(securityIdentity, SyslogHandler.SyslogType.RFC5424); + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/server/event/SecurityAuthenticationEvent.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/server/event/SecurityAuthenticationEvent.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/server/event/SecurityAuthenticationEvent.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,44 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2017 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.auth.server.event; + +import org.wildfly.security.auth.server.SecurityIdentity; + +/** + * A security authentication event. + * + * @author Darran Lofthouse + */ +public abstract class SecurityAuthenticationEvent extends SecurityDefiniteOutcomeEvent { + + /** + * Create a new instance. + * + * @param securityIdentity the {@link SecurityIdentity} at the time of authentication. + * @param successful was the authentication process successful. + */ + SecurityAuthenticationEvent(SecurityIdentity securityIdentity, boolean successful) { + super(securityIdentity, successful); + } + + @Override + public R accept(SecurityEventVisitor visitor, P param) { + return visitor.handleAuthenticationEvent(this, param); + } + +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/server/event/SecurityAuthenticationFailedEvent.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/server/event/SecurityAuthenticationFailedEvent.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/server/event/SecurityAuthenticationFailedEvent.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,58 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2017 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.auth.server.event; + +import org.wildfly.security.auth.server.SecurityIdentity; + +import java.security.Principal; + +/** + * An event to represent a failed authentication. + * + * @author Darran Lofthouse + */ +public final class SecurityAuthenticationFailedEvent extends SecurityAuthenticationEvent { + + private final Principal principal; + + /** + * Constructor for a new instance. + * + * @param securityIdentity the {@link SecurityIdentity} that failed authentication ({@code null} when identity does not exists) + * @param principal the principal used to that failed authentication (filled event if identity does not exists) + */ + public SecurityAuthenticationFailedEvent(SecurityIdentity securityIdentity, Principal principal) { + super(securityIdentity, false); + this.principal = principal; + } + + /** + * Gets the principal used to the failed authentication. + * + * @return the principal used to that failed authentication (filled event if identity does not exists) + */ + public Principal getPrincipal() { + return principal; + } + + @Override + public R accept(SecurityEventVisitor visitor, P param) { + return visitor.handleAuthenticationFailedEvent(this, param); + } + +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/server/event/SecurityAuthenticationSuccessfulEvent.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/server/event/SecurityAuthenticationSuccessfulEvent.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/server/event/SecurityAuthenticationSuccessfulEvent.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,43 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2017 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.auth.server.event; + +import org.wildfly.security.auth.server.SecurityIdentity; + +/** + * An event to represent a successful authentication. + * + * @author Darran Lofthouse + */ +public final class SecurityAuthenticationSuccessfulEvent extends SecurityAuthenticationEvent { + + /** + * Constructor for a new instance. + * + * @param securityIdentity the {@link SecurityIdentity} that successfully authenticated. + */ + public SecurityAuthenticationSuccessfulEvent(SecurityIdentity securityIdentity) { + super(securityIdentity, true); + } + + @Override + public R accept(SecurityEventVisitor visitor, P param) { + return visitor.handleAuthenticationSuccessfulEvent(this, param); + } + +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/server/event/SecurityDefiniteOutcomeEvent.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/server/event/SecurityDefiniteOutcomeEvent.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/server/event/SecurityDefiniteOutcomeEvent.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,42 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2017 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.auth.server.event; + +import org.wildfly.security.auth.server.SecurityIdentity; + +/** + * A {@link SecurityEvent} that has a definite outcome of being successful or not. + * + * @author Darran Lofthouse + */ +public abstract class SecurityDefiniteOutcomeEvent extends SecurityEvent { + + private final boolean successful; + /** + * @param securityIdentity + */ + SecurityDefiniteOutcomeEvent(SecurityIdentity securityIdentity, boolean successful) { + super(securityIdentity); + this.successful = successful; + } + + public boolean isSuccessful() { + return successful; + } + +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/server/event/SecurityEvent.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/server/event/SecurityEvent.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/server/event/SecurityEvent.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,79 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2017 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.auth.server.event; + +import static org.wildfly.common.Assert.checkNotNullParam; + +import java.time.Instant; + +import org.wildfly.security.auth.server.SecurityDomain; +import org.wildfly.security.auth.server.SecurityIdentity; + +/** + * Base class for security events emitted from a {@link SecurityDomain}. + * + * @author Darran Lofthouse + */ +public abstract class SecurityEvent { + + private final Instant instant = Instant.now(); + + private final SecurityIdentity securityIdentity; + + /** + * Constructor for a new instance. + * + * @param securityIdentity the current {@link SecurityIdentity} for the event. + */ + SecurityEvent(SecurityIdentity securityIdentity) { + this.securityIdentity = checkNotNullParam("securityIdentity", securityIdentity); + } + + /** + * Get the {@link SecurityIdentity} that was active at the time this event was triggered. + * + * @return the {@link SecurityIdentity} that was active at the time this event was triggered. + */ + public SecurityIdentity getSecurityIdentity() { + return securityIdentity; + } + + /** + * Obtain the {@link Instant} this event was created. + * + * @return the {@link Instant} this event was created. + */ + public Instant getInstant() { + return instant; + } + + /** + * Accept the given visitor, calling the method which is most applicable to this event type. + * + * @param visitor the visitor + * @param param the parameter to pass to the visitor {@code handleXxx} method + * @param

the visitor parameter type + * @param the visitor return type + * @return the value returned from the visitor {@code handleXxx} method + */ + public R accept(SecurityEventVisitor visitor, P param) { + return visitor.handleUnknownEvent(this, param); + } + +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/server/event/SecurityEventVisitor.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/server/event/SecurityEventVisitor.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/server/event/SecurityEventVisitor.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,142 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2017 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.auth.server.event; + +/** + * An abstract class to be extended by visitor implementations for handling SecurityEvents. + * + * @author Darran Lofthouse + */ +public abstract class SecurityEventVisitor { + + /** + * Construct a security event visitor. + */ + protected SecurityEventVisitor() { + } + + /** + * Handle any unhandled security event. + * + * @param event the security event + * @param param the visitor parameter + * @return the visitor return value + */ + public R handleUnknownEvent(final SecurityEvent event, final P param) { + return null; + } + + /** + * Handle a security definite outcome event. + * + * @param event the security event + * @param param the visitor parameter + * @return the visitor return value + */ + public R handleDefiniteOutcomeEvent(final SecurityDefiniteOutcomeEvent event, final P param) { + return handleUnknownEvent(event, param); + } + + /** + * Handle a security authentication event. + * + * @param event the security event + * @param param the visitor parameter + * @return the visitor return value + */ + public R handleAuthenticationEvent(final SecurityAuthenticationEvent event, final P param) { + return handleDefiniteOutcomeEvent(event, param); + } + + /** + * Handle a security authentication successful event. + * + * @param event the security event + * @param param the visitor parameter + * @return the visitor return value + */ + public R handleAuthenticationSuccessfulEvent(final SecurityAuthenticationSuccessfulEvent event, final P param) { + return handleAuthenticationEvent(event, param); + } + + /** + * Handle a security authentication failed event. + * + * @param event the security event + * @param param the visitor parameter + * @return the visitor return value + */ + public R handleAuthenticationFailedEvent(final SecurityAuthenticationFailedEvent event, final P param) { + return handleAuthenticationEvent(event, param); + } + + /** + * Handle a security permission check event. + * + * @param event the security event + * @param param the visitor parameter + * @return the visitor return value + */ + public R handlePermissionCheckEvent(final SecurityPermissionCheckEvent event, final P param) { + return handleDefiniteOutcomeEvent(event, param); + } + + /** + * Handle a security permission check successful event. + * + * @param event the security event + * @param param the visitor parameter + * @return the visitor return value + */ + public R handlePermissionCheckSuccessfulEvent(final SecurityPermissionCheckSuccessfulEvent event, final P param) { + return handlePermissionCheckEvent(event, param); + } + + /** + * Handle a security permission check failed event. + * + * @param event the security event + * @param param the visitor parameter + * @return the visitor return value + */ + public R handlePermissionCheckFailedEvent(final SecurityPermissionCheckFailedEvent event, final P param) { + return handlePermissionCheckEvent(event, param); + } + + /** + * Handle an auditable event that is to be logged to syslog. + * + * @param event the security event + * @param param the visitor parameter + * @return the visitor return value + */ + public R handleSyslogAuditEvent(final SyslogAuditEvent event, final P param) { + return handleUnknownEvent(event, param); + } + + /** + * Handle a security realm unavailable event. + * + * @param event the security event + * @param param the visitor parameter + * @return the visitor return value + */ + public R handleRealmUnavailableEvent(final SecurityRealmUnavailableEvent event, final P param) { + return handleUnknownEvent(event, param); + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/server/event/SecurityPermissionCheckEvent.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/server/event/SecurityPermissionCheckEvent.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/server/event/SecurityPermissionCheckEvent.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,59 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2017 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.auth.server.event; + +import java.security.Permission; + +import org.wildfly.security.auth.server.SecurityIdentity; + +/** + * A security event relating to a permission check. + * + * @author Darran Lofthouse + */ +public abstract class SecurityPermissionCheckEvent extends SecurityDefiniteOutcomeEvent { + + private final Permission permission; + + /** + * Construct a new instance. + * + * @param securityIdentity the {@link SecurityIdentity} the permission check was against. + * @param successful was the permission check successful. + * @param permission the {@link Permission} that was checked. + */ + public SecurityPermissionCheckEvent(SecurityIdentity securityIdentity, Permission permission, boolean successful) { + super(securityIdentity, successful); + this.permission = permission; + } + + /** + * Obtain the {@link Permission} this event related to. + * + * @return the {@link Permission} this event related to. + */ + public Permission getPermission() { + return permission; + } + + @Override + public R accept(SecurityEventVisitor visitor, P param) { + return visitor.handlePermissionCheckEvent(this, param); + } + +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/server/event/SecurityPermissionCheckFailedEvent.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/server/event/SecurityPermissionCheckFailedEvent.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/server/event/SecurityPermissionCheckFailedEvent.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,46 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2017 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.auth.server.event; + +import java.security.Permission; + +import org.wildfly.security.auth.server.SecurityIdentity; + +/** + * An event to represent a failed permission check. + * + * @author Darran Lofthouse + */ +public final class SecurityPermissionCheckFailedEvent extends SecurityPermissionCheckEvent { + + /** + * Construct a new instance. + * + * @param securityIdentity the {@link SecurityIdentity} the permisson check was against. + * @param permission the {@link Permission} that was checked. + */ + public SecurityPermissionCheckFailedEvent(SecurityIdentity securityIdentity, Permission permission) { + super(securityIdentity, permission, false); + } + + @Override + public R accept(SecurityEventVisitor visitor, P param) { + return visitor.handlePermissionCheckFailedEvent(this, param); + } + +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/server/event/SecurityPermissionCheckSuccessfulEvent.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/server/event/SecurityPermissionCheckSuccessfulEvent.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/server/event/SecurityPermissionCheckSuccessfulEvent.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,46 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2017 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.auth.server.event; + +import java.security.Permission; + +import org.wildfly.security.auth.server.SecurityIdentity; + +/** + * An event to represent a successful permission check. + * + * @author Darran Lofthouse + */ +public final class SecurityPermissionCheckSuccessfulEvent extends SecurityPermissionCheckEvent { + + /** + * Construct a new instance. + * + * @param securityIdentity the {@link SecurityIdentity} the permisson check was against. + * @param permission the {@link Permission} that was checked. + */ + public SecurityPermissionCheckSuccessfulEvent(SecurityIdentity securityIdentity, Permission permission) { + super(securityIdentity, permission, true); + } + + @Override + public R accept(SecurityEventVisitor visitor, P param) { + return visitor.handlePermissionCheckSuccessfulEvent(this, param); + } + +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/server/event/SecurityRealmUnavailableEvent.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/server/event/SecurityRealmUnavailableEvent.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/server/event/SecurityRealmUnavailableEvent.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,54 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2019 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.auth.server.event; + +import org.wildfly.security.auth.server.SecurityIdentity; + +/** + * A security event signifying unavailable realm. + * + * @author Martin Mazanek + */ +public class SecurityRealmUnavailableEvent extends SecurityEvent { + private final String realmName; + + /** + * Construct a new instance. + * + * @param securityIdentity the {@link SecurityIdentity} the permission check was against. + * @param realmName the unavailable realm name. + */ + public SecurityRealmUnavailableEvent(SecurityIdentity securityIdentity, String realmName) { + super(securityIdentity); + this.realmName = realmName; + } + + /** + * Obtain the unavailable realm name. + * + * @return the realm name. + */ + public String getRealmName() { + return realmName; + } + + @Override + public R accept(final SecurityEventVisitor visitor, final P param) { + return visitor.handleRealmUnavailableEvent(this, param); + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/server/event/SyslogAuditEvent.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/server/event/SyslogAuditEvent.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/server/event/SyslogAuditEvent.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,63 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2019 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.auth.server.event; + +import org.jboss.logmanager.handlers.SyslogHandler; +import org.wildfly.security.auth.server.SecurityIdentity; + +/** + * An abstract class to be extended by specific syslog audit events to be handled. + * + * @author Justin Cook + */ +public abstract class SyslogAuditEvent extends SecurityEvent { + private SyslogHandler.SyslogType format; + + /** + * Constructor for a new instance. + * + * @param securityIdentity the {@link SecurityIdentity} that corresponds to this event that is to be logged + * @param format The syslog format that is to be used for this event + */ + SyslogAuditEvent(SecurityIdentity securityIdentity, SyslogHandler.SyslogType format) { + super(securityIdentity); + this.format = format; + } + + /** + * Gets the syslog format that is to be used for this audit event + * + * @return The syslog format + */ + public SyslogHandler.SyslogType getFormat() { + return format; + } + + /** + * Accept the given visitor, calling the method which is most applicable to this event type. + * + * @param visitor the visitor + * @param param the parameter to pass to the visitor {@code handleXxx} method + * @param

the visitor parameter type + * @param the visitor return type + * @return the value returned from the visitor {@code handleXxx} method + */ + public R accept(SecurityEventVisitor visitor, P param) { + return visitor.handleSyslogAuditEvent(this, param); + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/server/http/ElytronMessages.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/server/http/ElytronMessages.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/server/http/ElytronMessages.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,36 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2014 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.auth.server.http; + +import org.jboss.logging.BasicLogger; +import org.jboss.logging.Logger; +import org.jboss.logging.annotations.MessageLogger; + + +/** + * Log messages and exceptions for Elytron. + * + * @author David M. Lloyd + * @author Darran Lofthouse + */ +@MessageLogger(projectCode = "ELY", length = 5) +interface ElytronMessages extends BasicLogger { + + ElytronMessages log = Logger.getMessageLogger(ElytronMessages.class, "org.wildfly.security"); +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/server/http/ElytronMessages_$logger.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/server/http/ElytronMessages_$logger.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/server/http/ElytronMessages_$logger.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,24 @@ +package org.wildfly.security.auth.server.http; + +import java.util.Locale; +import java.io.Serializable; +import javax.annotation.Generated; +import org.jboss.logging.DelegatingBasicLogger; +import org.jboss.logging.BasicLogger; +import org.jboss.logging.Logger; + +/** + * Warning this class consists of generated code. + */ +@Generated(value = "org.jboss.logging.processor.generator.model.MessageLoggerImplementor", date = "2024-01-15T21:48:56+0100") +public class ElytronMessages_$logger extends DelegatingBasicLogger implements ElytronMessages, BasicLogger, Serializable { + private static final long serialVersionUID = 1L; + private static final String FQCN = ElytronMessages_$logger.class.getName(); + public ElytronMessages_$logger(final Logger log) { + super(log); + } + private static final Locale LOCALE = Locale.ROOT; + protected Locale getLoggingLocale() { + return LOCALE; + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/server/http/HttpAuthenticationFactory.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/server/http/HttpAuthenticationFactory.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/server/http/HttpAuthenticationFactory.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,187 @@ +/* + * 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.auth.server.http; + +import static java.util.Arrays.asList; +import static java.util.Collections.emptySet; +import static java.util.Collections.singleton; +import static org.wildfly.security.auth.server.http.ElytronMessages.log; + +import java.util.Collection; +import java.util.Collections; +import java.util.function.UnaryOperator; + +import javax.security.auth.callback.CallbackHandler; + +import org.wildfly.security.auth.server.AbstractMechanismAuthenticationFactory; +import org.wildfly.security.auth.server.MechanismConfigurationSelector; +import org.wildfly.security.auth.server.SecurityDomain; +import org.wildfly.security.credential.AlgorithmCredential; +import org.wildfly.security.credential.Credential; +import org.wildfly.security.credential.PasswordCredential; +import org.wildfly.security.evidence.AlgorithmEvidence; +import org.wildfly.security.evidence.Evidence; +import org.wildfly.security.evidence.PasswordGuessEvidence; +import org.wildfly.security.http.HttpAuthenticationException; +import org.wildfly.security.http.HttpConstants; +import org.wildfly.security.http.HttpServerAuthenticationMechanism; +import org.wildfly.security.http.HttpServerAuthenticationMechanismFactory; +import org.wildfly.security.password.interfaces.ClearPassword; +import org.wildfly.security.password.interfaces.DigestPassword; + +/** + * A HTTP authentication mechanism configuration, the configuration is associated with the {@link SecurityDomain} and + * {@link HttpServerAuthenticationMechanismFactory} for obtaining configured mechanisms. + * + * @author Darran Lofthouse + */ +public final class HttpAuthenticationFactory extends AbstractMechanismAuthenticationFactory { + + HttpAuthenticationFactory(final SecurityDomain securityDomain, final MechanismConfigurationSelector mechanismConfigurationSelector, final HttpServerAuthenticationMechanismFactory factory) { + super(securityDomain, mechanismConfigurationSelector, factory); + } + + protected HttpServerAuthenticationMechanism doCreate(final String name, final CallbackHandler callbackHandler, final UnaryOperator factoryTransformation) throws HttpAuthenticationException { + HttpServerAuthenticationMechanism server = new SecurityIdentityServerMechanismFactory(factoryTransformation.apply(getFactory())).createAuthenticationMechanism(name, Collections.emptyMap(), callbackHandler); + log.tracef("Created HttpServerAuthenticationMechanism [%s] for mechanism [%s]", server, name); + return server; + } + + protected Collection getAllSupportedMechNames() { + return asList(getFactory().getMechanismNames(Collections.emptyMap())); + } + + // TODO: at some point these should no longer be hard-coded + + protected Collection> getSupportedEvidenceTypes(final String mechName) { + switch (mechName) { + case HttpConstants.BASIC_NAME: { + return singleton(PasswordGuessEvidence.class); + } + default: { + return emptySet(); + } + } + } + + protected Collection getSupportedEvidenceAlgorithmNames(final Class evidenceType, final String mechName) { + return emptySet(); + } + + protected Collection> getSupportedCredentialTypes(final String mechName) { + switch (mechName) { + case HttpConstants.BASIC_NAME: + case HttpConstants.DIGEST_NAME: { + return singleton(PasswordCredential.class); + } + default: { + return emptySet(); + } + } + } + + protected Collection getSupportedCredentialAlgorithmNames(final Class credentialType, final String mechName) { + switch (mechName) { + case HttpConstants.BASIC_NAME: { + return singleton("*"); + } + case HttpConstants.DIGEST_NAME: { + return asList(ClearPassword.ALGORITHM_CLEAR, DigestPassword.ALGORITHM_DIGEST_MD5); + } + default: { + return emptySet(); + } + } + } + + protected boolean usesCredentials(final String mechName) { + switch (mechName) { + case HttpConstants.BASIC_NAME: + case HttpConstants.DIGEST_NAME: { + return true; + } + default: { + return false; + } + } + } + + @Override + protected boolean isKnownMechanism(String mechName) { + switch (mechName) { + case HttpConstants.BASIC_NAME: + case HttpConstants.CLIENT_CERT_NAME: + case HttpConstants.DIGEST_NAME: + case HttpConstants.DIGEST_SHA256_NAME: + case HttpConstants.DIGEST_SHA512_256_NAME: + case HttpConstants.EXTERNAL_NAME: + case HttpConstants.FORM_NAME: + case HttpConstants.SPNEGO_NAME: + case HttpConstants.BEARER_TOKEN: { + return true; + } + default: { + return false; + } + } + } + + public void shutdownAuthenticationMechanismFactory() { + super.getFactory().shutdown(); + } + + /** + * Obtain a new {@link Builder} capable of building a {@link HttpAuthenticationFactory}. + * + * @return a new {@link Builder} capable of building a {@link HttpAuthenticationFactory}. + */ + public static Builder builder() { + return new Builder(); + } + + /** + * A builder for SASL server factory configurations. + */ + public static final class Builder extends AbstractMechanismAuthenticationFactory.Builder { + + /** + * Construct a new instance. + */ + Builder() { + } + + public Builder setSecurityDomain(final SecurityDomain securityDomain) { + super.setSecurityDomain(securityDomain); + return this; + } + + public Builder setMechanismConfigurationSelector(final MechanismConfigurationSelector mechanismConfigurationSelector) { + super.setMechanismConfigurationSelector(mechanismConfigurationSelector); + return this; + } + + public Builder setFactory(final HttpServerAuthenticationMechanismFactory factory) { + super.setFactory(factory); + return this; + } + + public HttpAuthenticationFactory build() { + return new HttpAuthenticationFactory(getSecurityDomain(), getMechanismConfigurationSelector(), getFactory()); + } + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/auth/server/http/SecurityIdentityServerMechanismFactory.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/server/http/SecurityIdentityServerMechanismFactory.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/server/http/SecurityIdentityServerMechanismFactory.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,137 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2016 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.auth.server.http; + +import static org.wildfly.security.http.HttpConstants.SECURITY_IDENTITY; + +import static org.wildfly.common.Assert.checkNotNullParam; + +import java.io.IOException; +import java.util.Map; + +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.UnsupportedCallbackException; + +import org.wildfly.security.auth.callback.AuthenticationCompleteCallback; +import org.wildfly.security.auth.callback.SecurityIdentityCallback; +import org.wildfly.security.auth.server.SecurityIdentity; +import org.wildfly.security.http.HttpAuthenticationException; +import org.wildfly.security.http.HttpServerAuthenticationMechanism; +import org.wildfly.security.http.HttpServerAuthenticationMechanismFactory; +import org.wildfly.security.http.HttpServerRequest; + +/** + * A {@link HttpServerAuthenticationMechanismFactory} that wraps authentication mechanism from delegating factory, so + * any request for {@code SECURITY_IDENTITY} negotiated property is caught and {@link SecurityIdentity} from + * the callback handler is returned instead. + * + * @author Darran Lofthouse + */ +class SecurityIdentityServerMechanismFactory implements HttpServerAuthenticationMechanismFactory { + + private final HttpServerAuthenticationMechanismFactory delegate; + + /** + * Construct a new instance. + * + * @param delegate the {@link HttpServerAuthenticationMechanismFactory} calls are delegated to. + */ + public SecurityIdentityServerMechanismFactory(HttpServerAuthenticationMechanismFactory delegate) { + this.delegate = checkNotNullParam("delegate", delegate); + } + + /** + * @see org.wildfly.security.http.HttpServerAuthenticationMechanismFactory#getMechanismNames(Map) + */ + @Override + public String[] getMechanismNames(Map properties) { + return delegate.getMechanismNames(properties); + } + + /** + * @see org.wildfly.security.http.HttpServerAuthenticationMechanismFactory#createAuthenticationMechanism(String, Map, CallbackHandler) + */ + @Override + public HttpServerAuthenticationMechanism createAuthenticationMechanism(String mechanismName, Map properties, CallbackHandler callbackHandler) throws HttpAuthenticationException { + SecurityIdentityCallbackHandler securityIdentityCallbackHandler = new SecurityIdentityCallbackHandler(callbackHandler); + final HttpServerAuthenticationMechanism delegate = this.delegate.createAuthenticationMechanism(mechanismName, properties, securityIdentityCallbackHandler); + if (delegate != null) { + return new HttpServerAuthenticationMechanism() { + + @Override + public String getMechanismName() { + return delegate.getMechanismName(); + } + + @Override + public void evaluateRequest(HttpServerRequest request) throws HttpAuthenticationException { + delegate.evaluateRequest(request); + } + + @Override + public Object getNegotiatedProperty(String propertyName) { + return SECURITY_IDENTITY.equals(propertyName) ? securityIdentityCallbackHandler.getSecurityIdentity() + : delegate.getNegotiatedProperty(propertyName); + } + + @Override + public void dispose() { + delegate.dispose(); + } + + }; + } + return null; + } + + private static class SecurityIdentityCallbackHandler implements CallbackHandler { + + private final CallbackHandler delegate; + private SecurityIdentity securityIdentity; + + SecurityIdentityCallbackHandler(CallbackHandler delegate) { + this.delegate = delegate; + } + + @Override + public void handle(Callback[] callbacks) throws IOException, UnsupportedCallbackException { + Callback[] theCallbacks = callbacks; + SecurityIdentityCallback securityIdentityCallback = null; + for (Callback current : callbacks) { + if (current instanceof AuthenticationCompleteCallback + && ((AuthenticationCompleteCallback) current).succeeded()) { + theCallbacks = new Callback[callbacks.length + 1]; + System.arraycopy(callbacks, 0, theCallbacks, 0, callbacks.length); + theCallbacks[theCallbacks.length - 1] = (securityIdentityCallback = new SecurityIdentityCallback()); + } + } + + delegate.handle(theCallbacks); + if (securityIdentityCallback != null) { + securityIdentity = securityIdentityCallback.getSecurityIdentity(); + } + } + + SecurityIdentity getSecurityIdentity() { + return securityIdentity; + } + + } + +} \ No newline at end of file Index: 3rdParty_sources/elytron/org/wildfly/security/auth/server/package-info.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/auth/server/package-info.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/auth/server/package-info.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,22 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2017 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. + */ + +/** + * Server side of authentication provided by Elytron. + */ +package org.wildfly.security.auth.server; \ No newline at end of file Index: 3rdParty_sources/elytron/org/wildfly/security/authz/AddPrefixRoles.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/authz/AddPrefixRoles.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/authz/AddPrefixRoles.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,49 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2016 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.authz; + +import java.util.Iterator; + +class AddPrefixRoles implements Roles { + private final Roles delegate; + private final String prefix; + + AddPrefixRoles(final Roles delegate, final String prefix) { + this.delegate = delegate; + this.prefix = prefix; + } + + public boolean contains(final String roleName) { + final String prefix = this.prefix; + return roleName.startsWith(prefix) && delegate.contains(roleName.substring(prefix.length())); + } + + public Iterator iterator() { + final Iterator iterator = delegate.iterator(); + return new Iterator() { + public boolean hasNext() { + return iterator.hasNext(); + } + + public String next() { + return prefix + iterator.next(); + } + }; + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/authz/AddSuffixRoles.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/authz/AddSuffixRoles.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/authz/AddSuffixRoles.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,49 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2016 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.authz; + +import java.util.Iterator; + +class AddSuffixRoles implements Roles { + private final Roles delegate; + private final String suffix; + + AddSuffixRoles(final Roles delegate, final String suffix) { + this.delegate = delegate; + this.suffix = suffix; + } + + public boolean contains(final String roleName) { + final String suffix = this.suffix; + return roleName.endsWith(suffix) && delegate.contains(roleName.substring(0, roleName.length() - suffix.length())); + } + + public Iterator iterator() { + final Iterator iterator = delegate.iterator(); + return new Iterator() { + public boolean hasNext() { + return iterator.hasNext(); + } + + public String next() { + return iterator.next() + suffix; + } + }; + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/authz/AggregateAttributes.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/authz/AggregateAttributes.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/authz/AggregateAttributes.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,83 @@ +/* + * Copyright 2019 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.authz; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + + +/** + * An implementation of {@link Attributes} aggregating multiple instances. + * + * Attributes are aggregated on a 'first defined wins' basis, i.e. the first definition of a specific attribute is the one used and remaining definitions are discarded. + * + * @author Darran Lofthouse + */ +public class AggregateAttributes implements Attributes { + + private final Map aggregatedEntries; + + private AggregateAttributes(final Attributes[] aggrgatedAttributes) { + Map aggregatedEntries = new HashMap<>(); + for (Attributes currentAttributes : aggrgatedAttributes) { + for (Entry currentEntry : currentAttributes.entries()) { + String key = currentEntry.getKey(); + if (aggregatedEntries.containsKey(key) == false) { + aggregatedEntries.put(key, currentEntry); + } + } + } + this.aggregatedEntries = aggregatedEntries; + } + + public static Attributes aggregateOf(Attributes... aggrgatedAttributes) { + return new AggregateAttributes(aggrgatedAttributes) + .asReadOnly(); + } + + @Override + public Collection entries() { + return aggregatedEntries.values(); + } + + @Override + public int size(String key) { + return get(key).size(); + } + + @Override + public Entry get(String key) { + if (aggregatedEntries.containsKey(key)) { + return aggregatedEntries.get(key); + } + + // We don't know about this attribute key and can't add it to any of the aggreagted entries. + return new SimpleAttributesEntry(Attributes.EMPTY, key); + } + + @Override + public String get(String key, int idx) { + return get(key).get(idx); + } + + @Override + public int size() { + return entries().size(); + } + +} Index: 3rdParty_sources/elytron/org/wildfly/security/authz/Attributes.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/authz/Attributes.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/authz/Attributes.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,709 @@ +/* + * 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.authz; + +import java.util.AbstractCollection; +import java.util.AbstractSet; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Set; +import java.util.Spliterator; +import java.util.Spliterators; + +import org.wildfly.common.Assert; + +/** + *

A collection of string attributes. + * + *

By default, this interface provides a default implementation for all methods that perform writes to the collection. The default + * implementation will always throw a {@link UnsupportedOperationException}, which means the collection is read-only by default. + * + *

If an implementation needs to also support writes it must override and implement all these default methods. + * + * @author David M. Lloyd + */ +public interface Attributes { + + /** + * Empty, read-only attribute collection. + */ + Attributes EMPTY = new Attributes() { + @Override + public Collection entries() { + return Collections.emptySet(); + } + + @Override + public int size(final String key) { + return 0; + } + + @Override + public boolean remove(final String key) { + return false; + } + + @Override + public String get(final String key, final int idx) { + return null; + } + + @Override + public Entry get(final String key) { + return new SimpleAttributesEntry(this, key); + } + + @Override + public int size() { + return 0; + } + }; + + /** + * Get the entry collection. Changes to the entry collection will modify this attribute collection, if it is + * writable. The returned entries will remain up to date with the state of this collection. + * + * @return the entry collection + */ + Collection entries(); + + /** + * Get the number of values mapped to the given key. + * + * @param key the key + * @return the number of mapped values + */ + int size(String key); + + /** + * Get the collection of values for the given key. The result may implement {@link SetEntry} if the values + * are distinct (for example, a role or group set). + * + * @param key the attribute name + * @return the (possibly empty) attribute value collection + */ + Entry get(String key); + + /** + * Get the mapping for the given key at the given position. + * + * @param key the key + * @param idx the index + * @return the mapping value + * @throws IndexOutOfBoundsException if {@code idx} is less than 0 or greater than or equal to {@code size(key)} + */ + String get(String key, int idx); + + /** + * Get the number of keys in this attribute collection. + * + * @return the number of keys + */ + int size(); + + /** + * Remove all values for the given key from this collection. + * + * @param key the key + * @return {@code true} if the key was found, {@code false} otherwise + * @throws UnsupportedOperationException if this method is not implemented and the operation is not supported + */ + default boolean remove(String key) { + throw Assert.unsupported(); + } + + /** + * Add a mapping for the given key at the given position. + * + * @param key the key + * @param idx the index + * @param value the mapping value + * @throws IndexOutOfBoundsException if {@code idx} is less than 0 or greater than or equal to {@code size(key)} + * @throws UnsupportedOperationException if this method is not implemented and the operation is not supported + */ + default void add(String key, int idx, String value) { + throw Assert.unsupported(); + } + + /** + * Modify the mapping for the given key at the given position. + * + * @param key the key + * @param idx the index + * @param value the mapping value + * @return the previous mapping value + * @throws IndexOutOfBoundsException if {@code idx} is less than 0 or greater than or equal to {@code size(key)} + * @throws UnsupportedOperationException if this method is not implemented and the operation is not supported + */ + default String set(String key, int idx, String value) { + throw Assert.unsupported(); + } + + /** + * Remove and return the mapping for the given key at the given position. All later entries for that key are shifted + * up to fill in the gap left by the removed element. + * + * @param key the key + * @param idx the index + * @return the previous mapping value + * @throws IndexOutOfBoundsException if {@code idx} is less than 0 or greater than or equal to {@code size(key)} + * @throws UnsupportedOperationException if this method is not implemented and the operation is not supported + */ + default String remove(String key, int idx) { + throw Assert.unsupported(); + } + + /** + * Clear this collection, resetting its size to zero. + * + * @throws UnsupportedOperationException if this method is not implemented and the operation is not supported + */ + default void clear() { + throw Assert.unsupported(); + } + + /** + * Remove all values for the given key from this collection, copying the values into a list which is returned. + * + * @param key the key + * @return the values as a list (not {@code null}) + */ + default List copyAndRemove(String key) { + final Entry values = get(key); + List copy = values.isEmpty() ? Collections.emptyList() : new ArrayList<>(values); + remove(key); + return copy; + } + + /** + * Get all the values of all the keys in this collection. The returned collection can be used to modify this + * attributes collection. + * + * @return the collection of all values (not {@code null}) + */ + default Collection values() { + return new AbstractCollection() { + public Iterator iterator() { + final Iterator entries = entries().iterator(); + return new Iterator() { + private Iterator values; + + public boolean hasNext() { + for (;;) { + if (values == null) { + if (! entries.hasNext()) { + return false; + } + values = entries.next().iterator(); + } else if (values.hasNext()) { + return true; + } else { + values = null; + } + } + } + + public String next() { + if (! hasNext()) throw new NoSuchElementException(); + return values.next(); + } + + public void remove() { + final Iterator values = this.values; + if (values == null) { + throw new IllegalStateException(); + } + values.remove(); + } + }; + } + + public void clear() { + Attributes.this.clear(); + } + + public boolean removeAll(final Collection c) { + boolean changed = false; + for (Entry entries : entries()) { + changed = entries.removeAll(c) || changed; + } + return changed; + } + + public boolean retainAll(final Collection c) { + boolean changed = false; + for (Entry entries : entries()) { + changed = entries.retainAll(c) || changed; + } + return changed; + } + + public boolean isEmpty() { + for (Entry entries : entries()) { + if (! entries.isEmpty()) { + return false; + } + } + return true; + } + + public int size() { + int size = 0; + for (Entry entries : entries()) { + size += entries.size(); + } + return size; + } + }; + } + + /** + * Get a set comprised of all the keys in this collection. The returned set can be used to modify this attributes + * collection. + * + * @return the set of all keys (not {@code null}) + */ + default Set keySet() { + return new AbstractSet() { + public Iterator iterator() { + final Iterator entries = entries().iterator(); + return new Iterator() { + public boolean hasNext() { + return entries.hasNext(); + } + + public String next() { + return entries.next().getKey(); + } + + public void remove() { + entries.remove(); + } + }; + } + + public boolean contains(final Object o) { + return o instanceof String && Attributes.this.containsKey((String) o); + } + + public boolean remove(final Object o) { + return o instanceof String && Attributes.this.remove((String) o); + } + + public void clear() { + Attributes.this.clear(); + } + + public int size() { + return Attributes.this.size(); + } + }; + } + + /** + * Conditionally set a specific value of a given key to a new value, if the existing value matches the {@code expect} + * parameter. + * + * @param key the key + * @param idx the index + * @param expect the expected value + * @param update the value to set + * @return {@code true} if the actual value matched the expected value and was updated, {@code false} otherwise + * @throws IndexOutOfBoundsException if {@code idx} is less than 0 or greater than or equal to {@code size(key)} + */ + default boolean set(String key, int idx, String expect, String update) { + Assert.checkNotNullParam("update", update); + if (expect == null || idx < 0 || idx >= size(key) || ! get(key, idx).equals(expect)) { + return false; + } + set(key, idx, update); + return true; + } + + /** + * Get the index of the first occurrence of the given value at the given key, if any. + * + * @param key the key + * @param value the value + * @return the index, or -1 if the value was not found at the given key + */ + default int indexOf(String key, String value) { + final int size = size(key); + for (int i = 0; i < size; i ++) { + if (get(key, i).equals(value)) { + return i; + } + } + return -1; + } + + /** + * Get the index of the last occurrence of the given value at the given key, if any. + * + * @param key the key + * @param value the value + * @return the index, or -1 if the value was not found at the given key + */ + default int lastIndexOf(String key, String value) { + final int size = size(key); + for (int i = size - 1; i >= 0; i --) { + if (get(key, i).equals(value)) { + return i; + } + } + return -1; + } + + /** + * Remove all the values for the given key between the {@code from} index (inclusive) and the {@code to} index + * (exclusive). + * + * @param key the key + * @param from the start index (inclusive) + * @param to the end index (exclusive) + * @throws IndexOutOfBoundsException if {@code idx} is less than 0 or greater than or equal to {@code size(key)} + */ + default void removeRange(String key, int from, int to) { + for (int i = to - 1; i >= from; i --) { + remove(key, i); + } + } + + /** + * Get the first value mapped to the given key. + * + * @param key the key + * @return the value + * @throws IndexOutOfBoundsException if there are no values for the given key + */ + default String getFirst(String key) { + return get(key, 0); + } + + /** + * Get the last value mapped to the given key. + * + * @param key the key + * @return the value + * @throws IndexOutOfBoundsException if there are no values for the given key + */ + default String getLast(String key) { + return get(key, size(key) - 1); + } + + /** + * Add a value before the first mapping for the given key. + * + * @param key the key + * @param value the value + */ + default void addFirst(String key, String value) { + add(key, 0, value); + } + + /** + * Add a value after the last mapping for the given key. + * + * @param key the key + * @param value the value + */ + default void addLast(String key, String value) { + add(key, size(key), value); + } + + /** + * Remove the first value mapped to the given key. + * + * @param key the key + * @return the value + * @throws IndexOutOfBoundsException if there are no values for the given key + */ + default String removeFirst(String key) { + return remove(key, 0); + } + + /** + * Remove the last value mapped to the given key. + * + * @param key the key + * @return the value + * @throws IndexOutOfBoundsException if there are no values for the given key + */ + default String removeLast(String key) { + return remove(key, size(key) - 1); + } + + /** + * Remove the mapping for the given key at the given position if it matches the given existing value. All later + * entries for that key are shifted up to fill in the gap left by the removed element. + * + * @param key the key + * @param idx the index + * @param value the expected previous mapping value + * @return {@code true} if the value matched and was removed, {@code false} otherwise + * @throws IndexOutOfBoundsException if {@code idx} is less than 0 or greater than or equal to {@code size(key)} + */ + default boolean remove(String key, int idx, String value) { + if (get(key, idx).equals(value)) { + remove(key, idx); + return true; + } else { + return false; + } + } + + /** + * Remove the first occurrence of the given value under the given key, if any. + * + * @param key the key + * @param value the value to remove + * @return {@code true} if the value was found and removed, {@code false} otherwise + */ + default boolean removeFirst(String key, String value) { + final int idx = indexOf(key, value); + return idx >= 0 && remove(key, idx, value); + } + + /** + * Remove the last occurrence of the given value under the given key, if any. + * + * @param key the key + * @param value the value to remove + * @return {@code true} if the value was found and removed, {@code false} otherwise + */ + default boolean removeLast(String key, String value) { + final int idx = lastIndexOf(key, value); + return idx >= 0 && remove(key, idx, value); + } + + /** + * Remove the all occurrences of the given value under the given key, if any. + * + * @param key the key + * @param value the value to remove + * @return {@code true} if the value was found and removed, {@code false} otherwise + */ + default boolean removeAll(String key, String value) { + int idx = lastIndexOf(key, value); + if (idx == -1) return false; + while (idx >= 0) { + remove(key, idx, value); + idx = lastIndexOf(key, value); + } + return true; + } + + /** + * Add all the values from the given map to this attributes collection. + * + * @param map the map to copy from + * @return {@code true} if elements were added, {@code false} otherwise + */ + default boolean addAll(Map> map) { + Assert.checkNotNullParam("map", map); + boolean changed = false; + for (Map.Entry> entry : map.entrySet()) { + final Collection value = entry.getValue(); + if (value != null && ! value.isEmpty()) { + final String key = entry.getKey(); + changed = addAll(key, value) || changed; + } + } + return changed; + } + + /** + * Add all the values from the given collection to the value collection for the given key. + * + * @param key the key + * @param values the values to add + * @return {@code true} if elements were added, {@code false} otherwise + */ + default boolean addAll(String key, Collection values) { + Assert.checkNotNullParam("key", key); + Assert.checkNotNullParam("values", values); + boolean changed = false; + for (String s : values) { + if (s != null) { + addLast(key, s); + changed = true; + } + } + return changed; + } + + /** + * Determine if the given key has values in this collection. + * + * @param key the key + * @return {@code true} if the key has values, {@code false} otherwise + */ + default boolean containsKey(String key) { + return key != null && size(key) > 0; + } + + /** + * Determine if the given key has a mapping for the given value in this collection. + * + * @param key the key + * @param value the value + * @return {@code true} if the key has a mapping to the given value, {@code false} otherwise + */ + default boolean containsValue(String key, String value) { + return key != null && value != null && indexOf(key, value) >= 0; + } + + /** + * Replace the mapping for the given key with the values copied from the given collection. + * + * @param key the key + * @param values the new values + * @return a list containing the previously mapped values + */ + default List copyAndReplace(String key, Collection values) { + final List old = copyAndRemove(key); + addAll(key, values); + return old; + } + + /** + * Determine if this collection is empty. + * + * @return {@code true} if the collection is empty, {@code false} otherwise + */ + default boolean isEmpty() { + return size() == 0; + } + + /** + * Returns a read-only instance of this instance. + * + * @return a read-only instance of this instance. + */ + default Attributes asReadOnly() { + return new Attributes() { + private Map entryCache = new HashMap<>(); + private Collection cachedEntries; + + @Override + public Collection entries() { + if (this.cachedEntries != null) { + return this.cachedEntries; + } + + return this.cachedEntries = new AbstractCollection() { + @Override + public Iterator iterator() { + Iterator iterator = Attributes.this.keySet().iterator(); + + return new Iterator() { + @Override + public boolean hasNext() { + return iterator.hasNext(); + } + + @Override + public Entry next() { + return get(iterator.next()); + } + }; + } + + @Override + public int size() { + return Attributes.this.keySet().size(); + } + }; + } + + @Override + public int size(String key) { + return Attributes.this.size(key); + } + + @Override + public String get(String key, int idx) { + return Attributes.this.get(key, idx); + } + + @Override + public int size() { + return Attributes.this.size(); + } + + @Override + public Entry get(String key) { + return this.entryCache.computeIfAbsent(key, s -> new SimpleAttributesEntry(this, s)); + } + }; + } + + /** + * The entry collection for a mapping. + */ + interface Entry extends List { + + /** + * Get the mapping key. + * + * @return the mapping key + */ + String getKey(); + + /** + * Remove all the values for the given key between the {@code from} index (inclusive) and the {@code to} index + * (exclusive). + * + * @param from the start index (inclusive) + * @param to the end index (exclusive) + * @throws IndexOutOfBoundsException if {@code from} or {@code to} is outside of the allowed range + */ + void removeRange(int from, int to); + + /** + * Create a spliterator over the elements of this ordered and non-null collection. + * + * @return the spliterator + */ + default Spliterator spliterator() { + return Spliterators.spliterator(this, Spliterator.ORDERED | Spliterator.NONNULL); + } + } + + /** + * The entry collection for a mapping whose values are a distinct set. + */ + interface SetEntry extends Entry, Set { + + /** + * Create a spliterator over the elements of this distinct, ordered, and non-null collection. + * + * @return the spliterator + */ + default Spliterator spliterator() { + return Spliterators.spliterator(this, Spliterator.DISTINCT | Spliterator.ORDERED | Spliterator.NONNULL); + } + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/authz/AuthorizationCheckException.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/authz/AuthorizationCheckException.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/authz/AuthorizationCheckException.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,72 @@ +/* + * 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.authz; + +import java.security.Permission; +import java.security.Principal; + +import org.wildfly.common.Assert; + +/** + * An exception indicating that an identity authorization check has failed. + * + * @author David M. Lloyd + */ +public class AuthorizationCheckException extends AuthorizationException { + + private static final long serialVersionUID = 5010607869851804099L; + + private final Permission failedPermission; + + /** + * Constructs a new {@code AuthorizationException} instance with an initial message. No cause is specified. + * + * @param msg the message + * @param authorizationPrincipal the principal that failed the authorization check + * @param failedPermission the permission that failed the authorization check + */ + public AuthorizationCheckException(final String msg, final Principal authorizationPrincipal, final Permission failedPermission) { + super(msg, authorizationPrincipal); + Assert.checkNotNullParam("failedPermission", failedPermission); + this.failedPermission = failedPermission; + } + + /** + * Constructs a new {@code AuthorizationException} instance with an initial message and cause. + * + * @param msg the message + * @param cause the cause + * @param authorizationPrincipal the principal that failed the authorization check + * @param failedPermission the permission that failed the authorization check + */ + public AuthorizationCheckException(final String msg, final Throwable cause, final Principal authorizationPrincipal, final Permission failedPermission) { + super(msg, cause, authorizationPrincipal); + Assert.checkNotNullParam("failedPermission", failedPermission); + this.failedPermission = failedPermission; + } + + /** + * Get the permission that failed the authorization check. + * + * @return the permission that failed the authorization check + */ + public Permission getFailedPermission() { + return failedPermission; + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/authz/AuthorizationException.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/authz/AuthorizationException.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/authz/AuthorizationException.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,68 @@ +/* + * 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.authz; + +import java.security.Principal; + +import org.wildfly.common.Assert; + +/** + * A general authorization exception. + * + * @author David M. Lloyd + */ +public abstract class AuthorizationException extends SecurityException { + private static final long serialVersionUID = 3791176860282223771L; + + private final Principal authorizationPrincipal; + + /** + * Constructs a new {@code AuthorizationException} instance with an initial message. No cause is specified. + * + * @param msg the message + * @param authorizationPrincipal the principal being authorized + */ + protected AuthorizationException(final String msg, final Principal authorizationPrincipal) { + super(msg); + Assert.checkNotNullParam("authorizationPrincipal", authorizationPrincipal); + this.authorizationPrincipal = authorizationPrincipal; + } + + /** + * Constructs a new {@code AuthorizationException} instance with an initial message and cause. + * + * @param msg the message + * @param cause the cause + * @param authorizationPrincipal the principal being authorized + */ + protected AuthorizationException(final String msg, final Throwable cause, final Principal authorizationPrincipal) { + super(msg, cause); + Assert.checkNotNullParam("authorizationPrincipal", authorizationPrincipal); + this.authorizationPrincipal = authorizationPrincipal; + } + + /** + * Get the principal being authorized. + * + * @return the principal being authorized + */ + public Principal getAuthorizationPrincipal() { + return authorizationPrincipal; + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/authz/AuthorizationFailureException.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/authz/AuthorizationFailureException.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/authz/AuthorizationFailureException.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,52 @@ +/* + * 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.authz; + +import java.security.Principal; + +/** + * An exception indicating that an authorization check failed for reasons not related to the actual authorization of + * the identity. + * + * @author David M. Lloyd + */ +public class AuthorizationFailureException extends AuthorizationException { + private static final long serialVersionUID = - 5699181816026435025L; + + /** + * Constructs a new {@code AuthorizationFailureException} instance with an initial message. No cause is specified. + * + * @param msg the message + * @param authorizationPrincipal the principal being authorized + */ + public AuthorizationFailureException(final String msg, final Principal authorizationPrincipal) { + super(msg, authorizationPrincipal); + } + + /** + * Constructs a new {@code AuthorizationFailureException} instance with an initial message and cause. + * + * @param msg the message + * @param cause the cause + * @param authorizationPrincipal the principal being authorized + */ + public AuthorizationFailureException(final String msg, final Throwable cause, final Principal authorizationPrincipal) { + super(msg, cause, authorizationPrincipal); + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/authz/AuthorizationIdentity.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/authz/AuthorizationIdentity.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/authz/AuthorizationIdentity.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,126 @@ +/* + * 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.authz; + +import java.util.function.Supplier; + +/** + * A realm's authorization identity. Objects of this class represent an active identity which may be examined for + * authorization decisions. Since there is no upper bound in the lifespan of instances of this class, they should + * not retain references to scarce resources like database connections or file handles. + * + * @author David M. Lloyd + */ +public interface AuthorizationIdentity { + + /** + * Get the attributes which pertain to this identity. By default, an empty attribute collection is returned. + * + * @return the attributes (must not be {@code null}) + */ + default Attributes getAttributes() { + return Attributes.EMPTY; + } + + /** + * Get the runtime attributes which pertain to this identity. By default, an empty attribute collection is returned. + * + * @return the runtime attributes (must not be {@code null}) + */ + default Attributes getRuntimeAttributes() { + return Attributes.EMPTY; + } + + /** + * The empty authorization identity. + */ + AuthorizationIdentity EMPTY = basicIdentity(Attributes.EMPTY); + + /** + * Create a basic authorization identity implementation. + * + * @param attributes the identity attributes + * @return the authorization identity + */ + static AuthorizationIdentity basicIdentity(Attributes attributes) { + return basicIdentity(() -> attributes, "EMPTY"); + } + + /** + * Create a basic authorization identity implementation. + * + * @param attributes the identity attributes + * @return the authorization identity + */ + static AuthorizationIdentity basicIdentity(Supplier attributes, final String string) { + return new AuthorizationIdentity() { + + public Attributes getAttributes() { + return attributes.get(); + } + + @Override + public String toString() { + return string; + } + + }; + } + + /** + * Create a basic authorization identity implementation using the given attributes and runtime attributes. + * + * @param attributes the attributes + * @param runtimeAttributes the runtime attributes + * @return the authorization identity + */ + static AuthorizationIdentity basicIdentity(Supplier attributes, Supplier runtimeAttributes, final String string) { + return new AuthorizationIdentity() { + + public Attributes getAttributes() { + return attributes.get(); + } + + public Attributes getRuntimeAttributes() { + return runtimeAttributes.get(); + } + + @Override + public String toString() { + return string; + } + + }; + } + + /** + * Create a basic authorization identity implementation using the given authorization + * identity and runtime attributes. + * + * @param authorizationIdentity the authorization identity + * @param runtimeAttributes the identity runtime attributes + * @return the authorization identity + */ + static AuthorizationIdentity basicIdentity(AuthorizationIdentity authorizationIdentity, Attributes runtimeAttributes) { + Attributes attributes = authorizationIdentity.getAttributes(); + Attributes combinedRuntimeAttributes = AggregateAttributes.aggregateOf(authorizationIdentity.getRuntimeAttributes(), runtimeAttributes); + return basicIdentity(() -> attributes, () -> combinedRuntimeAttributes, "EMPTY"); + } + +} Index: 3rdParty_sources/elytron/org/wildfly/security/authz/DifferenceRoles.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/authz/DifferenceRoles.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/authz/DifferenceRoles.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,77 @@ +/* + * 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.authz; + +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.Spliterator; +import java.util.Spliterators; + +class DifferenceRoles implements Roles { + + private final Roles left; + private final Roles right; + + DifferenceRoles(final Roles left, final Roles right) { + this.left = left; + this.right = right; + } + + public boolean contains(final String roleName) { + return left.contains(roleName) && ! right.contains(roleName); + } + + public Iterator iterator() { + final Iterator leftIterator = left.iterator(); + return new Iterator() { + String next; + + public boolean hasNext() { + if (next != null) { + return true; + } + for (;;) { + if (leftIterator.hasNext()) { + next = leftIterator.next(); + if (! right.contains(next)) { + return true; + } + next = null; + // fall out and re-loop + } else { + return false; + } + } + } + + public String next() { + if (! hasNext()) { + throw new NoSuchElementException(); + } + final String next = this.next; + this.next = null; + return next; + } + }; + } + + public Spliterator spliterator() { + return Spliterators.spliteratorUnknownSize(iterator(), Spliterator.NONNULL | Spliterator.DISTINCT); + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/authz/DisjunctionRoles.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/authz/DisjunctionRoles.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/authz/DisjunctionRoles.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,89 @@ +/* + * 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.authz; + +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.Spliterator; +import java.util.Spliterators; + +class DisjunctionRoles implements Roles { + + private final Roles left; + private final Roles right; + + DisjunctionRoles(final Roles left, final Roles right) { + this.left = left; + this.right = right; + } + + public boolean contains(final String roleName) { + return left.contains(roleName) != right.contains(roleName); + } + + public boolean isEmpty() { + return ! iterator().hasNext(); + } + + public Iterator iterator() { + final Iterator leftIterator = left.iterator(); + final Iterator rightIterator = right.iterator(); + return new Iterator() { + String next; + + public boolean hasNext() { + if (next != null) { + return true; + } + for (;;) { + if (leftIterator.hasNext()) { + next = leftIterator.next(); + if (! right.contains(next)) { + return true; + } + next = null; + // fall out and re-loop + } else if (rightIterator.hasNext()) { + next = rightIterator.next(); + if (! left.contains(next)) { + return true; + } + next = null; + // fall out and re-loop + } else { + return false; + } + } + } + + public String next() { + if (! hasNext()) { + throw new NoSuchElementException(); + } + final String next = this.next; + this.next = null; + return next; + } + }; + } + + public Spliterator spliterator() { + return Spliterators.spliteratorUnknownSize(iterator(), Spliterator.NONNULL | Spliterator.DISTINCT); + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/authz/IntersectionRoles.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/authz/IntersectionRoles.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/authz/IntersectionRoles.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,75 @@ +/* + * 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.authz; + +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.Spliterator; +import java.util.Spliterators; + +class IntersectionRoles implements Roles { + + private final Roles left; + private final Roles right; + + IntersectionRoles(final Roles left, final Roles right) { + this.left = left; + this.right = right; + } + + public boolean contains(final String roleName) { + return left.contains(roleName) && right.contains(roleName); + } + + public Iterator iterator() { + final Iterator iterator = left.iterator(); + return new Iterator() { + String next; + + public boolean hasNext() { + if (next != null) { + return true; + } + for (;;) { + if (! iterator.hasNext()) { + return false; + } + next = iterator.next(); + if (right.contains(next)) { + return true; + } + next = null; + } + } + + public String next() { + if (! hasNext()) { + throw new NoSuchElementException(); + } + final String next = this.next; + this.next = null; + return next; + } + }; + } + + public Spliterator spliterator() { + return Spliterators.spliteratorUnknownSize(iterator(), Spliterator.NONNULL | Spliterator.DISTINCT); + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/authz/MapAttributes.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/authz/MapAttributes.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/authz/MapAttributes.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,356 @@ +/* + * 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.authz; + +import java.util.AbstractCollection; +import java.util.ArrayList; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import org.wildfly.common.Assert; + +/** + * A map-backed attributes collection. + * + * @author David M. Lloyd + */ +public class MapAttributes implements Attributes { + // backing map + + private final Map map; + + // cache fields + + private HashMap entryCache; + private Collection entries; + private Collection values; + + /** + * Construct an instance using a hash map for backing store. + */ + public MapAttributes() { + this.map = new HashMap<>(); + } + + /** + * Construct a new instance copying mappings from an original map. + * + * @param original the original map + */ + public MapAttributes(Map> original) { + Assert.checkNotNullParam("original", original); + Map map = new HashMap<>(original.size()); + for (Map.Entry> entry : original.entrySet()) { + map.put(entry.getKey(), new EntriesList(entry.getValue())); + } + this.map = map; + } + + /** + * Construct a new instance copying mappings from an original attributes collection. + * + * @param original the original collection + */ + public MapAttributes(Attributes original) { + Assert.checkNotNullParam("original", original); + Map map = new HashMap<>(original.size()); + for (Entry entry : original.entries()) { + final EntriesList entriesList = new EntriesList(entry); + if (! entriesList.isEmpty()) map.put(entry.getKey(), entriesList); + } + this.map = map; + } + + public Set keySet() { + return map.keySet(); + } + + public Collection values() { + final Collection values = this.values; + if (values != null) { + return values; + } + return this.values = Attributes.super.values(); + } + + public Collection entries() { + final Collection entries = this.entries; + if (entries != null) { + return entries; + } + return this.entries = new AbstractCollection() { + public Iterator iterator() { + final Iterator iterator = map.keySet().iterator(); + return new Iterator() { + + public boolean hasNext() { + return iterator.hasNext(); + } + + public Entry next() { + return get(iterator.next()); + } + + public void remove() { + iterator.remove(); + } + }; + } + + public int size() { + return MapAttributes.this.size(); + } + }; + } + + public int size(final String key) { + final EntriesList list = map.get(key); + return list == null ? 0 : list.size(); + } + + public boolean remove(final String key) { + return map.remove(key) != null; + } + + public void add(final String key, final int idx, final String value) { + EntriesList list = map.get(key); + if (list == null) { + map.put(key, list = new EntriesList()); + } + list.add(idx, value); + } + + public String get(final String key, final int idx) { + EntriesList list = map.get(key); + return list == null ? null : list.get(idx); + } + + public String set(final String key, final int idx, final String value) { + EntriesList list = map.get(key); + if (list == null) { + throw new IndexOutOfBoundsException(); + } + return list.set(idx, value); + } + + public String remove(final String key, final int idx) { + EntriesList list = map.get(key); + if (list == null) { + throw new IndexOutOfBoundsException(); + } + final String result = list.remove(idx); + if (list.isEmpty()) { + map.remove(key); + } + return result; + } + + public List copyAndRemove(final String key) { + final EntriesList old = map.remove(key); + return old == null ? new ArrayList<>(0) : old; + } + + public List copyAndReplace(final String key, final Collection values) { + final EntriesList old = map.replace(key, new EntriesList(values)); + return old == null ? new ArrayList<>(0) : old; + } + + public boolean containsKey(final String key) { + return map.containsKey(key); + } + + public boolean containsValue(final String key, final String value) { + final EntriesList list = map.get(key); + return list != null && list.contains(value); + } + + public void removeRange(final String key, final int from, final int to) { + final EntriesList list = map.get(key); + if (list == null) { + throw new IndexOutOfBoundsException(); + } + list.removeRange(from, to); + } + + public int indexOf(final String key, final String value) { + EntriesList list = map.get(key); + return list == null ? -1 : list.indexOf(value); + } + + public int lastIndexOf(final String key, final String value) { + EntriesList list = map.get(key); + return list == null ? -1 : list.lastIndexOf(value); + } + + public boolean set(final String key, final int idx, final String expect, final String update) { + EntriesList list = map.get(key); + if (list == null || !list.get(idx).equals(expect)) { + return false; + } + list.set(idx, update); + return true; + } + + public String getFirst(final String key) { + EntriesList list = map.get(key); + return list == null ? null : list.get(0); + } + + public String getLast(final String key) { + EntriesList list = map.get(key); + return list == null ? null : list.get(list.size() - 1); + } + + public void addFirst(final String key, final String value) { + EntriesList list = map.get(key); + if (list == null) { + map.put(key, list = new EntriesList()); + } + list.add(0, value); + } + + public void addLast(final String key, final String value) { + EntriesList list = map.get(key); + if (list == null) { + map.put(key, list = new EntriesList()); + } + list.add(value); + } + + public boolean removeFirst(final String key, final String value) { + EntriesList list = map.get(key); + if (list == null) { + return false; + } + int idx = list.indexOf(value); + if (idx == -1) { + return false; + } + list.remove(idx); + if (list.isEmpty()) { + map.remove(key); + } + return true; + } + + public boolean removeLast(final String key, final String value) { + EntriesList list = map.get(key); + if (list == null) { + return false; + } + int idx = list.lastIndexOf(value); + if (idx == -1) { + return false; + } + list.remove(idx); + if (list.isEmpty()) { + map.remove(key); + } + return true; + } + + public String removeFirst(final String key) { + EntriesList list = map.get(key); + if (list == null) { + return null; + } + final String removed = list.remove(0); + if (list.isEmpty()) { + map.remove(key); + } + return removed; + } + + public String removeLast(final String key) { + EntriesList list = map.get(key); + if (list == null) { + return null; + } + final String removed = list.remove(list.size() - 1); + if (list.isEmpty()) { + map.remove(key); + } + return removed; + } + + public boolean remove(final String key, final int idx, final String value) { + EntriesList list = map.get(key); + if (list == null) { + return false; + } + if (! list.get(idx).equals(value)) { + return false; + } + list.remove(idx); + if (list.isEmpty()) { + map.remove(key); + } + return true; + } + + public boolean removeAll(final String key, final String value) { + EntriesList list = map.get(key); + return list != null && list.removeAll(Collections.singleton(value)); + } + + public Entry get(final String key) { + HashMap entryCache = this.entryCache; + if (entryCache == null) { + entryCache = this.entryCache = new HashMap<>(); + } + return entryCache.computeIfAbsent(key, s -> new SimpleAttributesEntry(this, s)); + } + + public int size() { + return map.size(); + } + + public boolean isEmpty() { + return map.isEmpty(); + } + + public void clear() { + map.clear(); + } + + static final class EntriesList extends ArrayList implements Set { + + private static final long serialVersionUID = 8113580421577650775L; + + EntriesList(final int initialCapacity) { + super(initialCapacity); + } + + EntriesList() { + } + + EntriesList(final Collection c) { + super(c); + } + + public void removeRange(final int fromIndex, final int toIndex) { + super.removeRange(fromIndex, toIndex); + } + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/authz/MappedRoleMapper.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/authz/MappedRoleMapper.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/authz/MappedRoleMapper.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,141 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2018 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.authz; + +import java.util.LinkedHashMap; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Set; + +import static org.wildfly.security.auth.server._private.ElytronMessages.log; + +/** + * A simple mapping role mapper. + * + * Maps each role to a set of new roles using a String to Set map. + * + * @author Martin Mazanek + */ +public class MappedRoleMapper implements RoleMapper { + + private final Map> reverseRoleMap = new LinkedHashMap<>(); + private volatile boolean initialized = false; + + /** + * Construct a new instance. + * Called from WildFly core when using this as a custom component. You should not use this constructor and use {@link MappedRoleMapper.Builder} instead. + * You must call {@link #initialize(Map)} to configure mapping map before usage. + * + * @see MappedRoleMapper.Builder + */ + public MappedRoleMapper() {} + + + private MappedRoleMapper(Map> roleMap) { + Set>> entrySet = roleMap.entrySet(); + + for (Map.Entry> entry : entrySet) { + + for (String mappedRole : entry.getValue()) { + Set rolesToMappedRole = reverseRoleMap.get(mappedRole); + + if (rolesToMappedRole == null) { + rolesToMappedRole = new LinkedHashSet<>(); + reverseRoleMap.put(mappedRole, rolesToMappedRole); + } + + rolesToMappedRole.add(entry.getKey()); + } + } + + initialized = true; + } + + /** + * Custom component method. + * Called from WildFly core. Used to include mapped role mapping functionality in older WildFly versions. + * + * @param configuration map of mapping rules where key is delegate role and value is whitespace separated list of new roles + * @throws IllegalStateException when called mapper is already initialized + */ + public void initialize(final Map configuration) { + if (initialized) { + throw log.roleMappedAlreadyInitialized(); + } + reverseRoleMap.clear(); + configuration.forEach( (key, value) -> { + String[] newRoles = value.split("\\s+"); + for (String newRole : newRoles) { + Set rolesToMappedRole = reverseRoleMap.get(newRole); + if (rolesToMappedRole == null) { + rolesToMappedRole = new LinkedHashSet<>(); + reverseRoleMap.put(newRole, rolesToMappedRole); + } + rolesToMappedRole.add(key); + } + }); + initialized = true; + } + + @Override + public Roles mapRoles(Roles rolesToMap) { + if (!initialized) { + throw log.roleMappedNotInitialized(); + } + return new MappedRoles(rolesToMap, this.reverseRoleMap); + } + + /** + * Construct a new {@link Builder} for creating the {@link MappedRoleMapper}. + * + * @return a new {@link Builder} for creating the {@link MappedRoleMapper}. + */ + public static Builder builder() { + return new Builder(); + } + + /** + * A builder for map backed role mappers. + */ + public static class Builder { + private Map> roleMap; + + Builder() { + } + + /** + * Build and return the resulting {@link MappedRoleMapper}. + * + * @return the resulting {@link MappedRoleMapper} + */ + public MappedRoleMapper build() { + return new MappedRoleMapper(roleMap); + } + + /** + * Set the {@link Map} to use for mapping roles + * + * @param roleMap the role map + * @return {@code this} builder to allow chaining. + */ + public Builder setRoleMap(Map> roleMap) { + this.roleMap = roleMap; + return this; + } + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/authz/MappedRoles.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/authz/MappedRoles.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/authz/MappedRoles.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,75 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2018 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.authz; + +import java.util.Iterator; +import java.util.Map; +import java.util.NoSuchElementException; +import java.util.Set; + + +class MappedRoles implements Roles { + private final Roles delegate; + private final Map> reverseRoleMap; + + public MappedRoles(final Roles delegate, final Map> reverseRoleMap) { + this.delegate = delegate; + this.reverseRoleMap = reverseRoleMap; + } + + @Override + public boolean contains(String roleName) { + Set rolesToContain = reverseRoleMap.get(roleName); + if (rolesToContain == null) return false; + return delegate.containsAny(rolesToContain); + } + + @Override + public Iterator iterator() { + final Iterator iterator = reverseRoleMap.keySet().iterator(); + + return new Iterator() { + String next = null; + + @Override + public boolean hasNext() { + if (next != null) return true; + + while (iterator.hasNext()) { + String nextt = iterator.next(); + if (delegate.containsAny(reverseRoleMap.get(nextt))) { + next = nextt; + return true; + } + } + + return false; + } + + @Override + public String next() { + if (! hasNext()) { + throw new NoSuchElementException(); + } + final String next = this.next; + this.next = null; + return next; + } + }; + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/authz/OneRole.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/authz/OneRole.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/authz/OneRole.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,69 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2016 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.authz; + +import java.util.Iterator; +import java.util.Spliterator; +import java.util.Spliterators; + +import org.wildfly.security.util.EnumerationIterator; + +final class OneRole implements Roles { + private final String role; + + OneRole(final String role) { + this.role = role; + } + + public boolean contains(final String roleName) { + return role.equals(roleName); + } + + public boolean isEmpty() { + return false; + } + + public Spliterator spliterator() { + return Spliterators.spliterator(iterator(), 1, Spliterator.NONNULL | Spliterator.DISTINCT | Spliterator.SIZED); + } + + public Roles and(final Roles other) { + return other.contains(role) ? this : NONE; + } + + public Roles or(final Roles other) { + return other.contains(role) ? other : Roles.super.or(other); + } + + public Roles minus(final Roles other) { + return other.contains(role) ? NONE : this; + } + + public Roles addSuffix(final String suffix) { + return new OneRole(role + suffix); + } + + public Roles addPrefix(final String prefix) { + return new OneRole(prefix + role); + } + + public Iterator iterator() { + return EnumerationIterator.over(role); + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/authz/PermissionMappable.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/authz/PermissionMappable.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/authz/PermissionMappable.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,68 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2016 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.authz; + +import java.security.Principal; +import java.time.Instant; + +import org.wildfly.security.auth.principal.AnonymousPrincipal; +import org.wildfly.security.auth.server.IdentityCredentials; + +/** + * An entity to which permissions can be mapped. + * + * @author David M. Lloyd + */ +public interface PermissionMappable { + /** + * Get the attributes of this entity. + * + * @return the attributes of this entity (must not be {@code null}) + */ + default Attributes getAttributes() { + return Attributes.EMPTY; + } + + /** + * Get the principal of this entity. + * + * @return the principal of this entity (must not be {@code null}) + */ + default Principal getPrincipal() { + return AnonymousPrincipal.getInstance(); + } + + /** + * Get the creation time of this entity (if known). + * + * @return the creation time of this entity, or {@code null} if it cannot be determined + */ + default Instant getCreationTime() { + return null; + } + + /** + * Get the public credentials of this entity. + * + * @return the public credentials (must not be {@code null}) + */ + default IdentityCredentials getPublicCredentials() { + return IdentityCredentials.NONE; + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/authz/PermissionMapper.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/authz/PermissionMapper.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/authz/PermissionMapper.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,104 @@ +/* + * JBoss, Home of Professional Open Source + * + * Copyright 2015 Red Hat, Inc. and/or its affiliates. + * + * 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.authz; + +import java.security.Permission; + +import org.wildfly.security.auth.server.SecurityDomain; +import org.wildfly.security.permission.PermissionVerifier; + +/** + * A permission mapper is responsible to enable permission mapping to a {@link SecurityDomain} + * in order to obtain and check permissions based on an previously authorized identity and any other authorization information (eg.: roles) + * associated with it. + * + * @author Pedro Igor + */ +@FunctionalInterface +public interface PermissionMapper { + + /** + *

Returns a {@link PermissionVerifier} with all the permissions associated with the given information. + * + *

Once returned, client code can use the {@link PermissionVerifier#implies(Permission)} to check if a given permission is granted or not + * to the given principal. Implementors must make sure that the returned collection is immutable. + * + * @param permissionMappable the object to which permissions can be mapped (must not be {@code null}) + * @param roles a set of effective roles after all role mapping was applied by security domain (may be {@code null}) + * @return a permission verifier (not {@code null}) + */ + PermissionVerifier mapPermissions(PermissionMappable permissionMappable, Roles roles); + + /** + * Returns a new mapper where the {@link PermissionVerifier} created by this {@link PermissionMapper} is combined with the + * {@code PermissionVerifier} of the {@code other} {@code PermissionMapper} using 'and'. + * + * @param other the other {@link PermissionMapper} to combine with this {@link PermissionMapper} + * @return the combined {@link PermissionMapper} + */ + default PermissionMapper and(final PermissionMapper other) { + return (p, r) -> mapPermissions(p, r).and(other.mapPermissions(p, r)); + } + + /** + * Returns a new mapper where the {@link PermissionVerifier} created by this {@link PermissionMapper} is combined with the + * {@code PermissionVerifier} of the {@code other} {@code PermissionMapper} using 'or'. + * + * @param other the other {@link PermissionMapper} to combine with this {@link PermissionMapper} + * @return the combined {@link PermissionMapper} + */ + default PermissionMapper or(final PermissionMapper other) { + return (p, r) -> mapPermissions(p, r).or(other.mapPermissions(p, r)); + } + + /** + * Returns a new mapper where the {@link PermissionVerifier} created by this {@link PermissionMapper} is combined with the + * {@code PermissionVerifier} of the {@code other} {@code PermissionMapper} using 'xor'. + * + * @param other the other {@link PermissionMapper} to combine with this {@link PermissionMapper} + * @return the combined {@link PermissionMapper} + */ + default PermissionMapper xor(final PermissionMapper other) { + return (p, r) -> mapPermissions(p, r).xor(other.mapPermissions(p, r)); + } + + /** + * Returns a new mapper where the {@link PermissionVerifier} created by this {@link PermissionMapper} is combined with the + * {@code PermissionVerifier} of the {@code other} {@code PermissionMapper} using 'unless'. + * + * @param other the other {@link PermissionMapper} to combine with this {@link PermissionMapper} + * @return the combined {@link PermissionMapper} + */ + default PermissionMapper unless(final PermissionMapper other) { + return (p, r) -> mapPermissions(p, r).unless(other.mapPermissions(p, r)); + } + + /** + * Returns a new mapper that maps all to pre-defined {@link PermissionVerifier} instance. + * @param verifier the {@link PermissionVerifier} that will be returned for anybody. + * @return the constant {@link PermissionVerifier} + */ + static PermissionMapper createConstant(PermissionVerifier verifier) { + return (p, r) -> verifier; + } + + /** + * A default implementation that does nothing but returns an empty and read-only {@link PermissionVerifier}. + */ + PermissionMapper EMPTY_PERMISSION_MAPPER = (permissionMappable, roles) -> PermissionVerifier.NONE; +} Index: 3rdParty_sources/elytron/org/wildfly/security/authz/RegexRoleMapper.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/authz/RegexRoleMapper.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/authz/RegexRoleMapper.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,106 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2019 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.authz; + +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; + +import static org.wildfly.common.Assert.checkNotNullParam; +import static org.wildfly.security.auth.server._private.ElytronMessages.log; + +/** + * A pattern role mapper. + *

+ * Role mapper that maps roles that contain Pattern with replacement. Pattern can capture groups that replacement string can use. + * Can be used to replace all occurrences of the pattern in role or only the first occurrence. + * + * @author Diana Vilkolakova + */ +public class RegexRoleMapper implements RoleMapper { + + private Pattern pattern; + private String replacement; + private boolean keepNonMapped; + private boolean replaceAll; + + private RegexRoleMapper(Builder builder) { + checkNotNullParam("pattern", builder.pattern); + checkNotNullParam("replacement", builder.replacement); + if (builder.pattern.length() < 1) { + throw log.invalidPatternInRegexRoleMapper(); + } + if (builder.replacement.length() < 1) { + throw log.invalidReplacementInRegexRoleMapper(); + } + try { + this.pattern = Pattern.compile(builder.pattern); + } catch (PatternSyntaxException ex) { + throw log.invalidPatternInRegexRoleMapper(); + } + this.replacement = builder.replacement; + this.keepNonMapped = builder.keepNonMapped; + this.replaceAll = builder.replaceAll; + } + + @Override + public Roles mapRoles(Roles rolesToMap) { + return new RegexRoles(rolesToMap, this.pattern, this.replacement, this.keepNonMapped, this.replaceAll); + } + + /** + * Construct a new {@link Builder} for creating the {@link RegexRoleMapper}. + * + * @return a new {@link Builder} for creating the {@link RegexRoleMapper}. + */ + public static class Builder { + private String pattern; + private String replacement; + private boolean keepNonMapped = true; + private boolean replaceAll = false; + + public RegexRoleMapper build() { + return new RegexRoleMapper(this); + } + + public RegexRoleMapper.Builder setPattern(String pattern) { + checkNotNullParam("pattern", pattern); + this.pattern = pattern; + return this; + } + + public RegexRoleMapper.Builder setReplacement(String replacement) { + checkNotNullParam("replacement", replacement); + this.replacement = replacement; + return this; + } + + public RegexRoleMapper.Builder setKeepNonMapped(boolean keepNonMapped) { + this.keepNonMapped = keepNonMapped; + return this; + } + + /** + * @param replaceAll if true replaces all occurrences of pattern in role. If false replaces only the first occurrence. + * @return builder + */ + public RegexRoleMapper.Builder setReplaceAll(boolean replaceAll) { + this.replaceAll = replaceAll; + return this; + } + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/authz/RegexRoles.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/authz/RegexRoles.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/authz/RegexRoles.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,102 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2019 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.authz; + +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.regex.Pattern; +import java.util.regex.PatternSyntaxException; +import java.util.stream.StreamSupport; + +import static org.wildfly.security.auth.server._private.ElytronMessages.log; + +/** + * A regex roles. + *

+ * Roles that are checked against pattern. Roles that contain the pattern are then replaced using the replacement. Pattern can capture groups that replacement can make use of. + * It is possible to replace all occurrences or only the first occurrence of the pattern in role. + * + * @author Diana Vilkolakova + */ +class RegexRoles implements Roles { + private final Roles delegate; + private final Pattern pattern; + private final String replace; + private final boolean keepNonMapped; + private final boolean replaceAll; + + RegexRoles(final Roles delegate, Pattern pattern, final String replace, final boolean keepNonMapped, final boolean replaceAll) { + this.delegate = delegate; + this.pattern = pattern; + this.replace = replace; + this.keepNonMapped = keepNonMapped; + this.replaceAll = replaceAll; + } + + public boolean contains(final String roleName) { + try { + return StreamSupport.stream(delegate.spliterator(), false).anyMatch(role -> + { + String pattern = this.pattern.pattern(); + boolean containsRegex = this.pattern.matcher(role).find(); + boolean containsRegexAndRoleExists = containsRegex && + (replaceAll ? role.replaceAll(pattern, replace).equals(roleName) : role.replaceFirst(pattern, replace).equals(roleName)); + boolean doesNotContainRegexButRoleExists = !containsRegex && keepNonMapped && role.equals(roleName); + return containsRegexAndRoleExists || doesNotContainRegexButRoleExists; + }); + } catch (PatternSyntaxException | IndexOutOfBoundsException ex) { + throw log.invalidReplacementInRegexRoleMapper(); + } + } + + @Override + public Iterator iterator() { + final Iterator iterator = delegate.iterator(); + + return new Iterator() { + String next = null; + + @Override + public boolean hasNext() { + if (next != null) return true; + + while (iterator.hasNext()) { + String nextt = iterator.next(); + if (pattern.matcher(nextt).find()) { + next = replaceAll ? nextt.replaceAll(pattern.pattern(), replace) : nextt.replaceFirst(pattern.pattern(), replace); + return true; + } else if (keepNonMapped) { + next = nextt; + return true; + } + } + return false; + } + + @Override + public String next() { + if (!hasNext()) { + throw new NoSuchElementException(); + } + final String next = this.next; + this.next = null; + return next; + } + }; + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/authz/RoleDecoder.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/authz/RoleDecoder.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/authz/RoleDecoder.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,92 @@ +/* + * 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.authz; + +import static org.wildfly.common.Assert.checkNotNullParam; + +import java.util.HashSet; + +/** + * A decoder to extract role information from an identity's attributes. + * + * @author David M. Lloyd + */ +@FunctionalInterface +public interface RoleDecoder { + + /** + * A key whose value is the string "Roles", to provide a standard/default location at which roles may be found. + */ + String KEY_ROLES = "Roles"; + + /** + * A key whose value is the string "Source-Address". This is where the IP address of a remote + * client may be found. + */ + String KEY_SOURCE_ADDRESS = "Source-Address"; + + /** + * Decode the role set from the given authorization identity. + * + * @param authorizationIdentity the authorization identity (not {@code null}) + * @return the role set (must not be {@code null}) + */ + Roles decodeRoles(AuthorizationIdentity authorizationIdentity); + + /** + * A role decoder which decodes no roles. + */ + RoleDecoder EMPTY = attributes -> Roles.NONE; + + /** + * A role decoder which always decodes roles from the attribute called "Roles". + */ + RoleDecoder DEFAULT = simple(KEY_ROLES); + + /** + * Create a simple role decoder which returns the values of the given attribute. + * + * @param attribute the attribute + * @return the roles + */ + static RoleDecoder simple(String attribute) { + return identity -> { + final Attributes.Entry entry = identity.getAttributes().get(attribute); + return entry.isEmpty() ? Roles.NONE : entry instanceof Attributes.SetEntry ? Roles.fromSet((Attributes.SetEntry) entry) : Roles.fromSet(new HashSet<>(entry)); + }; + } + + /** + * Create an aggregate role decoder. Each role decoder is applied in order and the returned value is + * a union of the roles returned by each decoder. + * + * @param decoders the role decoders to apply (must not be {@code null} or contain {@code null} elements) + * @return the aggregate role decoder (not {@code null}) + */ + static RoleDecoder aggregate(RoleDecoder... decoders) { + checkNotNullParam("decoders", decoders); + return identity -> { + Roles combinedRoles = Roles.NONE; + for (RoleDecoder decoder : decoders) { + combinedRoles = combinedRoles.or(decoder.decodeRoles(identity)); + } + return combinedRoles; + }; + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/authz/RoleMapper.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/authz/RoleMapper.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/authz/RoleMapper.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,134 @@ +/* + * JBoss, Home of Professional Open Source + * + * Copyright 2013 Red Hat, Inc. and/or its affiliates. + * + * 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.authz; + +import org.wildfly.common.Assert; + +/** + * A role mapper is responsible for mapping roles based on their raw form. + *

+ * Roles are basically represented as {@link String} values, where these values are their names. Role mapping allows to transform roles + * from their raw form (eg.: just like they were loaded from a identity store such as a database or LDAP server) in a more consistent + * form. + * + * @author Pedro Igor + */ +@FunctionalInterface +public interface RoleMapper { + + /** + * Returns a set of strings representing the roles mapped from the given roles in their raw form. + * + * @param rolesToMap the roles in their raw form to apply mapping + * @return the mapped role set + */ + Roles mapRoles(Roles rolesToMap); + + /** + * A default implementation that does nothing but return the given roles. + */ + RoleMapper IDENTITY_ROLE_MAPPER = rolesToMap -> rolesToMap; + + /** + * Create a role mapper which is the intersection (logical "and") of the results of this and the given role mapper. + * + * @param other the other role mapper + * @return the intersection role mapper + */ + default RoleMapper and(RoleMapper other) { + Assert.checkNotNullParam("other", other); + return rolesToMap -> mapRoles(rolesToMap).and(other.mapRoles(rolesToMap)); + } + + /** + * Create a role mapper which is the union (logical "or") of the results of this and the given role mapper. + * + * @param other the other role mapper + * @return the union role mapper + */ + default RoleMapper or(RoleMapper other) { + Assert.checkNotNullParam("other", other); + return rolesToMap -> mapRoles(rolesToMap).or(other.mapRoles(rolesToMap)); + } + + /** + * Create a role mapper which is the symmetric difference (or disjunction, or logical "xor") of the results of this + * and the given role mapper. + * + * @param other the other role mapper + * @return the difference role mapper + */ + default RoleMapper xor(RoleMapper other) { + Assert.checkNotNullParam("other", other); + return rolesToMap -> mapRoles(rolesToMap).xor(other.mapRoles(rolesToMap)); + } + + /** + * Create a role mapper which contains all the roles mapped by this mapper, minus the roles mapped by the given + * role mapper. + * + * @param other the other role mapper + * @return the difference role mapper + */ + default RoleMapper minus(RoleMapper other) { + Assert.checkNotNullParam("other", other); + return rolesToMap -> mapRoles(rolesToMap).minus(other.mapRoles(rolesToMap)); + } + + /** + * Create an aggregate role mapper. Each role mapper is applied in order. + * + * @param mapper1 the first role mapper to apply (must not be {@code null}) + * @param mapper2 the second role mapper to apply (must not be {@code null}) + * @return the aggregate role mapper (not {@code null}) + */ + static RoleMapper aggregate(RoleMapper mapper1, RoleMapper mapper2) { + Assert.checkNotNullParam("mapper1", mapper1); + Assert.checkNotNullParam("mapper2", mapper2); + return rolesToMap -> mapper2.mapRoles(mapper1.mapRoles(rolesToMap)); + } + + /** + * Create an aggregate role mapper. Each role mapper is applied in order. + * + * @param mappers the role mappers to apply (most not be {@code null} or contain {@code null} elements) + * @return the aggregate role mapper (not {@code null}) + */ + static RoleMapper aggregate(RoleMapper... mappers) { + Assert.checkNotNullParam("mappers", mappers); + final RoleMapper[] clone = mappers.clone(); + for (int i = 0; i < clone.length; i++) { + Assert.checkNotNullArrayParam("mappers", i, clone[i]); + } + return rolesToMap -> { + for (RoleMapper r : clone) rolesToMap = r.mapRoles(rolesToMap); + return rolesToMap; + }; + } + + /** + * Create a role mapper that always returns the same set of roles regardless of the input. + * + * @param roles the set of roles to always be returned (must not be {@code null}) + * @return the constant role mapper (not {@code null}) + */ + static RoleMapper constant(Roles roles) { + Assert.checkNotNullParam("roles", roles); + return rolesToMap -> roles; + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/authz/Roles.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/authz/Roles.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/authz/Roles.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,285 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2016 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.authz; + +import static org.wildfly.common.Assert.checkNotNullParam; +import static org.wildfly.common.Assert.checkNotEmptyParam; + +import java.util.Collections; +import java.util.Iterator; +import java.util.Set; +import java.util.Spliterator; +import java.util.Spliterators; +import java.util.TreeSet; +import java.util.function.Consumer; + +import org.wildfly.common.Assert; + +/** + * A collection of roles. + * + * @author David M. Lloyd + */ +public interface Roles extends Iterable { + + /** + * Determine if this collection contains the given role name. + * + * @param roleName the role name + * @return {@code true} if the role is contained in this collection, {@code false} otherwise + */ + boolean contains(String roleName); + + /** + * Determine if this collection contains any of the given role names. + * + * @param desiredRoles the roles to check. + * @return {@code true} if this collection contains any of the desired roles, {@code false} otherwise. + */ + default boolean containsAny(Set desiredRoles) { + checkNotNullParam("desiredRoles", desiredRoles); + for (String current : desiredRoles) { + if (contains(current)) { + return true; + } + } + return false; + } + + /** + * Determine if this collection contains all of the given role names. + * + * @param desiredRoles the roles to check. + * @return {@code true} if this collection contains all of the desired roles, {@code false} otherwise. + */ + default boolean containsAll(Set desiredRoles) { + checkNotNullParam("desiredRoles", desiredRoles); + checkNotEmptyParam("desiredRoles", desiredRoles); + for (String current : desiredRoles) { + if (contains(current) == false) { + return false; + } + } + + return true; + } + + /** + * Determine whether this roles collection is empty. + * + * @return {@code true} if the collection is empty, {@code false} otherwise + */ + default boolean isEmpty() { + return ! iterator().hasNext(); + } + + /** + * Create a {@link Spliterator} over this roles collection. + * + * @return the spliterator (not {@code null}) + */ + default Spliterator spliterator() { + return Spliterators.spliteratorUnknownSize(iterator(), Spliterator.NONNULL | Spliterator.DISTINCT); + } + + /** + * Construct a new roles collection from a set. + * + * @param set the set of role names (must not be {@code null}) + * @return the roles collection (not {@code null}) + */ + static Roles fromSet(Set set) { + Assert.checkNotNullParam("set", set); + if (set instanceof Roles) { + return (Roles) set; + } + return new Roles() { + public boolean contains(final String roleName) { + return set.contains(roleName); + } + + public Iterator iterator() { + return set.iterator(); + } + + public Spliterator spliterator() { + return set.spliterator(); + } + + public void forEach(final Consumer action) { + set.forEach(action); + } + + public boolean isEmpty() { + return set.isEmpty(); + } + }; + } + + /** + * Returns a set (immutable) containing roles from a roles collection. + * + * @param roles collection (not {@code null}) + * @return the set of role names (must not be {@code null}) + */ + static Set toSet(Roles roles) { + Assert.checkNotNullParam("roles", roles); + Iterator iterator = roles.iterator(); + if (!iterator.hasNext()) { + return Collections.emptySet(); + } + String role = iterator.next(); + if (!iterator.hasNext()) { + return Collections.singleton(role); + } + Set result = new TreeSet<>(); + result.add(role); + while (iterator.hasNext()) { + result.add(iterator.next()); + } + return Collections.unmodifiableSet(result); + } + + /** + * Construct a role set consisting of a single role. + * + * @param role the role name (must not be {@code null}) + * @return the role set (not {@code null}) + */ + static Roles of(String role) { + Assert.checkNotNullParam("role", role); + return new OneRole(role); + } + + /** + * Get the intersection of this collection and another. + * + * @param other the other roles collection (must not be {@code null}) + * @return the intersection (not {@code null}) + */ + default Roles and(Roles other) { + Assert.checkNotNullParam("other", other); + return isEmpty() || other.isEmpty() ? NONE : new IntersectionRoles(this, other); + } + + /** + * Get the union of this collection and another. + * + * @param other the other roles collection (must not be {@code null}) + * @return the union (not {@code null}) + */ + default Roles or(Roles other) { + Assert.checkNotNullParam("other", other); + return isEmpty() ? other : other.isEmpty() ? this : new UnionRoles(this, other); + } + + /** + * Get the disjunction of this collection and another. + * + * @param other the other roles collection (must not be {@code null}) + * @return the disjunction (not {@code null}) + */ + default Roles xor(Roles other) { + Assert.checkNotNullParam("other", other); + return isEmpty() ? other : other.isEmpty() ? this : new DisjunctionRoles(this, other); + } + + /** + * Get a roles collection which consists of the roles in this collection minus the roles in the other collection. + * + * @param other the other collection (must not be {@code null}) + * @return the difference (not {@code null}) + */ + default Roles minus(Roles other) { + Assert.checkNotNullParam("other", other); + return isEmpty() ? NONE : other.isEmpty() ? this : new DifferenceRoles(this, other); + } + + /** + * Get a roles collection which adds a suffix to all role names. + * + * @param suffix the suffix to add (must not be {@code null}) + * @return the new roles collection (not {@code null}) + */ + default Roles addSuffix(String suffix) { + Assert.checkNotNullParam("suffix", suffix); + return suffix.isEmpty() ? this : isEmpty() ? NONE : new AddSuffixRoles(this, suffix); + } + + /** + * Get a roles collection which adds a prefix to all role names. + * + * @param prefix the prefix to add (must not be {@code null}) + * @return the new roles collection (not {@code null}) + */ + default Roles addPrefix(String prefix) { + Assert.checkNotNullParam("prefix", prefix); + return prefix.isEmpty() ? this : isEmpty() ? NONE : new AddPrefixRoles(this, prefix); + } + + /** + * The empty roles collection. + */ + Roles NONE = new Roles() { + public boolean contains(final String roleName) { + return false; + } + + public Iterator iterator() { + return Collections.emptyIterator(); + } + + public Spliterator spliterator() { + return Spliterators.emptySpliterator(); + } + + public Roles and(final Roles other) { + return this; + } + + public Roles or(final Roles other) { + return other; + } + + public Roles xor(final Roles other) { + return other; + } + + public Roles minus(final Roles other) { + return this; + } + + public Roles addSuffix(final String suffix) { + return this; + } + + public Roles addPrefix(final String prefix) { + return this; + } + + public boolean isEmpty() { + return true; + } + + @Override + public String toString() { + return "NONE"; + } + }; +} Index: 3rdParty_sources/elytron/org/wildfly/security/authz/SimpleAttributesEntry.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/authz/SimpleAttributesEntry.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/authz/SimpleAttributesEntry.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,101 @@ +/* + * 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.authz; + +import java.util.AbstractList; + +/** + * An implementation of {@link Attributes.Entry} which can be used by implementations of {@link Attributes}. Operations + * are implemented in terms of methods on {@code Attributes} which do not rely upon entries. + * + * @author David M. Lloyd + */ +public class SimpleAttributesEntry extends AbstractList implements Attributes.Entry { + + private final Attributes attributes; + private final String key; + + /** + * Construct a new instance. + * + * @param attributes the backing attributes collection + * @param key the key of this entry + */ + public SimpleAttributesEntry(final Attributes attributes, final String key) { + this.attributes = attributes; + this.key = key; + } + + public String getKey() { + return key; + } + + public void removeRange(final int fromIndex, final int toIndex) { + attributes.removeRange(key, fromIndex, toIndex); + } + + public String get(final int index) { + return attributes.get(key, index); + } + + public String set(final int index, final String element) { + return attributes.set(key, index, element); + } + + public void add(final int index, final String element) { + attributes.add(key, index, element); + } + + public String remove(final int index) { + return attributes.remove(key, index); + } + + public boolean add(final String s) { + attributes.addLast(key, s); + return true; + } + + public void clear() { + attributes.remove(key); + } + + public boolean remove(final Object o) { + return o instanceof String && attributes.removeFirst(key, (String) o); + } + + public boolean contains(final Object o) { + return o instanceof String && attributes.containsValue(key, (String) o); + } + + public boolean isEmpty() { + return !attributes.containsKey(key); + } + + public int indexOf(final Object o) { + return o instanceof String ? attributes.indexOf(key, (String) o) : -1; + } + + public int lastIndexOf(final Object o) { + return o instanceof String ? attributes.lastIndexOf(key, (String) o) : -1; + } + + public int size() { + return attributes.size(key); + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/authz/SimplePermissionMapper.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/authz/SimplePermissionMapper.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/authz/SimplePermissionMapper.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,229 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2016 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.authz; + +import static org.wildfly.security.auth.server._private.ElytronMessages.log; +import static org.wildfly.common.Assert.checkNotNullParam; + +import java.security.Principal; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.function.Predicate; + +import org.wildfly.security.permission.PermissionVerifier; + +/** + * A simple {@link PermissionMapper} implementation that maps to pre-defined {@link PermissionVerifier} instances. + * + * This {@code PermissionMapper} is constructed using a {@link Builder} which is used to construct an ordered list of + * {@code PermissionVerifier} instances along with a set of principal names and a list of principal names. + * + * At the time {@link #mapPermissions(PermissionMappable, Roles)} is called this list is iterated to find corresponding + * definitions where either the name of the {@link Principal} within the {@link PermissionMappable} is contained + * within the mapping or the {@link Roles} in the {@code mapPermission} call contain at least one of the roles in the mapping + * then the associated {@code PermissionVerifier} will be used. + * + * It is possible that multiple mappings could be matched during the call to {@link #mapPermissions(PermissionMappable, Roles)} + * and this is why the ordering is important, by default only the first match will be used however this can be overridden by + * calling {@link Builder#setMappingMode(SimplePermissionMapper.MappingMode)} to choose a different mode to combine the resulting + * {@link PermissionVerifier} instances. + * + * @author Darran Lofthouse + */ +public class SimplePermissionMapper implements PermissionMapper { + + private final MappingMode mappingMode; + + private final List mappings; + + private SimplePermissionMapper(MappingMode mappingMode, List mappings) { + this.mappingMode = mappingMode; + this.mappings = mappings; + } + + @Override + public PermissionVerifier mapPermissions(PermissionMappable permissionMappable, Roles roles) { + checkNotNullParam("permissionMappable", permissionMappable); + checkNotNullParam("roles", roles); + + PermissionVerifier result = null; + + for (Mapping current : mappings) { + if (current.principalPredicate.test(permissionMappable.getPrincipal().getName()) || roles.containsAny(current.roles)) { + switch (mappingMode) { + case FIRST_MATCH: + return current.permissionVerifier; + case AND: + result = result != null ? result.and(current.permissionVerifier) : current.permissionVerifier; + break; + case OR: + result = result != null ? result.or(current.permissionVerifier) : current.permissionVerifier; + break; + case UNLESS: + result = result != null ? result.unless(current.permissionVerifier) : current.permissionVerifier; + break; + case XOR: + result = result != null ? result.xor(current.permissionVerifier) : current.permissionVerifier; + break; + } + } + } + + + return result != null ? result : PermissionVerifier.NONE; + } + + /** + * Construct a new {@link Builder} for creating the {@link PermissionMapper}. + * + * @return a new {@link Builder} for creating the {@link PermissionMapper}. + */ + public static Builder builder() { + return new Builder(); + } + + /** + * A builder for simple permission mappers. + */ + public static class Builder { + + private boolean built = false; + + private MappingMode mappingMode = MappingMode.FIRST_MATCH; + + private final List mappings = new ArrayList<>(); + + Builder() { + } + + /** + * Set the mapping mode that the newly created {@link PermissionMapper} should use. + * + * @param mappingMode the mapping mode. + * @return {@code this} builder to allow chaining. + */ + public Builder setMappingMode(MappingMode mappingMode) { + assertNotBuilt(); + this.mappingMode = mappingMode; + + return this; + } + + /** + * Add a new mapping to a {@link PermissionVerifier}, if the {@link PermissionMappable} being mapped has a principal name that is in the {@link Set} of principals or of any of the assigned roles are matched this mapping will be a match. + * + * @param principals the principal names to compare with the {@link PermissionMappable} principal. + * @param roles the role names to compare with the roles being passed for mapping. + * @param permissionVerifier the {@link PermissionVerifier} to use in the event of a resulting match. + * @return {@code this} builder to allow chaining. + */ + public Builder addMapping(Set principals, Set roles, PermissionVerifier permissionVerifier) { + assertNotBuilt(); + mappings.add(new Mapping(new HashSet<>(checkNotNullParam("principals", principals))::contains, roles, permissionVerifier)); + + return this; + } + + /** + * Add a new mapping to a {@link PermissionVerifier}, if the {@link PermissionMappable} being mapped has a principal or any of the assigned roles are matched this mapping will be a match. + * + * @param permissionVerifier the {@link PermissionVerifier} to use in the event of a resulting match. + * @return {@code this} builder to allow chaining. + */ + public Builder addMatchAllPrincipals(PermissionVerifier permissionVerifier) { + assertNotBuilt(); + mappings.add(new Mapping(name -> true, Collections.emptySet(), permissionVerifier)); + + return this; + } + + + /** + * Build and return the resulting {@link PermissionMapper}. + * + * @return the resulting {@link PermissionMapper} + */ + public PermissionMapper build() { + assertNotBuilt(); + built = true; + + return new SimplePermissionMapper(mappingMode, mappings); + } + + private void assertNotBuilt() { + if (built) { + throw log.builderAlreadyBuilt(); + } + } + } + + static class Mapping { + + final Predicate principalPredicate; + + final Set roles; + + final PermissionVerifier permissionVerifier; + + Mapping(Predicate principalPredicate, Set roles, PermissionVerifier permissionVerifier) { + this.principalPredicate = principalPredicate; + this.roles = Collections.unmodifiableSet(new HashSet<>(checkNotNullParam("roles", roles))); + this.permissionVerifier = checkNotNullParam("permissionVerifier", permissionVerifier); + } + + } + + /** + * Mode defining behaviour when multiple mappings are found. + */ + public enum MappingMode { + + /** + * If multiple mappings are found only the first will be used. + */ + FIRST_MATCH, + + /** + * If multiple mappings are found the corresponding {@link PermissionVerifier} instances will be combined using 'and'. + * Will assign permission which would be assigned by all mappings. + */ + AND, + + /** + * If multiple mappings are found the corresponding {@link PermissionVerifier} instances will be combined using 'or'. + * Will assign permissions which would be assigned by at least one mapping. + */ + OR, + + /** + * If multiple mappings are found the corresponding {@link PermissionVerifier} instances will be combined using 'xor'. + * Will assign permissions which would be assigned by odd amount of mappings. + */ + XOR, + + /** + * If multiple mappings are found the corresponding {@link PermissionVerifier} instances will be combined using 'unless'. + * Will assign permissions which would be assigned by first mapping but not by others. + */ + UNLESS; + } + +} Index: 3rdParty_sources/elytron/org/wildfly/security/authz/SourceAddressRoleDecoder.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/authz/SourceAddressRoleDecoder.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/authz/SourceAddressRoleDecoder.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,90 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2019 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.authz; + +import static org.wildfly.common.Assert.checkNotNullParam; + +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * A decoder to obtain role information using the source IP address runtime attribute from the identity. + * + * @author Farah Juma + */ +public class SourceAddressRoleDecoder implements RoleDecoder { + + private String sourceAddress; + private Pattern sourceAddressPattern; + private Roles roles; + + /** + * Construct a new instance. + * + * @param sourceAddress the source IP address to match (cannot be {@code null}) + * @param roles the roles to associate with the identity if the actual source IP address matches + * the given source IP address + */ + public SourceAddressRoleDecoder(String sourceAddress, Roles roles) { + checkNotNullParam("sourceAddress", sourceAddress); + checkNotNullParam("roles", roles); + this.sourceAddress = sourceAddress; + this.roles = roles; + } + + /** + * Construct a new instance. + * + * @param sourceAddressPattern the source IP address pattern to match (cannot be {@code null}) + * @param roles the roles to associate with the identity if the actual source IP address matches + * the given pattern + */ + public SourceAddressRoleDecoder(Pattern sourceAddressPattern, Roles roles) { + checkNotNullParam("sourceAddressPattern", sourceAddressPattern); + checkNotNullParam("roles", roles); + this.sourceAddressPattern = sourceAddressPattern; + this.roles = roles; + } + + /** + * Decode the role set using the source IP address runtime attribute from the given authorization identity. + * + * @param authorizationIdentity the authorization identity (not {@code null}) + * @return the role set (must not be {@code null}) + */ + public Roles decodeRoles(AuthorizationIdentity authorizationIdentity) { + Attributes runtimeAttributes = authorizationIdentity.getRuntimeAttributes(); + if (runtimeAttributes.containsKey(KEY_SOURCE_ADDRESS)) { + String actualSourceAddress = runtimeAttributes.getFirst(KEY_SOURCE_ADDRESS); + if (actualSourceAddress != null) { + if (sourceAddress != null) { + if (sourceAddress.equals(actualSourceAddress)) { + return roles; + } + } else { + final Matcher matcher = sourceAddressPattern.matcher(actualSourceAddress); + if (matcher.matches()) { + return roles; + } + } + } + } + return Roles.NONE; + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/authz/UnionRoles.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/authz/UnionRoles.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/authz/UnionRoles.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,81 @@ +/* + * 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.authz; + +import java.util.Iterator; +import java.util.NoSuchElementException; +import java.util.Spliterator; +import java.util.Spliterators; + +class UnionRoles implements Roles { + + private final Roles left; + private final Roles right; + + UnionRoles(final Roles left, final Roles right) { + this.left = left; + this.right = right; + } + + public boolean contains(final String roleName) { + return left.contains(roleName) || right.contains(roleName); + } + + public Iterator iterator() { + final Iterator leftIterator = left.iterator(); + final Iterator rightIterator = right.iterator(); + return new Iterator() { + String next; + + public boolean hasNext() { + if (next != null) { + return true; + } + for (;;) { + if (leftIterator.hasNext()) { + next = leftIterator.next(); + return true; + } else if (rightIterator.hasNext()) { + next = rightIterator.next(); + if (! left.contains(next)) { + return true; + } + next = null; + // fall out and re-loop + } else { + return false; + } + } + } + + public String next() { + if (! hasNext()) { + throw new NoSuchElementException(); + } + final String next = this.next; + this.next = null; + return next; + } + }; + } + + public Spliterator spliterator() { + return Spliterators.spliteratorUnknownSize(iterator(), Spliterator.NONNULL | Spliterator.DISTINCT); + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/authz/package-info.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/authz/package-info.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/authz/package-info.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,25 @@ +/* + * 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. + */ + +/** + * Elytron's Authorization API + * + * @author Pedro Igor + */ +package org.wildfly.security.authz; + Index: 3rdParty_sources/elytron/org/wildfly/security/cache/CachedIdentity.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/cache/CachedIdentity.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/cache/CachedIdentity.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,156 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2016 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.cache; + +import static org.wildfly.common.Assert.checkNotNullParam; + +import java.io.Serializable; +import java.security.Principal; +import java.util.Collections; +import java.util.Set; + +import org.wildfly.security.auth.server.SecurityIdentity; +import org.wildfly.security.authz.Roles; + +/** + * Represents a cached identity, managed by an {@link IdentityCache}. + * + * @author Pedro Igor + * @author Paul Ferraro + * @author Darran Lofthouse + * @see IdentityCache + */ +public final class CachedIdentity implements Serializable { + + private static final long serialVersionUID = -6408689383511392746L; + + private final String mechanismName; + private final boolean programmatic; + private final String name; + private final transient SecurityIdentity securityIdentity; + private final Set roles; + + /** + * Creates a new instance based on the given mechanismName and securityIdentity. + * + * @param mechanismName the name of the authentication mechanism used to authenticate/authorize the identity + * @param programmatic indicates if this identity was created as a result of programmatic authentication + * @param securityIdentity the identity to cache + */ + public CachedIdentity(String mechanismName, boolean programmatic, SecurityIdentity securityIdentity) { + this(mechanismName, programmatic, checkNotNullParam("securityIdentity", securityIdentity), securityIdentity.getPrincipal()); + } + + /** + * Creates a new instance based on the given mechanismName and principal. + * + * @param mechanismName the name of the authentication mechanism used to authenticate/authorize the identity + * @param programmatic indicates if this identity was created as a result of programmatic authentication + * @param principal the principal of this cached identity + */ + public CachedIdentity(String mechanismName, boolean programmatic, Principal principal) { + this(mechanismName, programmatic, null, principal); + } + + /** + * Creates a new instance based on the given mechanismName and principal. + * + * @param mechanismName the name of the authentication mechanism used to authenticate/authorize the identity + * @param programmatic indicates if this identity was created as a result of programmatic authentication + * @param principal the principal of this cached identity + * @param roles the roles assigned to this cached identity + */ + public CachedIdentity(String mechanismName, boolean programmatic, Principal principal, Set roles) { + this(mechanismName, programmatic, null, principal, roles); + } + + private CachedIdentity(String mechanismName, boolean programmatic, SecurityIdentity securityIdentity, Principal principal) { + this.mechanismName = checkNotNullParam("mechanismName", mechanismName); + this.programmatic = programmatic; + this.name = checkNotNullParam("name", checkNotNullParam("principal", principal).getName()); + this.securityIdentity = securityIdentity; + if (securityIdentity != null && securityIdentity.getPrincipal() != null) { + this.roles = Roles.toSet(securityIdentity.getRoles()); + } else { + this.roles = Collections.emptySet(); + } + } + + private CachedIdentity(String mechanismName, boolean programmatic, SecurityIdentity securityIdentity, Principal principal, Set roles) { + this.mechanismName = checkNotNullParam("mechanismName", mechanismName); + this.programmatic = programmatic; + this.name = checkNotNullParam("name", checkNotNullParam("principal", principal).getName()); + this.securityIdentity = securityIdentity; + this.roles = roles; + } + + /** + * Returns the name of the authentication mechanism used to authenticate/authorize the identity. + * + * @return the name of the authentication mechanism used to authenticate/authorize the identity + */ + public String getMechanismName() { + return this.mechanismName; + } + + /** + * Returns the principal name associated with the cached identity. + * + * @return the principal name associated with the cached identity. The name should never be null, as it will be used to re-create the identity when necessary (not {@code null}) + */ + public String getName() { + return this.name; + } + + /** + * Returns the identity represented by this instance. + * + * @return the identity represented by this instance. This method may return {@code null} in case the cache is holding the principal name only + */ + public SecurityIdentity getSecurityIdentity() { + return this.securityIdentity; + } + + /** + * Returns {@code true} if this identity was established using programmatic authentication, {@code false} otherwise. + * + * @return {@code true} if this identity was established using programmatic authentication, {@code false} otherwise. + */ + public boolean isProgrammatic() { + return programmatic; + } + + /** + * Returns the roles associated with the cached identity. + * + * @return the roles associated with the cached identity. + */ + public Set getRoles() { + if (this.securityIdentity != null) { + return Roles.toSet(this.securityIdentity.getRoles()); + } else { + return this.roles; + } + } + + @Override + public String toString() { + return "CachedIdentity{" + mechanismName + ", '" + name + "', " + securityIdentity + ", " + programmatic + "}"; + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/cache/IdentityCache.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/cache/IdentityCache.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/cache/IdentityCache.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,54 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2016 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.cache; + +import org.wildfly.security.auth.server.SecurityIdentity; + +/** + *

An identity cache is responsible to provide a specific caching strategy for identities. It should be used in conjunction with + * {@link org.wildfly.security.auth.callback.CachedIdentityAuthorizeCallback} when performing authorization within a authentication mechanism. + * + *

Implementations of this interface are specific for each authentication mechanism.

+ * + * @author Pedro Igor + * @see org.wildfly.security.auth.callback.CachedIdentityAuthorizeCallback + */ +public interface IdentityCache { + + /** + * Puts a {@link SecurityIdentity} into the cache. + * + * @param identity the identity to cache (not {@code null}) + */ + void put(SecurityIdentity identity); + + /** + * Returns an identity previously cached. + * + * @return the cached identity or {@code null} if there is no identity in the cache + */ + CachedIdentity get(); + + /** + * Removes an identity from the cache. + * + * @return the cached identity or {@code null} if there is no identity in the cache + */ + CachedIdentity remove(); +} Index: 3rdParty_sources/elytron/org/wildfly/security/cache/LRURealmIdentityCache.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/cache/LRURealmIdentityCache.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/cache/LRURealmIdentityCache.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,225 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2017 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.cache; + +import static org.wildfly.common.Assert.checkMinimumParameter; + +import java.security.Principal; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.locks.LockSupport; + +import org.wildfly.security.auth.server.RealmIdentity; + +/** + * A {@link RealmIdentityCache} implementation providing a LRU cache. + * + * @author Pedro Igor + */ +public final class LRURealmIdentityCache implements RealmIdentityCache { + + /** + * The load factor. + */ + private static final float DEFAULT_LOAD_FACTOR = 0.75f; + + /** + * Holds the cached identitys where the key is the domain principal, the one used to lookup the identity + */ + private final Map identityCache; + + /** + * Holds a mapping between a realm principal and domain principals + */ + private final Map> domainPrincipalMap; + + private final AtomicBoolean writing = new AtomicBoolean(false); + + private final long maxAge; + + /** + * Creates a new instance. + * + * @param maxEntries the maximum number of entries to keep in the cache + */ + public LRURealmIdentityCache(int maxEntries) { + this(maxEntries, -1); + } + + /** + * Creates a new instance. + * + * @param maxEntries the maximum number of entries to keep in the cache + * @param maxAge the time in milliseconds that an entry can stay in the cache. If {@code -1}, entries never expire + */ + public LRURealmIdentityCache(int maxEntries, long maxAge) { + checkMinimumParameter("maxEntries", 1, maxEntries); + checkMinimumParameter("maxAge", -1, maxAge); + identityCache = new LinkedHashMap(16, DEFAULT_LOAD_FACTOR, true) { + @Override + protected boolean removeEldestEntry(Entry eldest) { + return identityCache.size() > maxEntries; + } + }; + domainPrincipalMap = new HashMap<>(16); + this.maxAge = maxAge; + } + + @Override + public void put(Principal key, RealmIdentity newValue) { + try { + if (parkForWriteAndCheckInterrupt()) { + return; + } + + CacheEntry entry = identityCache.computeIfAbsent(key, principal -> { + domainPrincipalMap.computeIfAbsent(newValue.getRealmIdentityPrincipal(), principal1 -> { + Set principals = new HashSet<>(); + + principals.add(key); + + return principals; + }); + return new CacheEntry(key, newValue, maxAge); + }); + + if (entry != null) { + domainPrincipalMap.get(entry.value().getRealmIdentityPrincipal()).add(key); + } + } finally { + writing.lazySet(false); + } + } + + @Override + public RealmIdentity get(Principal key) { + if (parkForReadAndCheckInterrupt()) { + return null; + } + + CacheEntry cached = identityCache.get(key); + + if (cached != null) { + return removeIfExpired(cached); + } + + Set domainPrincipal = domainPrincipalMap.get(key); + + if (domainPrincipal != null) { + return removeIfExpired(identityCache.get(domainPrincipal.iterator().next())); + } + + return null; + } + + @Override + public void remove(Principal key) { + try { + if (parkForWriteAndCheckInterrupt()) { + return; + } + + if (identityCache.containsKey(key)) { + domainPrincipalMap.remove(identityCache.remove(key).value().getRealmIdentityPrincipal()).forEach(identityCache::remove); + } else if (domainPrincipalMap.containsKey(key)) { + domainPrincipalMap.remove(key).forEach(identityCache::remove); + } + } finally { + writing.lazySet(false); + } + } + + @Override + public void clear() { + try { + parkForWriteAndCheckInterrupt(); + identityCache.clear(); + domainPrincipalMap.clear(); + } finally { + writing.lazySet(false); + } + } + + private RealmIdentity removeIfExpired(CacheEntry cached) { + if (cached == null) { + return null; + } + + if (cached.isExpired()) { + remove(cached.key()); + return null; + } + + return cached.value(); + } + + private boolean parkForWriteAndCheckInterrupt() { + while (!writing.compareAndSet(false, true)) { + LockSupport.parkNanos(1L); + if (Thread.interrupted()) { + return true; + } + } + return false; + } + + private boolean parkForReadAndCheckInterrupt() { + while (writing.get()) { + LockSupport.parkNanos(1L); + if (Thread.interrupted()) { + return true; + } + } + return false; + } + + private static final class CacheEntry { + + final Principal key; + final RealmIdentity value; + final long expiration; + + CacheEntry(Principal key, RealmIdentity value, long maxAge) { + this.key = key; + this.value = value; + if(maxAge == -1) { + expiration = -1; + } else { + expiration = System.currentTimeMillis() + maxAge; + } + } + + Principal key() { + return key; + } + + RealmIdentity value() { + return value; + } + + boolean isExpired() { + return expiration != -1 ? System.currentTimeMillis() > expiration : false; + } + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/cache/RealmIdentityCache.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/cache/RealmIdentityCache.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/cache/RealmIdentityCache.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,91 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2016 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.cache; + +import static org.wildfly.common.Assert.checkNotNullParam; + +import java.security.Principal; +import java.util.function.Function; + +import org.wildfly.security.auth.server.RealmIdentity; + +/** + *

Provides a mechanism to plug a cache for {@link RealmIdentity} instances obtained from a {@link org.wildfly.security.auth.server.SecurityRealm}. + * + *

Implementors should be aware that {@link RealmIdentity} instances may require serialization depending on the implementation in use, as well + * any state within those instances such as attributes. + * + * @author Pedro Igor + */ +public interface RealmIdentityCache { + + /** + * Puts a new {@link RealmIdentity} into the cache and referenced by the specified {@link Principal}. + * + * @param principal the {@link Principal} that references the realm identity being cached + * @param realmIdentity the {@link RealmIdentity} instance + */ + void put(Principal principal, RealmIdentity realmIdentity); + + /** + *

If the specified key is not already associated with a value (or is mapped to {@code null}), attempts to compute its value using the given mapping + * function and enters it into this map unless {@code null}. + * + *

If the function returns {@code null} no mapping is recorded. If + * the function itself throws an (unchecked) exception, the + * exception is rethrown, and no mapping is recorded. + * + * @param principal the {@link Principal} that references the realm identity being cached + * @param mappingFunction the function that produces the {@link RealmIdentity} to cache or {@code null} + * @return a cached {@link RealmIdentity} instance + */ + default RealmIdentity computeIfAbsent(Principal principal, Function mappingFunction) { + checkNotNullParam("principal", principal); + checkNotNullParam("mappingFunction", mappingFunction); + RealmIdentity v; + if ((v = get(principal)) == null) { + RealmIdentity newValue; + if ((newValue = mappingFunction.apply(principal)) != null) { + put(principal, newValue); + return newValue; + } + } + return v; + } + + /** + * Obtains a previously cached {@link RealmIdentity} or {@code null} if no entry could be found with the specified {@link Principal}. + * + * @param principal the {@link Principal} that references a previously cached realm identity + * @return a cached {@link RealmIdentity} instance or {@code null} if no entry could be found with the specified principal. + */ + RealmIdentity get(Principal principal); + + /** + * Removes a specific cached identity from the cache and referenced by the specified {@link Principal}. + * + * @param principal the {@link Principal} that references a previously cached realm identity + */ + void remove(Principal principal); + + /** + * Removes all cached identities from this cache. + */ + void clear(); +} Index: 3rdParty_sources/elytron/org/wildfly/security/credential/source/CredentialSource.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/credential/source/CredentialSource.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/credential/source/CredentialSource.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,333 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2016 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.credential.source; + +import java.io.IOException; +import java.security.GeneralSecurityException; +import java.security.spec.AlgorithmParameterSpec; +import java.util.function.Function; + +import org.wildfly.common.Assert; +import org.wildfly.security.OneTimeSecurityFactory; +import org.wildfly.security.SecurityFactory; +import org.wildfly.security.auth.SupportLevel; +import org.wildfly.security.auth.server.IdentityCredentials; +import org.wildfly.security.credential.Credential; +import org.wildfly.security.key.KeyUtil; +import org.wildfly.security.auth.server._private.ElytronMessages; + +/** + * A source for credentials. + * + * @author David M. Lloyd + */ +public interface CredentialSource { + + /** + * Determine whether a given credential is definitely obtainable, possibly obtainable, or definitely not obtainable. + * + * @param credentialType the credential type class (must not be {@code null}) + * @param algorithmName the algorithm name, or {@code null} if any algorithm is acceptable or the credential type does + * not support algorithm names + * @param parameterSpec the algorithm parameters to match, or {@code null} if any parameters are acceptable or the credential type + * does not support algorithm parameters + * @return the level of support for this credential type (not {@code null}) + * + * @throws IOException if the credential source failed to determine the support level + */ + SupportLevel getCredentialAcquireSupport(Class credentialType, String algorithmName, AlgorithmParameterSpec parameterSpec) throws IOException; + + /** + * Determine whether a given credential is definitely obtainable, possibly obtainable, or definitely not obtainable. + * + * @param credentialType the credential type class (must not be {@code null}) + * @param algorithmName the algorithm name, or {@code null} if any algorithm is acceptable or the credential type + * does + * not support algorithm names + * @return the level of support for this credential type (not {@code null}) + * @throws IOException if the credential source failed to determine the support level + */ + default SupportLevel getCredentialAcquireSupport(Class credentialType, String algorithmName) throws IOException { + Assert.checkNotNullParam("credentialType", credentialType); + return getCredentialAcquireSupport(credentialType, algorithmName, null); + } + + /** + * Determine whether a given credential is definitely obtainable, possibly obtainable, or definitely not obtainable. + * + * @param credentialType the credential type class (must not be {@code null}) + * @return the level of support for this credential type (not {@code null}) + * @throws IOException if the credential source failed to determine the support level + */ + default SupportLevel getCredentialAcquireSupport(Class credentialType) throws IOException { + Assert.checkNotNullParam("credentialType", credentialType); + return getCredentialAcquireSupport(credentialType, null, null); + } + + /** + * Acquire a credential of the given type. The credential type is defined by its {@code Class} and an optional {@code algorithmName}. If the + * algorithm name is not given, then the query is performed for any algorithm of the given type. + * + * @param credentialType the credential type class (must not be {@code null}) + * @param algorithmName the algorithm name, or {@code null} if any algorithm is acceptable or the credential type does + * not support algorithm names + * @param parameterSpec the algorithm parameters to match, or {@code null} if any parameters are acceptable or the credential type + * does not support algorithm parameters + * @param the credential type + * + * @return the credential, or {@code null} if the principal has no credential of that type + * + * @throws IOException if the realm is not able to handle requests for any reason + * @throws IllegalStateException if no authentication has been initiated or authentication is already completed + */ + C getCredential(Class credentialType, String algorithmName, AlgorithmParameterSpec parameterSpec) throws IOException; + + /** + * Acquire a credential of the given type. The credential type is defined by its {@code Class} and an optional + * {@code algorithmName}. If the + * algorithm name is not given, then the query is performed for any algorithm of the given type. + * + * @param credentialType the credential type class (must not be {@code null}) + * @param algorithmName the algorithm name, or {@code null} if any algorithm is acceptable or the credential type + * does + * not support algorithm names + * @param the credential type + * @return the credential, or {@code null} if the principal has no credential of that type + * @throws IOException if the realm is not able to handle requests for any reason + * @throws IllegalStateException if no authentication has been initiated or authentication is already completed + */ + default C getCredential(Class credentialType, String algorithmName) throws IOException { + Assert.checkNotNullParam("credentialType", credentialType); + return getCredential(credentialType, algorithmName, null); + } + + /** + * Acquire a credential of the given type. The credential type is defined by its {@code Class} and an optional + * {@code algorithmName}. If the + * algorithm name is not given, then the query is performed for any algorithm of the given type. + * + * @param credentialType the credential type class (must not be {@code null}) + * @param the credential type + * @return the credential, or {@code null} if the principal has no credential of that type + * @throws IOException if the realm is not able to handle requests for any reason + * @throws IllegalStateException if no authentication has been initiated or authentication is already completed + */ + default C getCredential(Class credentialType) throws IOException { + Assert.checkNotNullParam("credentialType", credentialType); + return getCredential(credentialType, null, null); + } + + /** + * Apply the given function to the acquired credential, if it is set and of the given type. + * + * @param credentialType the credential type class (must not be {@code null}) + * @param function the function to apply (must not be {@code null}) + * @param the credential type + * @param the return type + * @return the result of the function, or {@code null} if the criteria are not met + * + * @throws IOException if the realm is not able to handle requests for any reason + * @throws IllegalStateException if no authentication has been initiated or authentication is already completed + */ + default R applyToCredential(Class credentialType, Function function) throws IOException { + final Credential credential = getCredential(credentialType); + return credential == null ? null : credential.castAndApply(credentialType, function); + } + + /** + * Apply the given function to the acquired credential, if it is set and of the given type and algorithm. + * + * @param credentialType the credential type class (must not be {@code null}) + * @param algorithmName the algorithm name + * @param function the function to apply (must not be {@code null}) + * @param the credential type + * @param the return type + * @return the result of the function, or {@code null} if the criteria are not met + * @throws IOException if the realm is not able to handle requests for any reason + * @throws IllegalStateException if no authentication has been initiated or authentication is already completed + */ + default R applyToCredential(Class credentialType, String algorithmName, Function function) throws IOException { + final Credential credential = getCredential(credentialType, algorithmName); + return credential == null ? null : credential.castAndApply(credentialType, algorithmName, function); + } + + /** + * Apply the given function to the acquired credential, if it is set and of the given type and algorithm with the + * given parameters. + * + * @param credentialType the credential type class (must not be {@code null}) + * @param algorithmName the algorithm name + * @param parameterSpec the parameter specification or {@code null} if any parameter specification is acceptable + * @param function the function to apply (must not be {@code null}) + * @param the credential type + * @param the return type + * @return the result of the function, or {@code null} if the criteria are not met + * @throws IOException if the realm is not able to handle requests for any reason + * @throws IllegalStateException if no authentication has been initiated or authentication is already completed + */ + default R applyToCredential(Class credentialType, String algorithmName, AlgorithmParameterSpec parameterSpec, Function function) throws IOException { + final Credential credential = getCredential(credentialType, algorithmName, parameterSpec); + return credential == null ? null : credential.castAndApply(credentialType, algorithmName, parameterSpec, function); + } + + /** + * Aggregate this credential source with another. + * + * @param other the other credential source (must not be {@code null}) + * @return the aggregated credential source (not {@code null}) + */ + default CredentialSource with(CredentialSource other) { + final CredentialSource self = this; + return new CredentialSource() { + public SupportLevel getCredentialAcquireSupport(final Class credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec) throws IOException { + return SupportLevel.max(self.getCredentialAcquireSupport(credentialType, algorithmName, parameterSpec), other.getCredentialAcquireSupport(credentialType, algorithmName, parameterSpec)); + } + + public C getCredential(final Class credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec) throws IOException { + C credential = self.getCredential(credentialType, algorithmName, parameterSpec); + if (credential != null) { + return credential; + } else { + return other.getCredential(credentialType, algorithmName, parameterSpec); + } + } + + public CredentialSource without(final Class credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec) { + final CredentialSource filteredSelf = self.without(credentialType, algorithmName, parameterSpec); + final CredentialSource filteredOther = other.without(credentialType, algorithmName, parameterSpec); + if (filteredSelf == NONE || filteredSelf == IdentityCredentials.NONE) { + if (filteredOther == NONE || filteredOther == IdentityCredentials.NONE) { + return NONE; + } else { + return filteredOther; + } + } else if (filteredOther == NONE || filteredOther == IdentityCredentials.NONE) { + return filteredSelf; + } else if (filteredSelf == self && filteredOther == other) { + return this; + } else { + return filteredSelf.with(filteredOther); + } + } + }; + } + + /** + * Get a derived credential source which excludes credentials of the given type. + * + * @param credentialType the credential type to exclude (must not be {@code null}) + * @return the derived credential source (not {@code null}) + */ + default CredentialSource without(Class credentialType) { + return without(credentialType, null, null); + } + + /** + * Get a derived credential source which excludes credentials of the given type and optional algorithm. + * + * @param credentialType the credential type to exclude (must not be {@code null}) + * @param algorithmName the algorithm name to exclude, or {@code null} to exclude all algorithms (or for credential types which do not use algorithms) + * @return the derived credential source (not {@code null}) + */ + default CredentialSource without(Class credentialType, String algorithmName) { + return without(credentialType, null, null); + } + + /** + * Get a derived credential source which excludes credentials of the given type and optional algorithm. + * + * @param credentialType the credential type to exclude (must not be {@code null}) + * @param algorithmName the algorithm name to exclude, or {@code null} to exclude all algorithms (or for credential types which do not use algorithms) + * @param parameterSpec the parameter specification or {@code null} if any parameter specification is acceptable + * @return the derived credential source (not {@code null}) + */ + default CredentialSource without(Class credentialType, String algorithmName, AlgorithmParameterSpec parameterSpec) { + return new CredentialSource() { + public SupportLevel getCredentialAcquireSupport(final Class credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec) throws IOException { + if (isUnsupported(credentialType, algorithmName, parameterSpec)) { + return SupportLevel.UNSUPPORTED; + } else { + return CredentialSource.this.getCredentialAcquireSupport(credentialType, algorithmName, parameterSpec); + } + } + + public C getCredential(final Class credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec) throws IOException { + if (isUnsupported(credentialType, algorithmName, parameterSpec)) { + return null; + } else { + return CredentialSource.this.getCredential(credentialType, algorithmName, parameterSpec); + } + } + + private boolean isUnsupported(final Class testCredentialType, final String testAlgorithmName, final AlgorithmParameterSpec testParameterSpec) { + return credentialType.isAssignableFrom(testCredentialType) && (algorithmName == null || algorithmName.equals(testAlgorithmName)) && (parameterSpec == null || KeyUtil.parametersEqual(parameterSpec, testParameterSpec)); + } + + public CredentialSource without(final Class testCredentialType, final String testAlgorithmName, final AlgorithmParameterSpec testParameterSpec) { + final CredentialSource without = CredentialSource.this.without(testCredentialType, testAlgorithmName, testParameterSpec); + if (without == NONE || without == IdentityCredentials.NONE) { + return NONE; + } + if (without == CredentialSource.this) { + return this; + } + return CredentialSource.super.without(credentialType, algorithmName, parameterSpec); + } + }; + } + + /** + * An empty credential source. + */ + CredentialSource NONE = new CredentialSource() { + public SupportLevel getCredentialAcquireSupport(final Class credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec) throws IOException { + return SupportLevel.UNSUPPORTED; + } + + public C getCredential(final Class credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec) throws IOException { + return null; + } + }; + + /** + * Get a credential source from the given security factory. The factory is queried on each request. If the value + * should be cached after the first request, use {@link OneTimeSecurityFactory}. + * + * @param credentialFactory the credential factory (must not be {@code null}) + * @return the credential source (not {@code null}) + */ + static CredentialSource fromSecurityFactory(SecurityFactory credentialFactory) { + Assert.checkNotNullParam("credentialFactory", credentialFactory); + return new CredentialSource() { + public SupportLevel getCredentialAcquireSupport(final Class credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec) throws IOException { + return SupportLevel.POSSIBLY_SUPPORTED; + } + + public C getCredential(final Class credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec) throws IOException { + final Credential credential; + try { + credential = credentialFactory.create(); + } catch (GeneralSecurityException e) { + throw ElytronMessages.log.cannotObtainCredentialFromFactory(e); + } + return credential.matches(credentialType, algorithmName, parameterSpec) ? credentialType.cast(credential) : null; + } + }; + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/http/ElytronMessages.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/http/ElytronMessages.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/http/ElytronMessages.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,59 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2014 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.http; + +import org.jboss.logging.BasicLogger; +import org.jboss.logging.Logger; +import org.jboss.logging.annotations.Message; +import org.jboss.logging.annotations.MessageLogger; +import org.jboss.logging.annotations.ValidIdRange; +import org.jboss.logging.annotations.ValidIdRanges; + +/** + * Log messages and exceptions for Elytron. + * + * @author David M. Lloyd + * @author Darran Lofthouse + */ +@MessageLogger(projectCode = "ELY", length = 5) +@ValidIdRanges({ + @ValidIdRange(min = 6000, max = 6018) +}) +interface ElytronMessages extends BasicLogger { + + ElytronMessages log = Logger.getMessageLogger(ElytronMessages.class, "org.wildfly.security"); + ElytronMessages httpUserPass = Logger.getMessageLogger(ElytronMessages.class, "org.wildfly.security.http.password"); + ElytronMessages httpClientCert = Logger.getMessageLogger(ElytronMessages.class, "org.wildfly.security.http.cert"); + + @Message(id = 6000, value = "Status code can not be set at this time.") + IllegalStateException statusCodeNotNow(); + + @Message(id = 6005, value = "Attachments are not supported on this scope.") + UnsupportedOperationException noAttachmentSupport(); + + @Message(id = 6016, value = "HTTP authentication failed validating request, no mechanisms remain to continue authentication.") + HttpAuthenticationException httpAuthenticationFailedEvaluatingRequest(); + + @Message(id = 6017, value = "HTTP authentication is required but no authentication mechansims are available.") + HttpAuthenticationException httpAuthenticationNoMechanisms(); + + @Message(id = 6018, value = "HTTP authentication none of the responders successfuly sent a response.") + HttpAuthenticationException httpAuthenticationNoSuccessfulResponder(); + +} Index: 3rdParty_sources/elytron/org/wildfly/security/http/ElytronMessages_$logger.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/http/ElytronMessages_$logger.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/http/ElytronMessages_$logger.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,77 @@ +package org.wildfly.security.http; + +import java.util.Locale; +import java.lang.IllegalStateException; +import java.io.Serializable; +import javax.annotation.Generated; +import org.jboss.logging.DelegatingBasicLogger; +import org.wildfly.security.http.HttpAuthenticationException; +import org.jboss.logging.BasicLogger; +import org.jboss.logging.Logger; +import java.util.Arrays; +import java.lang.UnsupportedOperationException; + +/** + * Warning this class consists of generated code. + */ +@Generated(value = "org.jboss.logging.processor.generator.model.MessageLoggerImplementor", date = "2024-01-15T21:47:04+0100") +public class ElytronMessages_$logger extends DelegatingBasicLogger implements ElytronMessages, BasicLogger, Serializable { + private static final long serialVersionUID = 1L; + private static final String FQCN = ElytronMessages_$logger.class.getName(); + public ElytronMessages_$logger(final Logger log) { + super(log); + } + private static final Locale LOCALE = Locale.ROOT; + protected Locale getLoggingLocale() { + return LOCALE; + } + protected String statusCodeNotNow$str() { + return "ELY06000: Status code can not be set at this time."; + } + @Override + public final IllegalStateException statusCodeNotNow() { + final IllegalStateException result = new IllegalStateException(String.format(getLoggingLocale(), statusCodeNotNow$str())); + _copyStackTraceMinusOne(result); + return result; + } + private static void _copyStackTraceMinusOne(final Throwable e) { + final StackTraceElement[] st = e.getStackTrace(); + e.setStackTrace(Arrays.copyOfRange(st, 1, st.length)); + } + protected String noAttachmentSupport$str() { + return "ELY06005: Attachments are not supported on this scope."; + } + @Override + public final UnsupportedOperationException noAttachmentSupport() { + final UnsupportedOperationException result = new UnsupportedOperationException(String.format(getLoggingLocale(), noAttachmentSupport$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String httpAuthenticationFailedEvaluatingRequest$str() { + return "ELY06016: HTTP authentication failed validating request, no mechanisms remain to continue authentication."; + } + @Override + public final org.wildfly.security.http.HttpAuthenticationException httpAuthenticationFailedEvaluatingRequest() { + final org.wildfly.security.http.HttpAuthenticationException result = new org.wildfly.security.http.HttpAuthenticationException(String.format(getLoggingLocale(), httpAuthenticationFailedEvaluatingRequest$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String httpAuthenticationNoMechanisms$str() { + return "ELY06017: HTTP authentication is required but no authentication mechansims are available."; + } + @Override + public final org.wildfly.security.http.HttpAuthenticationException httpAuthenticationNoMechanisms() { + final org.wildfly.security.http.HttpAuthenticationException result = new org.wildfly.security.http.HttpAuthenticationException(String.format(getLoggingLocale(), httpAuthenticationNoMechanisms$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String httpAuthenticationNoSuccessfulResponder$str() { + return "ELY06018: HTTP authentication none of the responders successfuly sent a response."; + } + @Override + public final org.wildfly.security.http.HttpAuthenticationException httpAuthenticationNoSuccessfulResponder() { + final org.wildfly.security.http.HttpAuthenticationException result = new org.wildfly.security.http.HttpAuthenticationException(String.format(getLoggingLocale(), httpAuthenticationNoSuccessfulResponder$str())); + _copyStackTraceMinusOne(result); + return result; + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/http/HttpAuthenticationException.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/http/HttpAuthenticationException.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/http/HttpAuthenticationException.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,70 @@ +/* + * 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.http; + +import java.io.IOException; + +/** + * Exception to indicate a general failure with the HTTP authentication mechanism. + * + * @author Darran Lofthouse + */ +public class HttpAuthenticationException extends IOException { + + /** + * + */ + private static final long serialVersionUID = 2920504964210220416L; + + /** + * Constructs a new {@code HttpAuthenticationException}. The message is left blank ({@code null}), + * and no cause is specified. + */ + public HttpAuthenticationException() { + } + + /** + * Constructs a new {@code HttpAuthenticationException}. with the message provided, + * and no cause is specified. + * + * @param message the message for this {@code HttpAuthenticationException}. + */ + public HttpAuthenticationException(String message) { + super(message); + } + + /** + * Constructs a new {@code HttpAuthenticationException} with an initial cause. + * + * @param cause the cause for this {@code HttpAuthenticationException}. + */ + public HttpAuthenticationException(Throwable cause) { + super(cause); + } + + /** + * Constructs a new {@code HttpAuthenticationException} with an initial message and cause. + * + * @param message the message for this {@code HttpAuthenticationException}. + * @param cause the cause for this {@code HttpAuthenticationException}. + */ + public HttpAuthenticationException(String message, Throwable cause) { + super(message, cause); + } + +} Index: 3rdParty_sources/elytron/org/wildfly/security/http/HttpAuthenticator.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/http/HttpAuthenticator.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/http/HttpAuthenticator.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,640 @@ +/* + * 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.http; + +import static java.lang.System.getSecurityManager; +import static org.wildfly.common.Assert.checkNotNullParam; +import static org.wildfly.security.http.ElytronMessages.log; +import static org.wildfly.security.http.HttpConstants.FORBIDDEN; +import static org.wildfly.security.http.HttpConstants.OK; +import static org.wildfly.security.http.HttpConstants.SECURITY_IDENTITY; + +import java.io.OutputStream; +import java.security.AccessController; +import java.security.PrivilegedAction; +import java.security.cert.Certificate; +import java.util.ArrayList; +import java.util.List; +import java.util.function.Consumer; +import java.util.function.Supplier; + +import org.wildfly.common.Assert; +import org.wildfly.security.auth.server.RealmUnavailableException; +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.cache.CachedIdentity; +import org.wildfly.security.cache.IdentityCache; +import org.wildfly.security.credential.PasswordCredential; +import org.wildfly.security.evidence.Evidence; +import org.wildfly.security.evidence.PasswordGuessEvidence; +import org.wildfly.security.http.impl.BaseHttpServerRequest; +import org.wildfly.security.password.interfaces.ClearPassword; + +/** + * A HTTP based authenticator responsible for performing the authentication of the current request based on the policies of the + * associated {@link SecurityDomain}. + * + * @author Darran Lofthouse + */ +public class HttpAuthenticator { + + private static final String MY_AUTHENTICATED_IDENTITY_KEY = HttpAuthenticator.class.getName() + ".authenticated-identity"; + + private final Supplier> mechanismSupplier; + private final Supplier identityCacheSupplier; + private final SecurityDomain securityDomain; + private final HttpExchangeSpi httpExchangeSpi; + private final boolean required; + private final boolean ignoreOptionalFailures; + private final String programmaticMechanismName; + private final Consumer logoutHandlerConsumer; + private volatile IdentityCache identityCache; + + private HttpAuthenticator(final Builder builder) { + this.mechanismSupplier = builder.mechanismSupplier; + this.securityDomain = builder.securityDomain; + this.programmaticMechanismName = builder.programmaticMechanismName; + this.logoutHandlerConsumer = builder.logoutHandlerConsumer; + this.httpExchangeSpi = builder.httpExchangeSpi; + this.required = builder.required; + this.ignoreOptionalFailures = builder.ignoreOptionalFailures; + this.identityCacheSupplier = builder.identityCacheSupplier != null ? builder.identityCacheSupplier : () -> createIdentityCache(programmaticMechanismName); + } + + /** + * Perform authentication for the request. + * + * @return {@code true} if the call should be allowed to continue within the web server, {@code false} if the call should be + * returning to the client. + * @throws HttpAuthenticationException + */ + public boolean authenticate() throws HttpAuthenticationException { + if (restoreIdentity()) { + return true; + } + + return new AuthenticationExchange().authenticate(); + } + + + /** + * Perform a login for the supplied username and password using the pre-configured mechanism name. + * + * @param username the username to use for authentication. + * @param password the password to use for authentication. + * @return A {@link SecurityIdentity} is authentication and authorization is successful. + */ + public SecurityIdentity login(String username, String password) { + final PasswordGuessEvidence evidence = new PasswordGuessEvidence(checkNotNullParam("password", password).toCharArray()); + try { + return login(username, evidence, programmaticMechanismName); + } finally { + evidence.destroy(); + } + } + + /** + * Perform a login for the supplied username and password using the specified mechanism name. + * + * @param username the username to use for authentication. + * @param evidence the evidence to use for authentication. + * @return A {@link SecurityIdentity} is authentication and authorization is successful. + */ + private SecurityIdentity login(String username, Evidence evidence, String mechanismName) { + if (securityDomain == null) { + return null; + } + + try (ServerAuthenticationContext authenticationContext = createServerAuthenticationContext()) { + authenticationContext.setAuthenticationName(username); + if (authenticationContext.verifyEvidence(evidence)) { + if (evidence instanceof PasswordGuessEvidence) { + log.tracef("Associating credential for '%s' with identity.", username); + authenticationContext.addPrivateCredential( + new PasswordCredential(ClearPassword.createRaw(ClearPassword.ALGORITHM_CLEAR, ((PasswordGuessEvidence) evidence).getGuess()))); + } + if (authenticationContext.authorize()) { + SecurityIdentity authorizedIdentity = authenticationContext.getAuthorizedIdentity(); + + IdentityCache identityCache = getOrCreateIdentityCache(); + identityCache.put(authorizedIdentity); + logoutHandlerConsumer.accept(identityCache::remove); + + httpExchangeSpi.authenticationComplete(authorizedIdentity, mechanismName); + + return authorizedIdentity; + } else { + httpExchangeSpi.authenticationFailed("Authorization Failed", mechanismName); + } + } else { + httpExchangeSpi.authenticationFailed("Authentication Failed", mechanismName); + } + } catch (IllegalArgumentException | RealmUnavailableException | IllegalStateException e) { + httpExchangeSpi.authenticationFailed(e.getMessage(), mechanismName); + } + + return null; + } + + private ServerAuthenticationContext createServerAuthenticationContext() { + if (getSecurityManager() != null) { + return AccessController.doPrivileged((PrivilegedAction) () -> securityDomain.createNewAuthenticationContext()); + } + + return securityDomain.createNewAuthenticationContext(); + } + + private boolean restoreIdentity() { + if (securityDomain == null) { + return false; + } + + IdentityCache identityCache = getOrCreateIdentityCache(); + + CachedIdentity cachedIdentity = identityCache.get(); + if (cachedIdentity != null) { + SecurityIdentity securityIdentity = cachedIdentity.getSecurityIdentity(); + + try (final ServerAuthenticationContext authenticationContext = securityDomain.createNewAuthenticationContext()) { + boolean authorized = securityIdentity != null && authenticationContext.importIdentity(securityIdentity); + boolean cache = false; + + if (authorized == false) { + log.trace("Unable to use SecurityIdentity from CachedIdentity - attempting to recreate"); + + authenticationContext.setAuthenticationName(cachedIdentity.getName()); + authorized = authenticationContext.authorize(); + cache = true; + } + + if (authorized) { + securityIdentity = authenticationContext.getAuthorizedIdentity(); + + httpExchangeSpi.authenticationComplete(securityIdentity, cachedIdentity.getMechanismName()); + logoutHandlerConsumer.accept(identityCache::remove); + + if (cache) { + log.tracef("Replacing cached identity for '%s' against session scope.", cachedIdentity.getName()); + identityCache.put(securityIdentity); + } + + return true; + } + } catch (IllegalArgumentException | RealmUnavailableException | IllegalStateException e) { + httpExchangeSpi.authenticationFailed(e.getMessage(), programmaticMechanismName); + } + + log.tracef("Restoring identity '%s' failed, clearing cache from scope.", cachedIdentity.getName()); + identityCache.remove(); // Whatever was in there no longer works so just + // drop it. + } else { + log.trace("No CachedIdentity to restore."); + } + + return false; + } + + private IdentityCache getOrCreateIdentityCache() { + if (identityCache == null) { + identityCache = identityCacheSupplier.get(); + } + + return identityCache; + } + + private IdentityCache createIdentityCache(String mechanismName) { + return new IdentityCache() { + + @Override + public void put(SecurityIdentity identity) { + HttpScope session = getAttachableSessionScope(true); + + if (session == null || !session.exists()) { + if (log.isTraceEnabled()) { + log.tracef("Unable to cache identity for '%s'.", identity.getPrincipal().getName()); + } + return; + } + + if (session.supportsChangeID() && session.getAttachment(MY_AUTHENTICATED_IDENTITY_KEY) == null) { + session.changeID(); + } + + if (log.isTraceEnabled()) { + log.tracef("Caching identity for '%s' against session scope.", identity.getPrincipal().getName()); + } + session.setAttachment(MY_AUTHENTICATED_IDENTITY_KEY, new CachedIdentity(mechanismName, true, identity)); + } + + @Override + public CachedIdentity get() { + HttpScope session = getAttachableSessionScope(false); + + if (session == null || session.exists() == false) { + return null; + } + + return (CachedIdentity) session.getAttachment(MY_AUTHENTICATED_IDENTITY_KEY); + } + + @Override + public CachedIdentity remove() { + HttpScope session = getAttachableSessionScope(false); + + if (session == null || session.exists() == false) { + return null; + } + + CachedIdentity cachedIdentity = get(); + + session.setAttachment(MY_AUTHENTICATED_IDENTITY_KEY, null); + + return cachedIdentity; + } + }; + } + + private HttpScope getAttachableSessionScope(boolean createSession) { + HttpScope scope = httpExchangeSpi.getScope(Scope.SESSION); + if (scope == null || scope.supportsAttachments() == false) { + return null; + } + + if (scope != null && scope.exists() == false && createSession) { + scope.create(); + } + + return scope; + } + + /** + * Construct and return a new {@code Builder} to configure and create an instance of {@code HttpAuthenticator}. + * + * @return a new {@code Builder} to configure and create an instance of {@code HttpAuthenticator}. + */ + public static Builder builder() { + return new Builder(); + } + + private class AuthenticationExchange extends BaseHttpServerRequest implements HttpServerRequest, HttpServerResponse { + + private volatile HttpServerAuthenticationMechanism currentMechanism; + + private volatile boolean authenticationAttempted = false; + private volatile int statusCode = -1; + private volatile boolean statusCodeAllowed = false; + private volatile List responders; + private volatile HttpServerMechanismsResponder successResponder; + private volatile boolean authenticated = false; + + AuthenticationExchange() { + super(httpExchangeSpi); + } + + private boolean isAuthenticated() { + return authenticated; + } + + private boolean authenticate() throws HttpAuthenticationException { + List authenticationMechanisms = mechanismSupplier.get(); + if (required && authenticationMechanisms.size() == 0) { + throw log.httpAuthenticationNoMechanisms(); + } + responders = new ArrayList<>(authenticationMechanisms.size()); + boolean evaluationFailed = false; + try { + for (HttpServerAuthenticationMechanism nextMechanism : authenticationMechanisms) { + currentMechanism = nextMechanism; + try { + nextMechanism.evaluateRequest(this); + } catch (HttpAuthenticationException e) { + // Give all mechanisms an opportunity to succeed, where a mechanism fails due to mis-configuration or a transient error + // others may still be able to operate correctly. + evaluationFailed = true; + log.trace("Request evaluation for mechanism '%s' failed.", nextMechanism.getMechanismName(), e); + } + + if (isAuthenticated()) { + if (successResponder != null) { + statusCodeAllowed = true; + successResponder.sendResponse(this); + if (statusCode > 0) { + httpExchangeSpi.setStatusCode(statusCode); + return false; + } + } + return true; + } + } + currentMechanism = null; + + if (required || (authenticationAttempted && ignoreOptionalFailures == false)) { + statusCodeAllowed = true; + if (responders.size() > 0) { + boolean atLeastOneChallenge = false; + + int defaultStatusCode = OK; + boolean statusSet = false; + for (HttpServerMechanismsResponder responder : responders) { + try { + responder.sendResponse(this); + atLeastOneChallenge = true; + if ( ! statusSet && statusCode > 0) { + if (statusCode == FORBIDDEN) { // minor status code change default + defaultStatusCode = statusCode; + } else if (statusCode != OK) { + statusSet = true; // other status codes (like UNAUTHORIZED) set status immediately + httpExchangeSpi.setStatusCode(statusCode); + } + } + } catch (HttpAuthenticationException e) { + log.trace("HTTP Authentication mechanism unable to send challenge.", e); + } + } + if (atLeastOneChallenge == false) { + throw log.httpAuthenticationNoSuccessfulResponder(); + } + if ( ! statusSet) { + httpExchangeSpi.setStatusCode(defaultStatusCode); + } + } else { // no responders set + if (evaluationFailed) { + throw log.httpAuthenticationFailedEvaluatingRequest(); + } + httpExchangeSpi.setStatusCode(FORBIDDEN); + } + return false; + } + + // If authentication was required it should have been picked up in the previous block. + return true; + } finally { + for (HttpServerAuthenticationMechanism current : authenticationMechanisms) { + current.dispose(); + } + } + } + + /* + * This method is overridden to trigger certificate re-negotiation if authentication + * is required. + */ + @Override + public Certificate[] getPeerCertificates() { + return httpExchangeSpi.getPeerCertificates(required); + } + + @Override + public void noAuthenticationInProgress(HttpServerMechanismsResponder responder) { + if (responder != null) { + responders.add(responder); + } + } + + @Override + public void authenticationInProgress(HttpServerMechanismsResponder responder) { + authenticationAttempted = true; + if (responder != null) { + responders.add(responder); + } + } + + @Override + public void authenticationComplete(HttpServerMechanismsResponder responder) { + authenticated = true; + httpExchangeSpi.authenticationComplete( + currentMechanism.getNegotiationProperty(SECURITY_IDENTITY, SecurityIdentity.class), + currentMechanism.getMechanismName()); + successResponder = responder; + } + + @Override + public void authenticationComplete(HttpServerMechanismsResponder responder, Runnable logoutHandler) { + authenticationComplete(responder); + if (logoutHandlerConsumer != null) { + logoutHandlerConsumer.accept(logoutHandler); + } + } + + @Override + public void authenticationFailed(String message, HttpServerMechanismsResponder responder) { + authenticationAttempted = true; + httpExchangeSpi.authenticationFailed(message, currentMechanism.getMechanismName()); + if (responder != null) { + responders.add(responder); + } + } + + @Override + public void badRequest(HttpAuthenticationException failure, HttpServerMechanismsResponder responder) { + authenticationAttempted = true; + httpExchangeSpi.badRequest(failure, currentMechanism.getMechanismName()); + if (responder != null) { + responders.add(responder); + } + } + + @Override + public void addResponseHeader(String headerName, String headerValue) { + httpExchangeSpi.addResponseHeader(headerName, headerValue); + } + + @Override + public void setStatusCode(int statusCode) { + if (statusCodeAllowed == false) { + throw log.statusCodeNotNow(); + } + + if (this.statusCode < 0 || statusCode != OK) { + this.statusCode = statusCode; + } + } + + @Override + public OutputStream getOutputStream() { + return httpExchangeSpi.getResponseOutputStream(); + } + + @Override + public void setResponseCookie(HttpServerCookie cookie) { + httpExchangeSpi.setResponseCookie(cookie); + } + + @Override + public boolean forward(String path) { + int statusCode = httpExchangeSpi.forward(path); + if (statusCode > 0) { + setStatusCode(statusCode); + + return true; + } + + return false; + } + + @Override + public boolean suspendRequest() { + return httpExchangeSpi.suspendRequest(); + } + + @Override + public boolean resumeRequest() { + return httpExchangeSpi.resumeRequest(); + } + + } + + /** + * A {@code Builder} to configure and create an instance of {@code HttpAuthenticator}. + */ + public static class Builder { + + private Supplier> mechanismSupplier; + private SecurityDomain securityDomain; + private HttpExchangeSpi httpExchangeSpi; + private boolean required; + private boolean ignoreOptionalFailures; + private Consumer logoutHandlerConsumer; + private String programmaticMechanismName; + private Supplier identityCacheSupplier; + + Builder() { + } + + /** + * Set the supplier to use to obtain list of {@link HttpServerAuthenticationMechanism} implementations + * instances to use, based on the configured policy. + * + * @param mechanismSupplier the {@link Supplier} with the configured authentication policy + * @return the {@link Builder} to allow method call chaining. + */ + public Builder setMechanismSupplier(Supplier> mechanismSupplier) { + this.mechanismSupplier = mechanismSupplier; + + return this; + } + + /** + * Set the {@link SecurityDomain} to use for programmatic authentication. + * + * @param securityDomain the {@link SecurityDomain} to use for programmatic authentication. + * @return the {@link Builder} to allow method call chaining. + */ + public Builder setSecurityDomain(final SecurityDomain securityDomain) { + this.securityDomain = securityDomain; + + return this; + } + + /** + * Set the {@link HttpExchangeSpi} instance for the current request to allow integration with the Elytron APIs. + * + * @param httpExchangeSpi the {@link HttpExchangeSpi} instance for the current request + * @return the {@link Builder} to allow method call chaining. + */ + public Builder setHttpExchangeSpi(final HttpExchangeSpi httpExchangeSpi) { + this.httpExchangeSpi = httpExchangeSpi; + + return this; + } + + + /** + * Sets if authentication is required for the current request, if not required mechanisms will be called to be given the + * opportunity to authenticate + * + * @param required is authentication required for the current request? + * @return the {@link Builder} to allow method call chaining. + */ + public Builder setRequired(final boolean required) { + this.required = required; + + return this; + } + + /** + * Where authentication is not required but is still attempted a failure of an authentication mechanism will cause the + * challenges to be sent and the current request returned to the client, setting this value to true will allow the + * failure to be ignored. + * + * This setting has no effect when required is set to {@code true}, in that case all failures will result in a client + * + * @param ignoreOptionalFailures should mechanism failures be ignored if authentication is optional. + * @return the {@link Builder} to allow method call chaining. + */ + public Builder setIgnoreOptionalFailures(final boolean ignoreOptionalFailures) { + this.ignoreOptionalFailures = ignoreOptionalFailures; + + return this; + } + + /** + *

A {@link Consumer} responsible for registering a {@link Runnable} emitted by one of the mechanisms during the evaluation + * of a request and representing some action to be taken during logout. + * + *

This method is mainly targeted for programmatic logout where logout requests are send by the application after the + * authentication. Although, integration code is free to register the logout handler whatever they want in order to support + * different logout scenarios. + * + * @param logoutHandlerConsumer the consumer responsible for registering the logout handler (not {@code null}) + * @return the {@link Builder} to allow method call chaining. + */ + public Builder registerLogoutHandler(Consumer logoutHandlerConsumer) { + this.logoutHandlerConsumer = Assert.checkNotNullParam("logoutHandlerConsumer", logoutHandlerConsumer); + return this; + } + + /** + * Set the mechanism name that should be used for programmatic authentication if one is not provided at the time of the call. + * + * @param programmaticMechanismName the name of the mechanism to use for programmatic authentication. + * @return the {@link Builder} to allow method call chaining. + */ + public Builder setProgrammaticMechanismName(final String programmaticMechanismName) { + this.programmaticMechanismName = programmaticMechanismName; + + return this; + } + + /** + * Set a {@code Supplier} which acts as a factory to return a new {@link IdentityCache} instance for the current request, this allows + * alternative caching strategies to be provided. + * + * @param identityCacheSupplier - a factory which returns new {@link IdentityCache} instances for the current request. + * @return the {@link Builder} to allow method call chaining. + */ + public Builder setIdentityCacheSupplier(final Supplier identityCacheSupplier) { + this.identityCacheSupplier = identityCacheSupplier; + + return this; + } + + /** + * Build the new {@code HttpAuthenticator} instance. + * + * @return the new {@code HttpAuthenticator} instance. + */ + public HttpAuthenticator build() { + return new HttpAuthenticator(this); + } + + } + +} Index: 3rdParty_sources/elytron/org/wildfly/security/http/HttpConstants.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/http/HttpConstants.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/http/HttpConstants.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,216 @@ +/* + * 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.http; + +import java.util.regex.Pattern; + +import org.ietf.jgss.GSSManager; + +/** + * Constants used within HTTP based authentication. + * + * @author Darran Lofthouse + */ +public class HttpConstants { + + + private HttpConstants() { + } + + /* + * Negotiated Properties + */ + + /** + * The property which holds the negotiated security identity after a successful HTTP server-side authentication. + */ + public static final String SECURITY_IDENTITY = "wildfly.http.security-identity"; + + /* + * Mechanism Configuration Properties + */ + + private static final String CONFIG_BASE = HttpConstants.class.getPackage().getName(); + public static final String CONFIG_CONTEXT_PATH = CONFIG_BASE + ".context-path"; + public static final String CONFIG_REALM = CONFIG_BASE + ".realm"; + + public static final String CONFIG_VALIDATE_DIGEST_URI = CONFIG_BASE + ".validate-digest-uri"; + public static final String CONFIG_SKIP_CERTIFICATE_VERIFICATION = CONFIG_BASE + ".skip-certificate-verification"; + + /** + * The context relative path of the login page. + */ + public static final String CONFIG_LOGIN_PAGE = CONFIG_BASE + ".login-page"; + + /** + * The context relative path of the error page. + */ + public static final String CONFIG_ERROR_PAGE = CONFIG_BASE + ".error-page"; + + /** + * This defines the location used by mechanisms dependent on the response to the challenge being sent in using 'POST'. + */ + public static final String CONFIG_POST_LOCATION = CONFIG_BASE + ".post-location"; + + /** + * This allows a {@link GSSManager} instance to be passed into the authentication mechanisms. + */ + public static final String CONFIG_GSS_MANAGER = CONFIG_BASE + ".gss-manager"; + + /** + * This enables workaround for native GSS, where createName() needs to be called for correct GSSContext initialization. + * Set to "true" to call createName() as part of GSSContext initialization. + * This is workaround of JDK-8194073. + */ + public static final String CONFIG_CREATE_NAME_GSS_INIT = CONFIG_BASE + ".create-name-gss-init"; + + /** + * In clustered environment Security Identity is restored during failover, load balancer change node (not sticky behavior) and session passivation/activation. + * Set to "true" to disable this behavior. + */ + public static final String CONFIG_DISABLE_RESTORE_SECURITY_IDENTITY = CONFIG_BASE + ".disable-restore-security-identity"; + + /** + * A comma separated list of scopes in preferred order the mechanism should attempt to use to persist state including the + * caching of any previously authenticated identity. + * + * Accepted values are: - + *

    + *
  • CONNECTION + *
  • SESSION + *
  • SSL_SESSION + *
  • NONE + *

+ * + * Presently only supported by the SPNEGO mechanism. + */ + public static final String CONFIG_STATE_SCOPES = CONFIG_BASE + ".state-scopes"; + + /** + * If set to {@code true} the SPNEGO and FORM authentication mechanisms will not change the session ID + * after a successful authentication. + * + * Where set the web application should be configured to use cookies exclusively for session management. + */ + public static final String DISABLE_SESSION_ID_CHANGE = CONFIG_BASE + ".unsafe.disable-session-change-id"; + + /* + * Header Fields + */ + public static final String ALGORITHM = "algorithm"; + public static final String AUTH = "auth"; + public static final String AUTH_PARAM = "auth-param"; + public static final String CHARSET = "charset"; + public static final String CNONCE = "cnonce"; + public static final String DOMAIN = "domain"; + public static final String NC = "nc"; + public static final String NEGOTIATE = "Negotiate"; + public static final String NEXT_NONCE = "nextnonce"; + public static final String NONCE = "nonce"; + public static final String PARTIAL = "partial/"; + public static final String OPAQUE = "opaque"; + public static final String QOP = "qop"; + public static final String REALM = "realm"; + public static final String RSPAUTH = "rspauth"; + public static final String RESPONSE = "response"; + public static final String STALE = "stale"; + public static final String URI = "uri"; + public static final String USERNAME = "username"; + public static final String USERNAME_STAR = "username*"; + public static final String XML_HTTP_REQUEST = "XMLHttpRequest"; + + /* + * Header Names + */ + + public static final String ACCEPT = "Accept"; + public static final String AUTHENTICATION_INFO = "Authentication-Info"; + public static final String AUTHORIZATION = "Authorization"; + public static final String FACES_REQUEST = "Faces-Request"; + public static final String HOST = "Host"; + public static final String LOCATION = "Location"; + public static final String SOAP_ACTION = "SOAPAction"; + public static final String WWW_AUTHENTICATE = "WWW-Authenticate"; + public static final String X_REQUESTED_WITH = "X-Requested-With"; + + /** + * Errors + */ + public static final String ERROR = "error"; + public static final String ERROR_DESCRIPTION = "error_description"; + public static final String INVALID_TOKEN = "invalid_token"; + public static final String STALE_TOKEN = "Stale token"; + public static final String NO_TOKEN = "no_token"; + + /* + * Mechanism Names + */ + + public static final String BASIC_NAME = "BASIC"; + public static final String CLIENT_CERT_NAME = "CLIENT_CERT"; + public static final String DIGEST_NAME = "DIGEST"; + public static final String DIGEST_SHA256_NAME = "DIGEST-SHA-256"; + public static final String DIGEST_SHA512_256_NAME = "DIGEST-SHA-512-256"; + public static final String EXTERNAL_NAME = "EXTERNAL"; + public static final String FORM_NAME = "FORM"; + public static final String SPNEGO_NAME = "SPNEGO"; + public static final String BEARER_TOKEN = "BEARER_TOKEN"; + + /* + * Response Codes + */ + + public static final int OK = 200; + public static final int FOUND = 302; + public static final int SEE_OTHER = 303; + public static final int TEMPORARY_REDIRECT = 307; + public static final int BAD_REQUEST = 400; + public static final int UNAUTHORIZED = 401; + public static final int FORBIDDEN = 403; + + /* + * Methods + */ + + public static final String POST = "POST"; + public static final String OPTIONS = "OPTIONS"; + + /* + * Algorithms + */ + + public static final String MD5 = "MD5"; + public static final String SHA256 = "SHA-256"; + public static final String SHA512_256 = "SHA-512-256"; + + /* + * Schemes + */ + + public static final String HTTP = "http"; + public static final String HTTPS = "https"; + + /** + * Bearer token pattern. + * The Bearer token authorization header is of the form "Bearer", followed by optional whitespace, followed by + * the token itself, followed by optional whitespace. The token itself must be one or more characters and must + * not contain any whitespace. + */ + public static final Pattern BEARER_TOKEN_PATTERN = Pattern.compile("^Bearer *([^ ]+) *$", Pattern.CASE_INSENSITIVE); + +} Index: 3rdParty_sources/elytron/org/wildfly/security/http/HttpExchangeSpi.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/http/HttpExchangeSpi.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/http/HttpExchangeSpi.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,293 @@ +/* + * 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.http; + +import static org.wildfly.security.http.ElytronMessages.httpClientCert; + +import java.io.InputStream; +import java.io.OutputStream; +import java.net.InetSocketAddress; +import java.net.URI; +import java.security.cert.Certificate; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.Supplier; + +import javax.net.ssl.SSLPeerUnverifiedException; +import javax.net.ssl.SSLSession; + +import org.wildfly.security.auth.server.SecurityIdentity; + +/** + * The SPI to be implemented to bridge the Elytron APIs with the available APIs + * of the web server being integrated with. + * + * @author Darran Lofthouse + */ +public interface HttpExchangeSpi extends HttpServerScopes { + + /** + * Get a list of all of the values set for the specified header within the HTTP request. + * + * @param headerName the not {@code null} name of the header the values are required for. + * @return a list of the values set for this header, if the header is not set on the request then {@code null} should be returned. + */ + List getRequestHeaderValues(final String headerName); + + /** + * Add the specified header and value to the end of the current response headers, + * + * @param headerName the name of the header. + * @param headerValue the value of the header. + */ + void addResponseHeader(final String headerName, final String headerValue); + + /** + * Get the first value for the header specified in the HTTP request. + * + * A {@code default} implementation of this method is provided although implementations of this SPI may choose to provide their own optimised implementation. + * + * @param headerName the not {@code null} name of the header the value is required for. + * @return the value for the first instance of the header specified, if the header is not present then {@code null} should be returned instead. + */ + default String getFirstRequestHeaderValue(final String headerName) { + List headerValues = getRequestHeaderValues(headerName); + return headerValues != null && headerValues.size() > 0 ? headerValues.get(0) : null; + } + + /** + * Get the {@link SSLSession} (if any) that has been established for the connection in use. + * + * @return the {@link SSLSession} (if any) that has been established for the connection in use, or {@code null} if none + * exists. + */ + default SSLSession getSSLSession() { + return null; + } + + /** + * Get the peer certificates (if any) associated with the current connection. + * + * Although re-negotiation may be requested the underlying implementation may not support re-negotiation and will continue + * to return {@code null}. {@code null} will also still be returned if after re-negotiation peer certificates are still not + * available. + * + * @param renegotiate if no certificates are available should re-negotiation of the session be attempted. + * @return the peer certificates associated with the current connection or {@code null} if none associated. + */ + default Certificate[] getPeerCertificates(boolean renegotiate) { + SSLSession sslSession = getSSLSession(); + if (sslSession != null) { + try { + return sslSession.getPeerCertificates(); + } catch (SSLPeerUnverifiedException e) { + httpClientCert.trace("Peer Unverified", e); + } + } + + return null; + } + + /** + * Set the required status code. + * + * This method is only expected to be called once after a response code has been selected. + * + * @param statusCode the desired status code. + */ + void setStatusCode(final int statusCode); + + /** + * Notification that authentication has been completed for a specific identity using a specific authentication mechanism. + * + * @param securityIdentity the identity of the authenticated account. + * @param mechanismName the name of the mechanism that was used to authenticate the account. + */ + void authenticationComplete(final SecurityIdentity securityIdentity, final String mechanismName); + + /** + * Notification that authentication has failed using the mechanism specified. + * + * @param message an error message describing the failure + * @param mechanismName a failed mechanism name + */ + void authenticationFailed(final String message, final String mechanismName); + + /** + * Notification that authentication has failed for a specific mechanism due to being a bad request. + * + * @param error an exception to describe the error. + * @param mechanismName a failed mechanism name + */ + void badRequest(final HttpAuthenticationException error, final String mechanismName); + + /** + * Returns the name of the HTTP method with which this request was made, for example, GET, POST, or PUT. + * + * @return a String specifying the name of the method with which this request was made + */ + String getRequestMethod(); + + /** + * Get the URI representation for the current request. + * + * @return the URI representation for the current request. + */ + URI getRequestURI(); + + /** + * Get the request path. This is the path relative to the context path. E.g.: for a request to + * http://my.appserver.com/my-application/path/sub-path this method is going to return /path/sub-path. + * + * @return the request relative path + */ + String getRequestPath(); + + /** + * Returns the parameters received in the current request. + * + * These parameters will be from both the query string and the form data when available. + * + * Where a parameter is named both in the query string and in the form data the list will contain the values from the query + * string followed by the values from the form data. + * + * @return the parameters received in the current request. + */ + Map> getRequestParameters(); + + /** + * Returns the names of all parameters either from the query string or from the form data where available. + * + * @return the names of all parameters either from the query string or from the form data where available. + */ + default Set getRequestParameterNames() { + return getRequestParameters().keySet(); + } + + /** + * Return the values for the parameter specified, where a parameter is specified both in the query string and in the form data the query string values will be first in the array. + * + * @param name the name of the desires parameter values. + * @return the values for the parameter specified or {@code null} if the parameter was not in the request. + */ + default List getRequestParameterValues(String name) { + Map> parameters = getRequestParameters(); + if (parameters != null) { + return parameters.get(name); + } + + return null; + } + + /** + * Get the first value for the parameter specified. + * + * @param name the name of the parameter the first value is required for. + * @return the first value of the named parameter or {@code null} if the paramter is not available. + */ + default String getFirstRequestParameterValue(String name) { + List values = getRequestParameterValues(name); + if (values != null && values.size() > 0) { + return values.get(0); + } + + return null; + } + + /** + * Returns a {@link List} containing all of the {@link HttpServerCookie} objects the client sent with this request. This method should return an empty {@code List} if no cookies were sent. + * + * @return a {@link List} of all the cookies included with this request. + */ + List getCookies(); + + /** + * Returns the request input stream. + * + * @return the input stream or {@code null} if not supported. + */ + InputStream getRequestInputStream(); + + /** + * Get the source address of the HTTP request. + * + * @return the source address of the HTTP request + */ + InetSocketAddress getSourceAddress(); + + /** + * Sets a response cookie. + * + * @param cookie the cookie + */ + void setResponseCookie(HttpServerCookie cookie); + + /** + * Returns the response output stream. + * + * @return the output stream or {@code null} if not supported. + */ + OutputStream getResponseOutputStream(); + + /** + * Forward the current request to a different path. + * + * @return the desired status code from forwarding or {@code -1} if forwarding was not successful. + */ + default int forward(String path) { + return -1; + } + + /** + * Returns a remotely authenticated user + * + * @return the remote user principal or {@code null} if no remote user was authenticated. + */ + default String getRemoteUser() { + return null; + } + + /** + * Suspend the current request so that it can be subsequently resumed. + * + * The server may use any strategy it deems appropriate to suspend the current request and store the state ready for a subsequent request. + * + * @return {@code true} if suspension is supported, {@code false} otherwise. + */ + default boolean suspendRequest() { + return false; + } + + /** + * Resume a previously suspended request. + * + * @return {@code true} if resuming a request is supported, {@code false} otherwise. + */ + default boolean resumeRequest() { + return false; + } + + /** + * Set the request input stream supplier. The default implementation does nothing. + * + * @param requestInputStreamSupplier the request input stream supplier + */ + default void setRequestInputStreamSupplier(Supplier requestInputStreamSupplier) {} + +} Index: 3rdParty_sources/elytron/org/wildfly/security/http/HttpScope.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/http/HttpScope.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/http/HttpScope.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,184 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2016 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.http; + +import static org.wildfly.security.http.ElytronMessages.log; + +import java.io.InputStream; +import java.util.function.Consumer; + +/** + * An attachment scope for use by an authentication mechanism. + *

+ * Different scopes may be available to share Objects e.g. Application, Session, Connection. + * + * @author Darran Lofthouse + */ +public interface HttpScope { + + /** + * Get the ID of this scope or (@code null} if IDs are not supported for this scope or the scope doesn't currently exist. + * + * @return the ID of this scope or (@code null} if IDs are not supported for this scope or the scope doesn't currently exist. + */ + default String getID() { + return null; + } + + /** + * Tests whether this scope exists. + * + * @return {@code true} if this scope exists. Otherwise, {@code false} + */ + default boolean exists() { + return true; + } + + /** + * Create this scope. + * + * @return {@code true} if the scope was created. Otherwise, {@code false} indicating that this scope was already created or that creation is not support. + */ + default boolean create() { + return false; + } + + /** + * Tests whether this scope support attachments. + * + * @return {@code true} if this scope supports attachments, {@code false} otherwise + */ + default boolean supportsAttachments() { + return false; + } + + /** + * Set the named attribute on this scope, setting to {@code null} will clear any value previously set for this key. + * + * @param key the key to use to store the attachment. + * @param value the value to store with the key or {@code null} to clear any previously stored attachment. + * @throws UnsupportedOperationException if attachments are not supported. + */ + default void setAttachment(final String key, final Object value) { + throw log.noAttachmentSupport(); + } + + /** + * Get the attachment previously associated with the key specified on this scope. + * + * @param key the key used to store the attachment on this scope. + * @return the value associated with the scope or {@code null} if no association exists. + * @throws UnsupportedOperationException if attachments are not supported. + */ + default Object getAttachment(final String key) { + throw log.noAttachmentSupport(); + } + + /** + * Get the attachment previously associated with the key specified on this scope and cast it to the type specified. + * + * This method will only return a value if the attachment exists AND can be cast to the desired type, otherwise it returns + * {@code null}. + * + * @param key the key used to store the attachment on this scope. + * @param type the desired type of the attachment. + * @return the value associated with the scope or {@code null} if no association exists or if it could not be converted to + * the requested type. + * @throws UnsupportedOperationException if attachments are not supported. + */ + default T getAttachment(final String key, final Class type) { + Object attachment = getAttachment(key); + if (attachment != null && type.isInstance(attachment)) { + return type.cast(attachment); + } + return null; + } + + /** + * Is invalidation supported for this scope? + * + * @return {@code true} if this scope supports invalidation, {@code false} otherwise. + */ + default boolean supportsInvalidation() { + return false; + } + + /** + * Invalidate this scope. + * + * @return {@code true} if invalidation was successful, {@code false} otherwise. + */ + default boolean invalidate() { + return false; + } + + /** + * Is changing the ID of the scope supported? + * + * @return {@code true} if this scope supports changing the ID, {@code false} otherwise. + */ + default boolean supportsChangeID() { + return false; + } + + /** + * Change the ID of this scope. + * + * @return {@code true} if the change was successful, {@code false} otherwise. + */ + default boolean changeID() { + return false; + } + + /** + * Tests whether this scope support access to scope specific resources. + * + * @return {@code true} if this scope supports access to scope specific resources, {@code false} otherwise. + */ + default boolean supportsResources() { + return false; + } + + /** + * Get the resource associated with the path specified. + * + * @param path the path to the resource. + * @return the {@link InputStream} of the resource or {@code null} if resources is not supported or the specified resource is not found. + */ + default InputStream getResource(final String path) { + return null; + } + + /** + * Tests whether this scope support registration to receive notifications. + * + * @return {@code true} if this scope supports registration for notifications, {@code false} otherwise. + */ + default boolean supportsNotifications() { + return false; + } + + /** + * Register a notification consumer to receive notifications from this scope. + * + * @param notificationConsumer the consumer to receive notifications from this scope. + */ + default void registerForNotification(Consumer notificationConsumer) { + } + +} Index: 3rdParty_sources/elytron/org/wildfly/security/http/HttpScopeNotification.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/http/HttpScopeNotification.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/http/HttpScopeNotification.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,42 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2016 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.http; + +/** + * Interface providing information about scope notifications. + * + * @author Pedro Igor + */ +public interface HttpScopeNotification extends HttpServerScopes { + + /** + * {@link Scope#SESSION} notification types + */ + enum SessionNotificationType { + INVALIDATED, TIMEOUT, UNDEPLOY + } + + /** + * Returns {@code true} if this notification matches any of the specified types. + * + * @param types the notification types to check + * @return {@code true} if this notification matches any of the specified types. Otherwise {@code false} + */ + boolean isOfType(Enum... types); +} Index: 3rdParty_sources/elytron/org/wildfly/security/http/HttpServerAuthenticationMechanism.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/http/HttpServerAuthenticationMechanism.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/http/HttpServerAuthenticationMechanism.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,83 @@ +/* + * 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.http; + +/** + * Definition of a server side HTTP authentication mechanism. + * + * @author Darran Lofthouse + */ +public interface HttpServerAuthenticationMechanism { + + /** + * Get the name of this mechanism, where appropriate this should be the IANA registered name. + * + * @return the name of the mechanism. + */ + String getMechanismName(); + + /** + * Evaluate the current request and attempt to authenticate if appropriate. + * + * The mechanism should call the appropriate callback methods on the {link HttpServerResponse} to both indicate the outcome + * of the evaluation and to register any {@link HttpServerMechanismsResponder} as required. + * + * @param request representation of the HTTP request. + * @throws HttpAuthenticationException if there is an internal failure handling the authentication. + */ + void evaluateRequest(HttpServerRequest request) throws HttpAuthenticationException; + + /** + * Get the property negotiated as a result of authentication. + * + * Mechanisms only make properties available after indicating a successful authentication has completed. + * + * @param propertyName the name of the property. + * @return the value of the property or {@code null} if the specified property is not available. + */ + default Object getNegotiatedProperty(String propertyName) { + return null; + } + + /** + * Get the strongly typed property negotiated as a result of authentication. + * + * Mechanisms only make properties available after indicating a successful authentication has completed. + * + * Note: This form of the mechanism will also return {@code null} if the property is set but is of a different type. + * + * @param propertyName the name of the property. + * @param type the expected type of the property. + * @return the value of the property or {@code null} if the specified property is not available or is of a different type.. + */ + default T getNegotiationProperty(String propertyName, Class type) { + Object property = getNegotiatedProperty(propertyName); + if (property != null && type.isInstance(property)) { + return type.cast(property); + } + return null; + } + + /** + * Dispose of any resources currently held by this authentication mechanism. + */ + default void dispose() { + } + +} Index: 3rdParty_sources/elytron/org/wildfly/security/http/HttpServerAuthenticationMechanismFactory.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/http/HttpServerAuthenticationMechanismFactory.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/http/HttpServerAuthenticationMechanismFactory.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,58 @@ +/* + * 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.http; + +import java.util.Map; + +import javax.security.auth.callback.CallbackHandler; + +/** + * Factory to create authentication mechanisms. + * + * @author Darran Lofthouse + */ +public interface HttpServerAuthenticationMechanismFactory { + + /** + * Get the names of the HTTP authentication mechanisms that can be supplied by this factory filtered by the supplied + * properties. + * + * @param properties the {@code non-null} set of properties to pass configuration to the mechanisms that may be evaluated for mechanism availability. + * @return A {@code non-null} array of the names of the supported HTTP authentication mechanisms. + */ + String[] getMechanismNames(Map properties); + + + /** + * Obtain an instance of the authentication mechanism requested provided this is allowed by any policy specified within the supplied properties. + * + * @param mechanismName The {@code non-null} name of the mechanism to create. + * @param properties The {@code non-null} set of properties to select and configure the mechanism that may be evaluated for mechanism availability. + * @param callbackHandler The {@code non-null} {@link CallbackHandler} for use by the mechanism during authentication. + * @return the configured {@link HttpServerAuthenticationMechanism} or {@code null} if no mechanism could be resolved for the given mechanism name. + * @throws HttpAuthenticationException if there is an error creating the mechanism. + */ + HttpServerAuthenticationMechanism createAuthenticationMechanism(String mechanismName, Map properties, CallbackHandler callbackHandler) throws HttpAuthenticationException; + + /** + * Can be used for clean up + */ + default void shutdown() { + return; + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/http/HttpServerCookie.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/http/HttpServerCookie.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/http/HttpServerCookie.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,141 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2016 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.http; + +/** + * Server side representation of a HTTP Cookie. + * + * @author Pedro Igor + */ +public interface HttpServerCookie { + + /** + * Returns a new instance representing HttpServerCookie + * + * @param name the name of the cookie + * @param value the current value of this cookie + * @param domain the domain name of this cookie + * @param maxAge specifying the maximum age of the cookie in seconds + * @param path a String specifying a path on the server + * @param secure true if the browser uses a secure protocol, false otherwise + * @param version the version of the protocol this cookie complies with + * @param httpOnly true if this cookie has been marked as HttpOnly, false otherwise + * @return a new instance representing HttpServerCookie + */ + static HttpServerCookie getInstance(String name, String value, String domain, int maxAge, String path, boolean secure, int version, boolean httpOnly) { + return new HttpServerCookie() { + @Override + public String getName() { + return name; + } + + @Override + public String getValue() { + return value; + } + + @Override + public String getDomain() { + return domain; + } + + @Override + public int getMaxAge() { + return maxAge; + } + + @Override + public String getPath() { + return path; + } + + @Override + public boolean isSecure() { + return secure; + } + + @Override + public int getVersion() { + return version; + } + + @Override + public boolean isHttpOnly() { + return httpOnly; + } + }; + } + + /** + * Returns the name of the cookie. + * + * @return the name of the cookie + */ + String getName(); + + /** + * Returns the current value of this cookie. + * + * @return the current value of this cookie + */ + String getValue(); + + /** + * Gets the domain name of this cookie. + * + * @return the domain name of this cookie + */ + String getDomain(); + + /** + * Gets the maximum age in seconds of this Cookie. + * + * @return an integer specifying the maximum age of the cookie in seconds + */ + int getMaxAge(); + + /** + * Returns the path on the server to which the browser returns this cookie. + * + * @return a String specifying a path on the server + */ + String getPath(); + + /** + * Returns true if the browser is sending cookies only over a secure protocol, or false if the + * browser can send cookies using any protocol. + * + * @return true if the browser uses a secure protocol, false otherwise + */ + boolean isSecure(); + + /** + * Returns the version of the protocol this cookie complies with. + * + * @return the version of the protocol this cookie complies with. + */ + int getVersion(); + + /** + * Checks whether this cookie has been marked as HttpOnly. + * + * @return true if this cookie has been marked as HttpOnly, false otherwise + */ + boolean isHttpOnly(); +} Index: 3rdParty_sources/elytron/org/wildfly/security/http/HttpServerMechanismsResponder.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/http/HttpServerMechanismsResponder.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/http/HttpServerMechanismsResponder.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,36 @@ +/* + * 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.http; + +/** + * A responder for sending either authentication challenges or responses as a result of successful authentication back to the + * calling client. + * + * @author Darran Lofthouse + */ +@FunctionalInterface +public interface HttpServerMechanismsResponder { + + /** + * Send any required response to the client. + * + * @param response the {@link HttpServerResponse} to use to set the response / challenge. + */ + void sendResponse(HttpServerResponse response) throws HttpAuthenticationException; + +} Index: 3rdParty_sources/elytron/org/wildfly/security/http/HttpServerRequest.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/http/HttpServerRequest.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/http/HttpServerRequest.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,279 @@ +/* + * 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.http; + +import java.io.InputStream; +import java.net.InetSocketAddress; +import java.net.URI; +import java.security.cert.Certificate; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.Supplier; + +import javax.net.ssl.SSLSession; + +/** + * Server side representation of a HTTP request. + * + * @author Darran Lofthouse + */ +public interface HttpServerRequest extends HttpServerScopes { + + /** + * Get a list of all of the values set for the specified header within the HTTP request. + * + * @param headerName the not {@code null} name of the header the values are required for. + * @return a list of the values set for this header, if the header is not set on the request then + * {@code null} should be returned. + */ + List getRequestHeaderValues(final String headerName); + + /** + * Get the first value for the header specified in the HTTP request. + * + * @param headerName the not {@code null} name of the header the value is required for. + * @return the value for the first instance of the header specified, if the header is not present then {@code null} should + * be returned instead. + */ + String getFirstRequestHeaderValue(final String headerName); + + /** + * Get the {@link SSLSession} (if any) that has been established for the connection in use. + * + * Note that even if this is null {@link #getPeerCertificates()} can still return some certificates, as the certificates + * may have been provided to the underlying server via some external mechanism (such as headers). + * + * @return the {@link SSLSession} (if any) that has been established for the connection in use, or {@code null} if none + * exists. + */ + SSLSession getSSLSession(); + + /** + * Get the peer certificates established on the connection. + * + * @return the peer certificates established on the connection or {@code null} if none available. + */ + Certificate[] getPeerCertificates(); + + /** + * Notification from the mechanism to state no authentication is in progress whilst evaluating the current request. + * + * @param responder a {@link HttpServerMechanismsResponder} that can send a challenge should it be required. + */ + void noAuthenticationInProgress(final HttpServerMechanismsResponder responder); + + /** + * Notification from the mechanism to state no authentication is in progress whilst evaluating the current request. + * + * If this form is called no challenge is expected from this mechanism. + */ + default void noAuthenticationInProgress() { + noAuthenticationInProgress(null); + } + + /** + * Notification that this mechanism has commenced but not completed authentication, typically because another challenge / + * response round trip is required. + * + * @param responder a {@link HttpServerMechanismsResponder} that can send a challenge should it be required. + */ + void authenticationInProgress(final HttpServerMechanismsResponder responder); + + /** + * Notification that authentication is now complete. + * + * After this point the framework will perform an authorization check for the authenticated user and if successful establish + * the identity of the request. + * + * @param responder a {@link HttpServerMechanismsResponder} that can send a response. + */ + void authenticationComplete(final HttpServerMechanismsResponder responder); + + /** + *

Notification that authentication is now complete. + * + *

This method behaves exactly like {@code {@link #authenticationComplete(HttpServerMechanismsResponder)}}, allowing + * mechanisms to register a logout handler which should be called when a logout request is received by the underlying container. + * + * @param responder a {@link HttpServerMechanismsResponder} that can send a response. + * @param logoutHandler a {@link Runnable} that can handle logout + */ + void authenticationComplete(final HttpServerMechanismsResponder responder, Runnable logoutHandler); + + /** + * Notification that authentication is now complete. + * + * After this point the framework will perform an authorization check for the authenticated user and if successful establish + * the identity of the request. + * + * If this form is called no response is expected from this mechanism. + */ + default void authenticationComplete() { + authenticationComplete(null); + } + + /** + * Notification that authentication failes. + * + * @param message an error message describing the failure. + * @param responder a {@link HttpServerMechanismsResponder} that can send a challenge should it be required. + */ + void authenticationFailed(final String message, final HttpServerMechanismsResponder responder); + + /** + * Notification that authentication failes. + * + * If this form is called no challenge is expected from this mechanism. + * + * @param message an error message describing the failure. + */ + default void authenticationFailed(final String message) { + authenticationFailed(message, null); + } + + /** + * Notification to indicate that this was a bad request. + * + * @param failure an {@link HttpAuthenticationException} to describe the error. + * @param responder a {@link HttpServerMechanismsResponder} that can send a challenge should it be required. + */ + void badRequest(HttpAuthenticationException failure, final HttpServerMechanismsResponder responder); + + /** + * Notification to indicate that this was a bad request. + * + * If this form is called no challenge is expected from this mechanism. + * + * @param failure an {@link HttpAuthenticationException} to describe the error. + */ + default void badRequest(HttpAuthenticationException failure) { + badRequest(failure, null); + } + + /** + * Returns the name of the HTTP method with which this request was made, for example, GET, POST, or PUT. + * + * @return a String specifying the name of the method with which this request was made + */ + String getRequestMethod(); + + /** + * Get the URI representation for the current request. + * + * @return the URI representation for the current request. + */ + URI getRequestURI(); + + /** + * Get the request path. This is the path relative to the context path. E.g.: for a HTTP GET request to + * http://my.appserver.com/my-application/path/sub-path this method is going to return /path/sub-path. + * + * @return the request path + */ + String getRequestPath(); + + /** + * Returns a remotely authenticated user + * + * @return the remote user principal or {@code null} if no remote user was authenticated. + */ + default String getRemoteUser() { + return null; + } + + /** + * Returns the parameters received in the current request. + * + * These parameters will be from both the query string and the form data when available. + * + * Where a parameter is named both in the query string and in the form data the {@link List} will contain the values from the query + * string followed by the values from the form data. + * + * @return the parameters received in the current request. + */ + Map> getParameters(); + + /** + * Returns the names of all parameters either from the query string or from the form data where available. + * + * @return the names of all parameters either from the query string or from the form data where available. + */ + Set getParameterNames(); + + /** + * Return the values for the parameter specified, where a parameter is specified both in the query string and in the form data the query string values will be first in the {@link List}. + * + * @param name the name of the desires parameter values. + * @return the values for the parameter specified or {@code null} if the parameter was not in the request. + */ + List getParameterValues(String name); + + /** + * Get the first value for the parameter specified. + * + * @param name the name of the parameter the first value is required for. + * @return the first value of the named parameter or {@code null} if the parameter is not available. + */ + String getFirstParameterValue(String name); + + /** + * Returns a {@link List} containing all of the {@link HttpServerCookie} objects the client sent with this request, or an empty {@link List} if no cookies were included in the request. + * + * @return a {@link List} containing all of the {@link HttpServerCookie} objects the client sent with this request, or an empty {@link List} if no cookies were included in the request. + */ + List getCookies(); + + /** + * Returns the request input stream. + * + * @return the input stream or {@code null} if not supported. + */ + InputStream getInputStream(); + + /** + * Get the source address of the HTTP request. + * + * @return the source address of the HTTP request + */ + InetSocketAddress getSourceAddress(); + + /** + * Suspend the current request so that it can be subsequently resumed. + * + * @return {@code true} if suspending requests is supported, {@code false} otherwise. + */ + boolean suspendRequest(); + + /** + * Resume any previously suspended request. + * + * @return {@code true} if resuming requests is supported, {@code false} otherwise. + */ + boolean resumeRequest(); + + /** + * Set the request input stream supplier. The default implementation does nothing. + * + * @param requestInputStreamSupplier the request input stream supplier + */ + default void setRequestInputStreamSupplier(Supplier requestInputStreamSupplier) {} + + +} Index: 3rdParty_sources/elytron/org/wildfly/security/http/HttpServerRequestWrapper.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/http/HttpServerRequestWrapper.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/http/HttpServerRequestWrapper.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,180 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2016 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.http; + +import java.io.InputStream; +import java.net.InetSocketAddress; +import java.net.URI; +import java.security.cert.Certificate; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.net.ssl.SSLSession; + +import org.wildfly.common.Assert; + +/** + * A wrapper delegating any request to the delegated implementation. + *

+ * Determined to be used as base class for {@link HttpServerRequest} without need to implement + * methods delegating requests to the delegated implementation. + * + * @author Pedro Igor + */ +public class HttpServerRequestWrapper implements HttpServerRequest { + + private final HttpServerRequest delegate; + + /** + * Construct new instance. + * + * @param delegate a request to which will be delegated any requests + */ + public HttpServerRequestWrapper(HttpServerRequest delegate) { + Assert.checkNotNullParam("delegate", delegate); + this.delegate = delegate; + } + + @Override + public HttpScope getScope(Scope scope) { + return delegate.getScope(scope); + } + + @Override + public Collection getScopeIds(Scope scope) { + return delegate.getScopeIds(scope); + } + + @Override + public HttpScope getScope(Scope scope, String id) { + return delegate.getScope(scope, id); + } + + @Override + public List getRequestHeaderValues(String headerName) { + return delegate.getRequestHeaderValues(headerName); + } + + @Override + public String getFirstRequestHeaderValue(String headerName) { + return delegate.getFirstRequestHeaderValue(headerName); + } + + @Override + public SSLSession getSSLSession() { + return delegate.getSSLSession(); + } + + @Override + public Certificate[] getPeerCertificates() { + return delegate.getPeerCertificates(); + } + + @Override + public void noAuthenticationInProgress(HttpServerMechanismsResponder responder) { + delegate.noAuthenticationInProgress(responder); + } + + @Override + public void authenticationInProgress(HttpServerMechanismsResponder responder) { + delegate.authenticationInProgress(responder); + } + + @Override + public void authenticationComplete(HttpServerMechanismsResponder responder) { + delegate.authenticationComplete(responder); + } + + @Override + public void authenticationComplete(HttpServerMechanismsResponder responder, Runnable logoutHandler) { + delegate.authenticationComplete(responder, logoutHandler); + } + + @Override + public void authenticationFailed(String message, HttpServerMechanismsResponder responder) { + delegate.authenticationFailed(message, responder); + } + + @Override + public void badRequest(HttpAuthenticationException failure, HttpServerMechanismsResponder responder) { + delegate.badRequest(failure, responder); + } + + @Override + public String getRequestMethod() { + return delegate.getRequestMethod(); + } + + @Override + public URI getRequestURI() { + return delegate.getRequestURI(); + } + + @Override + public String getRequestPath() { + return delegate.getRequestPath(); + } + + @Override + public Map> getParameters() { + return delegate.getParameters(); + } + + @Override + public Set getParameterNames() { + return delegate.getParameterNames(); + } + + @Override + public List getParameterValues(String name) { + return delegate.getParameterValues(name); + } + + @Override + public String getFirstParameterValue(String name) { + return delegate.getFirstParameterValue(name); + } + + @Override + public List getCookies() { + return delegate.getCookies(); + } + + @Override + public InputStream getInputStream() { + return delegate.getInputStream(); + } + + @Override + public InetSocketAddress getSourceAddress() { + return delegate.getSourceAddress(); + } + + @Override + public boolean suspendRequest() { + return delegate.suspendRequest(); + } + + @Override + public boolean resumeRequest() { + return delegate.resumeRequest(); + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/http/HttpServerResponse.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/http/HttpServerResponse.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/http/HttpServerResponse.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,67 @@ +/* + * 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.http; + +import java.io.OutputStream; + +/** + * Server side representation of a HTTP response. + * + * @author Darran Lofthouse + */ +public interface HttpServerResponse { + + /** + * Add the specified header and value to the end of the current response headers, + * + * @param headerName the name of the header. + * @param headerValue the value of the header. + */ + void addResponseHeader(final String headerName, final String headerValue); + + /** + * Set the desired status code for the current request. + * + * Note: If multiple mechanisms call this method then a resolution process will begin to decide which one to use. + * + * @param statusCode the response code. + */ + void setStatusCode(final int statusCode); + + /** + * Sets a response cookie + * + * @param cookie the cookie + */ + void setResponseCookie(final HttpServerCookie cookie); + + /** + * Returns the output stream. + * + * @return the output stream or {@code null} if not supported. + */ + OutputStream getOutputStream(); + + /** + * Forward the current request to a different path. + * + * @return {@code true} if forwarding was supported and successful, {@code false} otherwise. + */ + boolean forward(String path); + +} Index: 3rdParty_sources/elytron/org/wildfly/security/http/HttpServerScopes.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/http/HttpServerScopes.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/http/HttpServerScopes.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,54 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2016 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.http; + +import java.util.Collection; + +/** + * Interface providing access to context specific {@link HttpScope} instances. + * + * @author Darran Lofthouse + */ +public interface HttpServerScopes { + + /** + * Get the specified {@link HttpScope} if available. + * + * @param scope the type of the scope required. + * @return the scope specified or {@code null} if not supported. + */ + HttpScope getScope(Scope scope); + + /** + * Get the IDs available for the scope specified. + * + * @param scope the scope the IDs are required for. + * @return The IDs available for the scope specified or {@code null} if the scope specified does not support obtaining scopes by ID. + */ + Collection getScopeIds(Scope scope); + + /** + * Get the specified {@link HttpScope} with the specified ID. + * + * @param scope the type of the scope required. + * @param id the id of the scope instance required. + * @return the scope specified or {@code null} if not supported or if the scope with that ID does not exist. + */ + HttpScope getScope(Scope scope, String id); + +} Index: 3rdParty_sources/elytron/org/wildfly/security/http/Scope.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/http/Scope.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/http/Scope.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,58 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2016 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.http; + +/** + * Enumeration of the available scopes during HTTP request handling. + * + * @author Darran Lofthouse + */ +public enum Scope { + + /** + * The scope applicable to the application (or deployment) being accessed. + */ + APPLICATION, + + /** + * The scope applicable to the connection handling the incoming request. + * + */ + CONNECTION, + + /** + * The scope applicable to the current request/response exchange. + */ + EXCHANGE, + + /** + * The scope applicable to the whole JVM / Process. + */ + GLOBAL, + + /** + * The scope applicable to any underlying session. + */ + SESSION, + + /** + * The scope applicable to any underlying SSL Session. + */ + SSL_SESSION; + +} Index: 3rdParty_sources/elytron/org/wildfly/security/http/form/ElytronMessages.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/http/form/ElytronMessages.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/http/form/ElytronMessages.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,48 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2021 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.http.form; + +import static org.jboss.logging.Logger.Level.WARN; + +import org.jboss.logging.BasicLogger; +import org.jboss.logging.Logger; +import org.jboss.logging.annotations.LogMessage; +import org.jboss.logging.annotations.Message; +import org.jboss.logging.annotations.MessageLogger; +import org.jboss.logging.annotations.ValidIdRange; +import org.jboss.logging.annotations.ValidIdRanges; + +/** + * Log messages and exceptions for Elytron. + * + * @author Darran Lofthouse + */ +@MessageLogger(projectCode = "ELY", length = 5) +@ValidIdRanges({ + @ValidIdRange(min = 22000, max = 22499) +}) +interface ElytronMessages extends BasicLogger { + + ElytronMessages log = Logger.getMessageLogger(ElytronMessages.class, "org.wildfly.security.http.form"); + + @Message(id = 22000, value = "Changing the session ID has been disabled, ensure session tracking uses cookies only to avoid session fixation.") + @LogMessage(level = WARN) + void sessionIdChangeDiabled(); + +} Index: 3rdParty_sources/elytron/org/wildfly/security/http/form/ElytronMessages_$logger.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/http/form/ElytronMessages_$logger.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/http/form/ElytronMessages_$logger.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,34 @@ +package org.wildfly.security.http.form; + +import java.util.Locale; +import java.io.Serializable; +import javax.annotation.Generated; +import org.jboss.logging.DelegatingBasicLogger; +import org.jboss.logging.BasicLogger; +import org.jboss.logging.Logger; + + +import static org.jboss.logging.Logger.Level.WARN; + +/** + * Warning this class consists of generated code. + */ +@Generated(value = "org.jboss.logging.processor.generator.model.MessageLoggerImplementor", date = "2024-01-15T21:50:39+0100") +public class ElytronMessages_$logger extends DelegatingBasicLogger implements ElytronMessages, BasicLogger, Serializable { + private static final long serialVersionUID = 1L; + private static final String FQCN = ElytronMessages_$logger.class.getName(); + public ElytronMessages_$logger(final Logger log) { + super(log); + } + private static final Locale LOCALE = Locale.ROOT; + protected Locale getLoggingLocale() { + return LOCALE; + } + @Override + public final void sessionIdChangeDiabled() { + super.log.logf(FQCN, WARN, null, sessionIdChangeDiabled$str()); + } + protected String sessionIdChangeDiabled$str() { + return "ELY22000: Changing the session ID has been disabled, ensure session tracking uses cookies only to avoid session fixation."; + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/http/form/FormAuthenticationMechanism.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/http/form/FormAuthenticationMechanism.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/http/form/FormAuthenticationMechanism.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,421 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2016 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.http.form; + +import static java.util.Arrays.fill; +import static org.wildfly.common.Assert.checkNotNullParam; +import static org.wildfly.security.http.HttpConstants.CONFIG_CONTEXT_PATH; +import static org.wildfly.security.http.HttpConstants.CONFIG_ERROR_PAGE; +import static org.wildfly.security.http.HttpConstants.CONFIG_LOGIN_PAGE; +import static org.wildfly.security.http.HttpConstants.CONFIG_POST_LOCATION; +import static org.wildfly.security.http.HttpConstants.DISABLE_SESSION_ID_CHANGE; +import static org.wildfly.security.http.HttpConstants.FORM_NAME; +import static org.wildfly.security.http.HttpConstants.FOUND; +import static org.wildfly.security.http.HttpConstants.HTTP; +import static org.wildfly.security.http.HttpConstants.HTTPS; +import static org.wildfly.security.http.HttpConstants.LOCATION; +import static org.wildfly.security.http.HttpConstants.POST; +import static org.wildfly.security.mechanism._private.ElytronMessages.httpForm; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.URI; +import java.util.Map; + +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.UnsupportedCallbackException; + +import org.wildfly.security.auth.callback.CachedIdentityAuthorizeCallback; +import org.wildfly.security.auth.server.SecurityIdentity; +import org.wildfly.security.cache.CachedIdentity; +import org.wildfly.security.cache.IdentityCache; +import org.wildfly.security.http.HttpAuthenticationException; +import org.wildfly.security.http.HttpScope; +import org.wildfly.security.http.HttpServerMechanismsResponder; +import org.wildfly.security.http.HttpServerRequest; +import org.wildfly.security.http.HttpServerResponse; +import org.wildfly.security.http.Scope; +import org.wildfly.security.mechanism.http.UsernamePasswordAuthenticationMechanism; + +/** + * A generic FORM authentication mechanism which is usable in a number of different scenarios. + * + * @author Darran Lofthouse + */ +final class FormAuthenticationMechanism extends UsernamePasswordAuthenticationMechanism { + + /* + * These two could also be made configurable but defer until proven demand. + */ + + private static final String USERNAME = "j_username"; + private static final String PASSWORD = "j_password"; + + private static final String LOCATION_KEY = FormAuthenticationMechanism.class.getName() + ".Location"; + private static final String CACHED_IDENTITY_KEY = FormAuthenticationMechanism.class.getName() + ".elytron-identity"; + + private static final String DEFAULT_POST_LOCATION = "j_security_check"; + + private final String contextPath; + private final String loginPage; + private final String errorPage; + private final String postLocation; + private final boolean disableSessionIdChange; + + FormAuthenticationMechanism(final CallbackHandler callbackHandler, final Map properties) { + super(checkNotNullParam("callbackHandler", callbackHandler)); + checkNotNullParam("properties", properties); + + String postLocation = (String) properties.get(CONFIG_POST_LOCATION); + this.postLocation = postLocation != null ? postLocation : DEFAULT_POST_LOCATION; + + contextPath = properties.containsKey(CONFIG_CONTEXT_PATH) ? (String) properties.get(CONFIG_CONTEXT_PATH) : ""; + loginPage = (String) properties.get(CONFIG_LOGIN_PAGE); + errorPage = (String) properties.get(CONFIG_ERROR_PAGE); + disableSessionIdChange = Boolean.valueOf((String) properties.get(DISABLE_SESSION_ID_CHANGE)); + } + + @Override + public String getMechanismName() { + return FORM_NAME; + } + + /** + * @see org.wildfly.security.http.HttpServerAuthenticationMechanism#evaluateRequest(org.wildfly.security.http.HttpServerRequest) + */ + @Override + public void evaluateRequest(final HttpServerRequest request) throws HttpAuthenticationException { + // Is current request an authentication attempt? + if (POST.equals(request.getRequestMethod()) && isAuthenticationRequest(request.getRequestURI().getPath())) { + attemptAuthentication(request); + return; + } + + // try to re-authenticate based on a previously cached identity + if (attemptReAuthentication(request)) { + return; + } + + // Register challenger + if (loginPage != null) { + request.noAuthenticationInProgress((response) -> sendLogin(request, response)); + } + } + + private boolean isAuthenticationRequest(final String path) { + int lastSlash = path.lastIndexOf('/'); + int pathParam = path.indexOf(';', lastSlash > 0 ? lastSlash : 0); + String target = path.substring(lastSlash >= 0 ? lastSlash + 1 : 0, pathParam > 0 ? pathParam : path.length()); + + return target.equals(postLocation); + } + + private IdentityCache createIdentityCache(HttpServerRequest request) { + return new IdentityCache() { + @Override + public void put(SecurityIdentity identity) { + HttpScope session = getSessionScope(request, true); + + if (session == null || !session.exists()) { + return; + } + + /* + * If we are associating an identity with the session for the first time we need to + * change the ID of the session, in other cases we can continue with the same ID. + */ + if (!disableSessionIdChange && session.supportsChangeID() && session.getAttachment(CACHED_IDENTITY_KEY) == null) { + String originalSessionID = session.getID(); + session.changeID(); + String newSessionID = session.getID(); + fixCachedLocation(session, originalSessionID, newSessionID); + } + + session.setAttachment(CACHED_IDENTITY_KEY, new CachedIdentity(getMechanismName(), false, identity)); + } + + @Override + public CachedIdentity get() { + HttpScope session = getSessionScope(request, false); + + if (session == null || !session.exists()) { + return null; + } + + return (CachedIdentity) session.getAttachment(CACHED_IDENTITY_KEY); + } + + @Override + public CachedIdentity remove() { + HttpScope session = getSessionScope(request, false); + + if (session == null || !session.exists()) { + return null; + } + + CachedIdentity cachedIdentity = get(); + + session.setAttachment(CACHED_IDENTITY_KEY, null); + + return cachedIdentity; + } + }; + } + + private void fixCachedLocation(HttpScope scope, String originalSessionID, String newSessionID) { + String originalPath = scope.getAttachment(LOCATION_KEY, String.class); + if (originalPath != null && originalPath.contains(originalSessionID) && !originalSessionID.equals(newSessionID)) { + String newPath = originalPath.replace(originalSessionID, newSessionID); + scope.setAttachment(LOCATION_KEY, newPath); + } + } + + private void error(String message, HttpServerRequest request) { + request.authenticationFailed(message, (response) -> sendPage(errorPage, request, response)); + } + + private void attemptAuthentication(HttpServerRequest request) throws HttpAuthenticationException { + String username = request.getFirstParameterValue(USERNAME); + String password = request.getFirstParameterValue(PASSWORD); + + if (username == null || username.length() == 0 || password == null) { + error(httpForm.usernameOrPasswordMissing(), request); + return; + } + + char[] passwordChars = password.toCharArray(); + try { + if (authenticate(null, username, passwordChars)) { + IdentityCache identityCache = createIdentityCache(request); + identityCache.remove(); // We are mid NEW authentication so don't want to import an old identity. + if (authorize(username, request, identityCache)) { + httpForm.debugf("User [%s] authenticated successfully", username); + succeed(); + + HttpScope session = getSessionScope(request, true); + HttpServerMechanismsResponder responder = null; + if (session != null && session.exists()) { + String postAuthenticationPath; + String originalPath = session.getAttachment(LOCATION_KEY, String.class); + if (originalPath != null) { + postAuthenticationPath = originalPath; + httpForm.tracef("User redirected to original path [%s]", postAuthenticationPath); + } else { + URI requestUri = request.getRequestURI(); + String currentPath = requestUri.getPath(); + + StringBuilder sb = new StringBuilder(); + String scheme = requestUri.getScheme(); + sb.append(scheme); + sb.append("://"); + sb.append(requestUri.getHost()); + int port = requestUri.getPort(); + if (appendPort(scheme, port)) { + sb.append(':').append(port); + } + sb.append(currentPath.substring(0, currentPath.indexOf(DEFAULT_POST_LOCATION))); + + postAuthenticationPath = sb.toString(); + httpForm.tracef("User redirected to default path [%s]", postAuthenticationPath); + } + session.setAttachment(LOCATION_KEY, null); + responder = (response) -> sendRedirect(response, postAuthenticationPath); + } + + request.authenticationComplete(responder, identityCache::remove); + // no resumeRequest here, need to redirect first + return; + } else { + httpForm.debugf("User [%s] authorization failed", username); + failAndRedirectToErrorPage(request, username); + return; + } + + } else { + httpForm.debugf("User [%s] authentication failed", username); + failAndRedirectToErrorPage(request, username); + return; + } + } catch (IOException | UnsupportedCallbackException e) { + throw new HttpAuthenticationException(e); + } finally { + fill(passwordChars, (char) 0x00); + } + } + + private boolean authorize(String username, HttpServerRequest request, IdentityCache identityCache) throws HttpAuthenticationException { + httpForm.tracef("Authorizing username: [%s], Request URI: [%s], Context path: [%s]", username, request.getRequestURI(), this.contextPath); + + if (identityCache != null) { + CachedIdentityAuthorizeCallback authorizeCallback = new CachedIdentityAuthorizeCallback(username, identityCache); + try { + callbackHandler.handle(new Callback[]{authorizeCallback}); + return authorizeCallback.isAuthorized(); + } catch (IOException | UnsupportedCallbackException e) { + throw new HttpAuthenticationException(e); + } + } + return super.authorize(username); + } + + private boolean attemptReAuthentication(HttpServerRequest request) throws HttpAuthenticationException { + if (httpForm.isTraceEnabled()) { + HttpScope sessionScope = getSessionScope(request, false); + if (sessionScope != null && sessionScope.exists()) { + httpForm.tracef("Trying to re-authenticate session %s. Request URI: [%s], Context path: [%s]", + sessionScope.getID(), request.getRequestURI(), this.contextPath); + } else { + httpForm.tracef("Trying to re-authenticate. There is no session attached to the following request. " + + "Request URI: [%s], Context path: [%s]", request.getRequestURI(), this.contextPath); + } + } + + IdentityCache identityCache = createIdentityCache(request); + if (identityCache != null) { + CachedIdentityAuthorizeCallback authorizeCallback = new CachedIdentityAuthorizeCallback(identityCache); + try { + callbackHandler.handle(new Callback[]{authorizeCallback}); + } catch (IOException | UnsupportedCallbackException e) { + throw new HttpAuthenticationException(e); + } + if (authorizeCallback.isAuthorized()) { + try { + succeed(); + } catch (IOException | UnsupportedCallbackException e) { + throw new HttpAuthenticationException(e); + } + request.authenticationComplete(null, identityCache::remove); + request.resumeRequest(); + return true; + } + } + return false; + } + + private void failAndRedirectToErrorPage(HttpServerRequest request, String username) throws IOException, UnsupportedCallbackException { + IdentityCache identityCache = createIdentityCache(request); + if (identityCache != null) { + identityCache.remove(); + } + fail(); + error(httpForm.authorizationFailed(username), request); + } + + private void sendLogin(HttpServerRequest request, HttpServerResponse response) throws HttpAuthenticationException { + if (request.getRequestPath().isEmpty() && !contextPath.isEmpty()) { + sendRedirect(response, getCompleteRedirectLocation(request, "/")); + return; + } + + // Save the current request. + URI requestURI = request.getRequestURI(); + HttpScope session = getSessionScope(request, true); + if (session != null && session.supportsAttachments()) { + StringBuilder sb = new StringBuilder(); + String scheme = requestURI.getScheme(); + sb.append(scheme); + sb.append("://"); + sb.append(requestURI.getHost()); + int port = requestURI.getPort(); + if (appendPort(scheme, port)) { + sb.append(':').append(port); + } + sb.append(requestURI.getPath()); + if(requestURI.getRawQuery() != null) { + sb.append("?"); + sb.append(requestURI.getRawQuery()); + } + if(requestURI.getRawFragment() != null) { + sb.append("#"); + sb.append(requestURI.getRawFragment()); + } + //TODO: we need to have some way up updating the jsessionid path parameter if the session ID changes + //see UNDERTOW-958 for more details + session.setAttachment(LOCATION_KEY, sb.toString()); + request.suspendRequest(); + } + + sendPage(loginPage, request, response); + } + + private void sendPage(String page, HttpServerRequest request, HttpServerResponse response) throws HttpAuthenticationException { + if (response.forward(page)) { + return; + } + // Work out how and send the login page. + HttpScope application = request.getScope(Scope.APPLICATION); + if (application != null && application.supportsResources()) { + try (InputStream pageStream = application.getResource(page)) { + if (pageStream != null) { + OutputStream responseStream = response.getOutputStream(); + if (responseStream != null) { + byte[] content = new byte[1024]; + int length; + while ((length = pageStream.read(content)) > 0) { + responseStream.write(content, 0, length); + } + + return; + } + } + } catch (IOException e) { + throw new HttpAuthenticationException(e); + } + } + + sendRedirect(response, getCompleteRedirectLocation(request, page)); + } + + private String getCompleteRedirectLocation(HttpServerRequest request, String location) { + URI requestURI = request.getRequestURI(); + StringBuilder sb = new StringBuilder(); + String scheme = requestURI.getScheme(); + sb.append(scheme); + sb.append("://"); + sb.append(requestURI.getHost()); + int port = requestURI.getPort(); + if (appendPort(scheme, port)) { + sb.append(':').append(port); + } + sb.append(contextPath); + sb.append(location); + + return sb.toString(); + } + + private void sendRedirect(HttpServerResponse response, String location) { + response.addResponseHeader(LOCATION, location); + response.setStatusCode(FOUND); + } + + private HttpScope getSessionScope(HttpServerRequest request, boolean createSession) { + HttpScope scope = request.getScope(Scope.SESSION); + + if (scope != null &&!scope.exists() && createSession) { + scope.create(); + } + + return scope; + } + + private static boolean appendPort(final String scheme, final int port) { + return port > -1 && ((HTTP.equalsIgnoreCase(scheme) && port != 80) || (HTTPS.equalsIgnoreCase(scheme) && port != 443)); + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/http/form/FormMechanismFactory.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/http/form/FormMechanismFactory.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/http/form/FormMechanismFactory.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,84 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2018 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.http.form; + +import static org.wildfly.common.Assert.checkNotNullParam; +import static org.wildfly.security.http.HttpConstants.DISABLE_SESSION_ID_CHANGE; +import static org.wildfly.security.http.HttpConstants.FORM_NAME; +import static org.wildfly.security.http.form.ElytronMessages.log; + +import java.security.Provider; +import java.util.Map; +import java.util.concurrent.atomic.AtomicBoolean; + +import javax.security.auth.callback.CallbackHandler; + +import org.kohsuke.MetaInfServices; + +import org.wildfly.security.http.HttpAuthenticationException; +import org.wildfly.security.http.HttpServerAuthenticationMechanism; +import org.wildfly.security.http.HttpServerAuthenticationMechanismFactory; + +/** + * A {@link HttpServerAuthenticationMechanismFactory} implementation for the FORM HTTP authentication mechanism. + * + * @author Darran Lofthouse + */ +@MetaInfServices(value = HttpServerAuthenticationMechanismFactory.class) +public class FormMechanismFactory implements HttpServerAuthenticationMechanismFactory { + + private static final AtomicBoolean ID_CHANGE_LOGGED = new AtomicBoolean(false); + + public FormMechanismFactory() { + } + + public FormMechanismFactory(final Provider provider) { + } + + /** + * @see org.wildfly.security.http.HttpServerAuthenticationMechanismFactory#getMechanismNames(Map) + */ + @Override + public String[] getMechanismNames(Map properties) { + return new String[]{FORM_NAME}; + } + + /** + * @see org.wildfly.security.http.HttpServerAuthenticationMechanismFactory#createAuthenticationMechanism(String, + * Map, CallbackHandler) + */ + @Override + public HttpServerAuthenticationMechanism createAuthenticationMechanism(String mechanismName, + Map properties, CallbackHandler callbackHandler) throws HttpAuthenticationException { + checkNotNullParam("mechanismName", mechanismName); + checkNotNullParam("properties", properties); + checkNotNullParam("callbackHandler", callbackHandler); + + if (FORM_NAME.equals(mechanismName)) { + if (Boolean.valueOf((String) properties.get(DISABLE_SESSION_ID_CHANGE)) && !ID_CHANGE_LOGGED.getAndSet(true)) { + log.sessionIdChangeDiabled(); + } + + return new FormAuthenticationMechanism(callbackHandler, properties); + } + + return null; + } + +} \ No newline at end of file Index: 3rdParty_sources/elytron/org/wildfly/security/http/form/WildFlyElytronHttpFormProvider.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/http/form/WildFlyElytronHttpFormProvider.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/http/form/WildFlyElytronHttpFormProvider.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,54 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2018 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.http.form; + +import java.security.Provider; + +import org.kohsuke.MetaInfServices; +import org.wildfly.security.WildFlyElytronBaseProvider; + +/** + * Provider for the HTTP FORM authentication mechanism. + * + * @author Farah Juma + */ +@MetaInfServices(Provider.class) +public final class WildFlyElytronHttpFormProvider extends WildFlyElytronBaseProvider { + + private static final long serialVersionUID = 3872696509387755963L; + private static WildFlyElytronHttpFormProvider INSTANCE = new WildFlyElytronHttpFormProvider(); + + /** + * Construct a new instance. + */ + public WildFlyElytronHttpFormProvider() { + super("WildFlyElytronHttpFormProvider", "1.0", "WildFly Elytron HTTP FORM Provider"); + putService(new ProviderService(this, HTTP_SERVER_FACTORY_TYPE, "FORM", "org.wildfly.security.http.form.FormMechanismFactory", emptyList, emptyMap, true, true)); + } + + /** + * Get the HTTP FORM authentication mechanism provider instance. + * + * @return the HTTP FORM authentication mechanism provider instance + */ + public static WildFlyElytronHttpFormProvider getInstance() { + return INSTANCE; + } + +} Index: 3rdParty_sources/elytron/org/wildfly/security/http/impl/BaseHttpServerRequest.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/http/impl/BaseHttpServerRequest.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/http/impl/BaseHttpServerRequest.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,139 @@ +/* + * Copyright 2021 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.http.impl; + +import java.io.InputStream; +import java.net.InetSocketAddress; +import java.net.URI; +import java.security.cert.Certificate; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.net.ssl.SSLSession; + +import org.wildfly.security.http.HttpExchangeSpi; +import org.wildfly.security.http.HttpScope; +import org.wildfly.security.http.HttpServerCookie; +import org.wildfly.security.http.HttpServerRequest; +import org.wildfly.security.http.Scope; + +/** + * A base implementation of {@link HttpServerRequest} + * + * @author Darran Lofthouse + */ +public abstract class BaseHttpServerRequest implements HttpServerRequest { + + private final HttpExchangeSpi httpExchangeSpi; + + protected BaseHttpServerRequest(final HttpExchangeSpi httpExchangeSpi) { + this.httpExchangeSpi = httpExchangeSpi; + } + + @Override + public HttpScope getScope(Scope scope) { + return httpExchangeSpi.getScope(scope); + } + + @Override + public Collection getScopeIds(Scope scope) { + return httpExchangeSpi.getScopeIds(scope); + } + + @Override + public HttpScope getScope(Scope scope, String id) { + return httpExchangeSpi.getScope(scope, id); + } + + @Override + public List getRequestHeaderValues(String headerName) { + return httpExchangeSpi.getRequestHeaderValues(headerName); + } + + @Override + public String getFirstRequestHeaderValue(String headerName) { + return httpExchangeSpi.getFirstRequestHeaderValue(headerName); + } + + @Override + public SSLSession getSSLSession() { + return httpExchangeSpi.getSSLSession(); + } + + @Override + public Certificate[] getPeerCertificates() { + return httpExchangeSpi.getPeerCertificates(false); + } + + @Override + public String getRemoteUser() { + return httpExchangeSpi.getRemoteUser(); + } + + @Override + public String getRequestMethod() { + return httpExchangeSpi.getRequestMethod(); + } + + @Override + public URI getRequestURI() { + return httpExchangeSpi.getRequestURI(); + } + + @Override + public String getRequestPath() { + return httpExchangeSpi.getRequestPath(); + } + + @Override + public Map> getParameters() { + return httpExchangeSpi.getRequestParameters(); + } + + @Override + public Set getParameterNames() { + return httpExchangeSpi.getRequestParameterNames(); + } + + @Override + public List getParameterValues(String name) { + return httpExchangeSpi.getRequestParameterValues(name); + } + + @Override + public String getFirstParameterValue(String name) { + return httpExchangeSpi.getFirstRequestParameterValue(name); + } + + @Override + public List getCookies() { + return httpExchangeSpi.getCookies(); + } + + @Override + public InputStream getInputStream() { + return httpExchangeSpi.getRequestInputStream(); + } + + @Override + public InetSocketAddress getSourceAddress() { + return httpExchangeSpi.getSourceAddress(); + } + +} Index: 3rdParty_sources/elytron/org/wildfly/security/http/impl/package-info.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/http/impl/package-info.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/http/impl/package-info.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,22 @@ +/* + * Copyright 2021 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. + */ + +/** + * Implementation classes which do not form part of the public API. + * + * @author Darran Lofthouse + */ +package org.wildfly.security.http.impl; \ No newline at end of file Index: 3rdParty_sources/elytron/org/wildfly/security/http/package-info.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/http/package-info.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/http/package-info.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,24 @@ +/* + * 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 containing the HTTP based authentication APIs, SPIs and related classes. + * + * @author Darran Lofthouse + */ +package org.wildfly.security.http; \ No newline at end of file Index: 3rdParty_sources/elytron/org/wildfly/security/http/util/AggregateServerMechanismFactory.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/http/util/AggregateServerMechanismFactory.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/http/util/AggregateServerMechanismFactory.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,94 @@ +/* + * 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.http.util; + +import static org.wildfly.common.Assert.checkNotNullParam; +import static org.wildfly.security.http.util.ElytronMessages.log; + +import java.util.Arrays; +import java.util.Collections; +import java.util.LinkedHashSet; +import java.util.Map; + +import javax.security.auth.callback.CallbackHandler; + +import org.wildfly.security.http.HttpAuthenticationException; +import org.wildfly.security.http.HttpServerAuthenticationMechanism; +import org.wildfly.security.http.HttpServerAuthenticationMechanismFactory; + +/** + * A {@link HttpServerAuthenticationMechanismFactory} that is an aggregation of other factories. + * + * @author Darran Lofthouse + */ +public final class AggregateServerMechanismFactory implements HttpServerAuthenticationMechanismFactory { + + private final HttpServerAuthenticationMechanismFactory[] factories; + + /** + * Construct an instance of {@code AggregateServerMechanismFactory} with an array of factories to aggregate. + * + * @param factories the array of factories to aggregate. + */ + public AggregateServerMechanismFactory(HttpServerAuthenticationMechanismFactory... factories) { + this.factories = checkNotNullParam("factories", factories); + } + + /** + * @see org.wildfly.security.http.HttpServerAuthenticationMechanismFactory#getMechanismNames(Map) + */ + @Override + public String[] getMechanismNames(Map properties) { + LinkedHashSet names = new LinkedHashSet<>(); + for (HttpServerAuthenticationMechanismFactory current : factories) { + if (current != null) { + Collections.addAll(names, current.getMechanismNames(properties)); + } + } + if (log.isTraceEnabled()) { + log.tracef("%s provided by factories in %s: %s", HttpServerAuthenticationMechanismFactory.class.getSimpleName(), getClass().getSimpleName(), Arrays.toString(factories)); + } + return names.toArray(new String[names.size()]); + } + + /** + * @throws HttpAuthenticationException if there is a problem creating the mechanism instance. + * @see org.wildfly.security.http.HttpServerAuthenticationMechanismFactory#createAuthenticationMechanism(String, + * Map, CallbackHandler) + */ + @Override + public HttpServerAuthenticationMechanism createAuthenticationMechanism(String mechanismName, Map properties, + CallbackHandler callbackHandler) throws HttpAuthenticationException { + for (HttpServerAuthenticationMechanismFactory current : factories) { + if (current != null) { + HttpServerAuthenticationMechanism mechanism = current.createAuthenticationMechanism(mechanismName, properties, + callbackHandler); + if (mechanism != null) { + return mechanism; + } + } + } + if (log.isTraceEnabled()) { + log.tracef("Mechanism %s not provided as %s is not provided by factories in %s: %s", + mechanismName, HttpServerAuthenticationMechanismFactory.class.getSimpleName(), + getClass().getSimpleName(), Arrays.toString(factories)); + } + return null; + } + +} \ No newline at end of file Index: 3rdParty_sources/elytron/org/wildfly/security/http/util/ElytronMessages.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/http/util/ElytronMessages.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/http/util/ElytronMessages.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,47 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2014 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.http.util; + +import org.jboss.logging.BasicLogger; +import org.jboss.logging.Logger; +import org.jboss.logging.annotations.Cause; +import org.jboss.logging.annotations.Message; +import org.jboss.logging.annotations.MessageLogger; +import org.jboss.logging.annotations.ValidIdRange; +import org.jboss.logging.annotations.ValidIdRanges; +import org.wildfly.security.mechanism.AuthenticationMechanismException; + +/** + * Log messages and exceptions for Elytron. + * + * @author David M. Lloyd + * @author Darran Lofthouse + */ +@MessageLogger(projectCode = "ELY", length = 5) +@ValidIdRanges({ + @ValidIdRange(min = 5172, max = 5172) +}) +interface ElytronMessages extends BasicLogger { + + ElytronMessages log = Logger.getMessageLogger(ElytronMessages.class, "org.wildfly.security"); + + @Message(id = 5172, value = "Unable to locate MechanismConfiguration for mechanism.") + AuthenticationMechanismException unableToLocateMechanismConfiguration(@Cause Throwable cause); +} + Index: 3rdParty_sources/elytron/org/wildfly/security/http/util/ElytronMessages_$logger.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/http/util/ElytronMessages_$logger.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/http/util/ElytronMessages_$logger.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,40 @@ +package org.wildfly.security.http.util; + +import java.util.Locale; +import java.io.Serializable; +import javax.annotation.Generated; +import org.jboss.logging.DelegatingBasicLogger; +import org.jboss.logging.BasicLogger; +import java.lang.Throwable; +import org.jboss.logging.Logger; +import java.util.Arrays; +import org.wildfly.security.mechanism.AuthenticationMechanismException; + +/** + * Warning this class consists of generated code. + */ +@Generated(value = "org.jboss.logging.processor.generator.model.MessageLoggerImplementor", date = "2024-01-15T21:50:57+0100") +public class ElytronMessages_$logger extends DelegatingBasicLogger implements ElytronMessages, BasicLogger, Serializable { + private static final long serialVersionUID = 1L; + private static final String FQCN = ElytronMessages_$logger.class.getName(); + public ElytronMessages_$logger(final Logger log) { + super(log); + } + private static final Locale LOCALE = Locale.ROOT; + protected Locale getLoggingLocale() { + return LOCALE; + } + protected String unableToLocateMechanismConfiguration$str() { + return "ELY05172: Unable to locate MechanismConfiguration for mechanism."; + } + @Override + public final AuthenticationMechanismException unableToLocateMechanismConfiguration(final Throwable cause) { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), unableToLocateMechanismConfiguration$str()), cause); + _copyStackTraceMinusOne(result); + return result; + } + private static void _copyStackTraceMinusOne(final Throwable e) { + final StackTraceElement[] st = e.getStackTrace(); + e.setStackTrace(Arrays.copyOfRange(st, 1, st.length)); + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/http/util/FilterServerMechanismFactory.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/http/util/FilterServerMechanismFactory.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/http/util/FilterServerMechanismFactory.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,130 @@ +/* + * 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.http.util; + +import static org.wildfly.common.Assert.checkNotNullParam; +import static org.wildfly.security.http.util.ElytronMessages.log; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.function.Predicate; + +import javax.security.auth.callback.CallbackHandler; + +import org.wildfly.security.http.HttpAuthenticationException; +import org.wildfly.security.http.HttpServerAuthenticationMechanism; +import org.wildfly.security.http.HttpServerAuthenticationMechanismFactory; + +/** + * An implementation of {@link HttpServerAuthenticationMechanismFactory} that wraps an existing factory and provides mechanism + * filtering by name. + * + * @author Darran Lofthouse + */ +public final class FilterServerMechanismFactory implements HttpServerAuthenticationMechanismFactory { + + private final HttpServerAuthenticationMechanismFactory delegate; + private final Predicate predicate; + + /** + * Constructs a new instance. + * + * @param delegate the {@link HttpServerAuthenticationMechanismFactory} to delegate to. + * @param predicate mechanism name based predicate to filter available mechanisms. + */ + public FilterServerMechanismFactory(final HttpServerAuthenticationMechanismFactory delegate, final Predicate predicate) { + this.delegate = checkNotNullParam("delegate", delegate); + this.predicate = checkNotNullParam("predicate", predicate); + } + + /** + * Construct a new instance that filters from a provided set of mechanism names. + * + * @param delegate the {@link HttpServerAuthenticationMechanismFactory} to delegate to. + * @param include when {@code true} mechanisms will be advertised as available if included in the provided mechanismNames. + * @param mechanismNames the mechanism names to use as a filter. + */ + public FilterServerMechanismFactory(final HttpServerAuthenticationMechanismFactory delegate, final boolean include , String ... mechanismNames) { + this.delegate = checkNotNullParam("delegate", delegate); + final Set nameSet = new HashSet<>(mechanismNames.length); + Collections.addAll(nameSet, mechanismNames); + + Predicate predicate = nameSet::contains; + this.predicate = include ? predicate : predicate.negate(); + } + + /** + * Construct a new instance that filters from a provided set of mechanism names. + * + * @param delegate the {@link HttpServerAuthenticationMechanismFactory} to delegate to. + * @param include when {@code true} mechanisms will be advertised as available if included in the provided mechanismNames. + * @param mechanismNames the mechanism names to use as a filter. + */ + public FilterServerMechanismFactory(final HttpServerAuthenticationMechanismFactory delegate, final boolean include , Collection mechanismNames) { + this.delegate = checkNotNullParam("delegate", delegate); + final Set nameSet = new HashSet<>(checkNotNullParam("mechanismNames", mechanismNames)); + + Predicate predicate = nameSet::contains; + this.predicate = include ? predicate : predicate.negate(); + } + + /** + * Get the available mechanism names after filtering has been performed by the previously provided {@link Predicate} + * + * @param properties the {@link Map} of properties to pass into the {@link HttpServerAuthenticationMechanismFactory#getMechanismNames(Map)} call on the delegate. + * @return The array of filtered mechanism names. + * @see org.wildfly.security.http.HttpServerAuthenticationMechanismFactory#getMechanismNames(Map) + */ + @Override + public String[] getMechanismNames(Map properties) { + String[] allMechanisms = delegate.getMechanismNames(properties); + ArrayList filtered = new ArrayList<>(allMechanisms.length); + for (String current : allMechanisms) { + if (predicate.test(current)) { + filtered.add(current); + } + } + if (filtered.size() == 0 && log.isTraceEnabled()) { + log.tracef("No mechanisms after filtering by %s (original mechanisms: %s)", FilterServerMechanismFactory.class.getSimpleName(), Arrays.toString(allMechanisms)); + } + return filtered.toArray(new String[filtered.size()]); + } + + /** + * Create the requested {@link HttpServerAuthenticationMechanism} provided it is available and allowed by the current filter. + * + * @param mechanismName the name of the required mechanism. + * @param properties the configuration properties to pass in for mechanism creation. + * @param callbackHandler the {@link CallbackHandler} the mechanism should use for verification. + * @return The {@link HttpServerAuthenticationMechanism} or {@code null} if not available. + * @throws HttpAuthenticationException + * @see org.wildfly.security.http.HttpServerAuthenticationMechanismFactory#createAuthenticationMechanism(String, + * Map, CallbackHandler) + */ + @Override + public HttpServerAuthenticationMechanism createAuthenticationMechanism(String mechanismName, Map properties, + CallbackHandler callbackHandler) throws HttpAuthenticationException { + return predicate.test(mechanismName) ? delegate.createAuthenticationMechanism(mechanismName, properties, callbackHandler) : null; + } + +} \ No newline at end of file Index: 3rdParty_sources/elytron/org/wildfly/security/http/util/PrivilegedServerMechanism.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/http/util/PrivilegedServerMechanism.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/http/util/PrivilegedServerMechanism.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,265 @@ +/* + * 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.http.util; + +import static org.wildfly.common.Assert.checkNotNullParam; + +import java.io.InputStream; +import java.lang.reflect.UndeclaredThrowableException; +import java.net.InetSocketAddress; +import java.net.URI; +import java.security.AccessControlContext; +import java.security.AccessController; +import java.security.PrivilegedActionException; +import java.security.PrivilegedExceptionAction; +import java.security.cert.Certificate; +import java.util.Collection; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import javax.net.ssl.SSLSession; + +import org.wildfly.security.http.HttpAuthenticationException; +import org.wildfly.security.http.HttpScope; +import org.wildfly.security.http.HttpServerAuthenticationMechanism; +import org.wildfly.security.http.HttpServerCookie; +import org.wildfly.security.http.HttpServerMechanismsResponder; +import org.wildfly.security.http.HttpServerRequest; +import org.wildfly.security.http.HttpServerResponse; +import org.wildfly.security.http.Scope; + +/** + * A {@link HttpServerAuthenticationMechanism} with a stored {@link AccessControlContext} that is used for all request + * processing calls. + * + * @author Darran Lofthouse + */ +final class PrivilegedServerMechanism implements HttpServerAuthenticationMechanism { + + private final HttpServerAuthenticationMechanism mechanism; + private final AccessControlContext accessControlContext; + + /** + * Construct a new instance of {@code PrivilegedServerMechanism}. + * + * @param mechanism the mechanism to be wrapped. + * @param accessControlContext the {@link AccessControlContext} to use for calls to the wrapped mechanism. + */ + PrivilegedServerMechanism(final HttpServerAuthenticationMechanism mechanism, final AccessControlContext accessControlContext) { + this.mechanism = checkNotNullParam("mechanism", mechanism); + this.accessControlContext = checkNotNullParam("accessControlContext", accessControlContext); + } + + + /** + * @see org.wildfly.security.http.HttpServerAuthenticationMechanism#getMechanismName() + */ + @Override + public String getMechanismName() { + return mechanism.getMechanismName(); + } + + /** + * @see org.wildfly.security.http.HttpServerAuthenticationMechanism#evaluateRequest(org.wildfly.security.http.HttpServerRequest) + */ + @Override + public void evaluateRequest(final HttpServerRequest request) throws HttpAuthenticationException { + try { + AccessController.doPrivileged((PrivilegedExceptionAction) () -> { + mechanism.evaluateRequest(new HttpServerRequestWrapper(request)); + return null; + }, accessControlContext); + } catch (PrivilegedActionException pae) { + try { + throw pae.getCause(); + } catch (HttpAuthenticationException | RuntimeException | Error e) { + throw e; + } catch (Throwable throwable) { + throw new UndeclaredThrowableException(throwable); + } + } + } + + @Override + public Object getNegotiatedProperty(String propertyName) { + return mechanism.getNegotiatedProperty(propertyName); + } + + @Override + public T getNegotiationProperty(String propertyName, Class type) { + return mechanism.getNegotiationProperty(propertyName, type); + } + + + private HttpServerMechanismsResponder wrap(final HttpServerMechanismsResponder toWrap) { + return toWrap != null ? (HttpServerResponse r) -> + { + try { + AccessController.doPrivileged((PrivilegedExceptionAction) () -> { + toWrap.sendResponse(r); + return null; + } + , accessControlContext); + } catch (PrivilegedActionException e) { + if (e.getCause() instanceof HttpAuthenticationException) { + throw (HttpAuthenticationException) e.getCause(); + } + throw new HttpAuthenticationException(e); + } + } + : null; + } + + private class HttpServerRequestWrapper implements HttpServerRequest { + + private final HttpServerRequest wrapped; + + private HttpServerRequestWrapper(HttpServerRequest toWrap) { + wrapped = toWrap; + } + + @Override + public List getRequestHeaderValues(String headerName) { + return wrapped.getRequestHeaderValues(headerName); + } + + @Override + public String getFirstRequestHeaderValue(String headerName) { + return wrapped.getFirstRequestHeaderValue(headerName); + } + + @Override + public SSLSession getSSLSession() { + return wrapped.getSSLSession(); + } + + @Override + public Certificate[] getPeerCertificates() { + return wrapped.getPeerCertificates(); + } + + @Override + public HttpScope getScope(Scope scope) { + return wrapped.getScope(scope); + } + + @Override + public Collection getScopeIds(Scope scope) { + return wrapped.getScopeIds(scope); + } + + @Override + public HttpScope getScope(Scope scope, String id) { + return wrapped.getScope(scope, id); + } + + @Override + public void noAuthenticationInProgress(HttpServerMechanismsResponder responder) { + wrapped.noAuthenticationInProgress(wrap(responder)); + } + + @Override + public void authenticationInProgress(HttpServerMechanismsResponder responder) { + wrapped.authenticationInProgress(wrap(responder)); + } + + @Override + public void authenticationComplete(HttpServerMechanismsResponder responder) { + wrapped.authenticationComplete(wrap(responder)); + } + + @Override + public void authenticationComplete(HttpServerMechanismsResponder responder, Runnable logoutHandler) { + wrapped.authenticationComplete(responder, logoutHandler); + } + + @Override + public void authenticationFailed(String message, HttpServerMechanismsResponder responder) { + wrapped.authenticationFailed(message, wrap(responder)); + } + + @Override + public void badRequest(HttpAuthenticationException failure, HttpServerMechanismsResponder responder) { + wrapped.badRequest(failure, wrap(responder)); + } + + @Override + public String getRequestMethod() { + return wrapped.getRequestMethod(); + } + + @Override + public URI getRequestURI() { + return wrapped.getRequestURI(); + } + + @Override + public String getRequestPath() { + return wrapped.getRequestPath(); + } + + @Override + public Map> getParameters() { + return wrapped.getParameters(); + } + + @Override + public Set getParameterNames() { + return wrapped.getParameterNames(); + } + + @Override + public List getParameterValues(String name) { + return wrapped.getParameterValues(name); + } + + @Override + public String getFirstParameterValue(String name) { + return wrapped.getFirstParameterValue(name); + } + + @Override + public List getCookies() { + return wrapped.getCookies(); + } + + @Override + public InputStream getInputStream() { + return wrapped.getInputStream(); + } + + @Override + public InetSocketAddress getSourceAddress() { + return wrapped.getSourceAddress(); + } + + @Override + public boolean suspendRequest() { + return wrapped.suspendRequest(); + } + + @Override + public boolean resumeRequest() { + return wrapped.resumeRequest(); + } + + + } + +} Index: 3rdParty_sources/elytron/org/wildfly/security/http/util/PrivilegedServerMechanismFactory.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/http/util/PrivilegedServerMechanismFactory.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/http/util/PrivilegedServerMechanismFactory.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,87 @@ +/* + * 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.http.util; + +import static org.wildfly.common.Assert.checkNotNullParam; + +import java.security.AccessControlContext; +import java.security.AccessController; +import java.util.Map; + +import javax.security.auth.callback.CallbackHandler; + +import org.wildfly.security.http.HttpAuthenticationException; +import org.wildfly.security.http.HttpServerAuthenticationMechanism; +import org.wildfly.security.http.HttpServerAuthenticationMechanismFactory; + +/** + * A {@link HttpServerAuthenticationMechanismFactory} that wraps a delegate so that any returned + * {@link HttpServerAuthenticationMechanism} is wrapped by a wrapper that ensures all calls are using the provided + * {@link AccessControlContext}, if no AccessControlContext is provided then the one in place at the time this factory is + * instantiated is used instead. + * + * @author Darran Lofthouse + */ +public final class PrivilegedServerMechanismFactory implements HttpServerAuthenticationMechanismFactory { + + private final HttpServerAuthenticationMechanismFactory delegate; + private final AccessControlContext accessControlContext; + + /** + * Construct a new instance of {@code PrivilegedServerMechanismFactory}. + * + * @param delegate the {@link HttpServerAuthenticationMechanismFactory} to delegate to. + * @param accessControlContext the {@link AccessControlContext} to use for calls to the wrapped factory and subsequently any + * mechanism created by that factory. + */ + public PrivilegedServerMechanismFactory(final HttpServerAuthenticationMechanismFactory delegate, + final AccessControlContext accessControlContext) { + this.delegate = checkNotNullParam("delegate", delegate); + this.accessControlContext = checkNotNullParam("accessControlContext", accessControlContext); + } + + /** + * Construct a new instance of {@code PrivilegedServerMechanismFactory} using the current {@link AccessControlContext} for + * calls to the wrapped factory + * + * @param delegate the {@link HttpServerAuthenticationMechanismFactory} to delegate to. + */ + public PrivilegedServerMechanismFactory(final HttpServerAuthenticationMechanismFactory delegate) { + this(delegate, AccessController.getContext()); + } + + /** + * @see org.wildfly.security.http.HttpServerAuthenticationMechanismFactory#getMechanismNames(Map) + */ + @Override + public String[] getMechanismNames(Map properties) { + return delegate.getMechanismNames(properties); + } + + /** + * @see org.wildfly.security.http.HttpServerAuthenticationMechanismFactory#createAuthenticationMechanism(String, Map, CallbackHandler) + */ + @Override + public HttpServerAuthenticationMechanism createAuthenticationMechanism(String mechanismName, Map properties, + CallbackHandler callbackHandler) throws HttpAuthenticationException { + HttpServerAuthenticationMechanism serverMechanism = delegate.createAuthenticationMechanism(mechanismName, properties, + callbackHandler); + return serverMechanism != null ? new PrivilegedServerMechanism(serverMechanism, accessControlContext) : null; + } + +} \ No newline at end of file Index: 3rdParty_sources/elytron/org/wildfly/security/http/util/PropertiesServerMechanismFactory.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/http/util/PropertiesServerMechanismFactory.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/http/util/PropertiesServerMechanismFactory.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,87 @@ +/* + * 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.http.util; + +import static org.wildfly.common.Assert.checkNotNullParam; + +import java.util.HashMap; +import java.util.Map; + +import javax.security.auth.callback.CallbackHandler; + +import org.wildfly.security.http.HttpAuthenticationException; +import org.wildfly.security.http.HttpServerAuthenticationMechanism; +import org.wildfly.security.http.HttpServerAuthenticationMechanismFactory; + +/** + * A {@link HttpServerAuthenticationMechanismFactory} that adds a predefined set of properties to all calls to the delegate. + * + * @author Darran Lofthouse + */ +public final class PropertiesServerMechanismFactory implements HttpServerAuthenticationMechanismFactory { + + private final HttpServerAuthenticationMechanismFactory delegate; + private final Map properties; + + /** + * Construct a new instance. + * + * @param delegate the {@link HttpServerAuthenticationMechanismFactory} calls are delegated to. + * @param properties the properties that should be added to any properties passed in overriding any duplicate keys. + */ + public PropertiesServerMechanismFactory(final HttpServerAuthenticationMechanismFactory delegate, final Map properties) { + this.delegate = checkNotNullParam("delegate", delegate); + this.properties = new HashMap<>(checkNotNullParam("properties", properties)); + } + + /** + * Obtain the list of available mechanism names after merging the properties. + * + * @param properties the initial set of properties to pass to the delegate to obtain the mechanism names. + * @return the list of authentication mechanisms available form this factory. + * @see org.wildfly.security.http.HttpServerAuthenticationMechanismFactory#getMechanismNames(Map) + */ + @Override + public String[] getMechanismNames(Map properties) { + return delegate.getMechanismNames(combine(properties, this.properties)); + } + + /** + * Create an instance of the requested {@link HttpServerAuthenticationMechanismFactory}. + * + * @param mechanismName the name of the mechanism being requested. + * @param properties initial properties to be passed into the delegate factory. + * @param callbackHandler the {@link CallbackHandler} to use for verification. + * @return The newly created {@link HttpServerAuthenticationMechanismFactory}, or {@code null} if not availbale. + * @throws HttpAuthenticationException + * @see org.wildfly.security.http.HttpServerAuthenticationMechanismFactory#createAuthenticationMechanism(String, + * Map, CallbackHandler) + */ + @Override + public HttpServerAuthenticationMechanism createAuthenticationMechanism(String mechanismName, Map properties, CallbackHandler callbackHandler) throws HttpAuthenticationException { + return delegate.createAuthenticationMechanism(mechanismName, combine(properties, this.properties), callbackHandler); + } + + private static Map combine(Map provided, Map configured) { + Map combined = new HashMap<>(provided); + combined.putAll( configured); + + return combined; + } + +} \ No newline at end of file Index: 3rdParty_sources/elytron/org/wildfly/security/http/util/SecurityProviderServerMechanismFactory.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/http/util/SecurityProviderServerMechanismFactory.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/http/util/SecurityProviderServerMechanismFactory.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,180 @@ +/* + * 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.http.util; + +import static org.wildfly.common.Assert.checkNotNullParam; +import static org.wildfly.security.http.util.ElytronMessages.log; +import static org.wildfly.security.provider.util.ProviderUtil.INSTALLED_PROVIDERS; + +import java.security.NoSuchAlgorithmException; +import java.security.Provider; +import java.security.Provider.Service; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.function.Supplier; + +import javax.security.auth.callback.CallbackHandler; + +import org.wildfly.security.http.HttpAuthenticationException; +import org.wildfly.security.http.HttpServerAuthenticationMechanism; +import org.wildfly.security.http.HttpServerAuthenticationMechanismFactory; + +/** + * A {@link HttpServerAuthenticationMechanismFactory} that loads factories from a supplied array of {@link Provider} instances. + * The provider service instances may or may not be cached. + * + * @author Darran Lofthouse + */ +public final class SecurityProviderServerMechanismFactory implements HttpServerAuthenticationMechanismFactory { + + private static final String SERVICE_TYPE = HttpServerAuthenticationMechanismFactory.class.getSimpleName(); + + private final Supplier providerSupplier; + private volatile Provider[] providers; + private volatile Map> services; + + /** + * Construct a new instance which uses the globally registered {@link Provider} instances. + */ + public SecurityProviderServerMechanismFactory() { + this(INSTALLED_PROVIDERS); + } + + /** + * Construct a new instance of {@code SecurityProviderServerMechanismFactory}. + * + * @param providerSupplier a supplier of providers to use for locating the factories + */ + public SecurityProviderServerMechanismFactory(Supplier providerSupplier) { + this.providerSupplier = checkNotNullParam("providerSupplier", providerSupplier); + } + + /** + * Construct a new instance of {@code SecurityProviderServerMechanismFactory}. + * + * @param providers the provider instances this factory should use. + */ + public SecurityProviderServerMechanismFactory(Provider[] providers) { + this.providerSupplier = null; + this.providers = checkNotNullParam("providers", providers); + } + + /** + * Construct a new instance of {@code SecurityProviderServerMechanismFactory}. + * + * @param provider the provider instance this factory should use. + */ + public SecurityProviderServerMechanismFactory(Provider provider) { + this(new Provider[] { checkNotNullParam("provider", provider) }); + } + + /** + * @see org.wildfly.security.http.HttpServerAuthenticationMechanismFactory#getMechanismNames(Map) + */ + @Override + public String[] getMechanismNames(Map properties) { + Map> services = getServices(); + final Set names; + if (properties.isEmpty()) { + /* + * If no properties are provided that could filter the names return them all. + */ + names = services.keySet(); + } else { + names = new LinkedHashSet<>(); + for (List currentServices : services.values()) { + for (Service currentService : currentServices) { + try { + String[] serviceMechNames = ((HttpServerAuthenticationMechanismFactory) currentService.newInstance(null)).getMechanismNames(properties); + Collections.addAll(names, serviceMechNames); + } catch (NoSuchAlgorithmException e) { + log.debug("Unable to create instance", e); + } + } + } + } + if (names.size() == 0 && log.isTraceEnabled()) { + log.tracef("No %s provided by provider loader in %s: %s", SERVICE_TYPE, getClass().getSimpleName(), + Arrays.toString(providerSupplier.get())); + } + return names.toArray(new String[names.size()]); + } + + /** + * @see org.wildfly.security.http.HttpServerAuthenticationMechanismFactory#createAuthenticationMechanism(String, Map, CallbackHandler) + */ + @Override + public HttpServerAuthenticationMechanism createAuthenticationMechanism(String mechanismName, Map properties, CallbackHandler callbackHandler) throws HttpAuthenticationException { + List services = getServices().get(mechanismName); + if (services != null) { + for (Service currentService : services) { + try { + HttpServerAuthenticationMechanismFactory factory = (HttpServerAuthenticationMechanismFactory) currentService.newInstance(null); + HttpServerAuthenticationMechanism mechanism = factory.createAuthenticationMechanism(mechanismName, properties, callbackHandler); + if (mechanism != null) { + return mechanism; + } + } catch (NoSuchAlgorithmException e) { + log.debug("Unable to create instance", e); + } + } + } + if (log.isTraceEnabled()) { + log.tracef("No %s provided by provider loader in %s", SERVICE_TYPE, getClass().getSimpleName()); + } + return null; + } + + private Map> getServices() { + if (services == null) { + synchronized(this) { + if (services == null) { + if (providers == null) { + providers = providerSupplier.get(); + } + Map> services = new HashMap<>(); + for (Provider provider : providers) { + Set providerServices = provider.getServices(); + for (Service currentService : providerServices) { + if (SERVICE_TYPE.equals(currentService.getType())) { + String algorithm = currentService.getAlgorithm(); + if (services.containsKey(algorithm)) { + services.get(algorithm).add(currentService); + } else { + List serviceList = new ArrayList<>(); + serviceList.add(currentService); + services.put(algorithm, serviceList); + } + } + } + } + this.services = services; + } + } + } + + return services; + } + +} \ No newline at end of file Index: 3rdParty_sources/elytron/org/wildfly/security/http/util/ServiceLoaderServerMechanismFactory.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/http/util/ServiceLoaderServerMechanismFactory.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/http/util/ServiceLoaderServerMechanismFactory.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,115 @@ +/* + * 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.http.util; + +import static org.wildfly.common.Assert.checkNotNullParam; +import static org.wildfly.security.http.util.ElytronMessages.log; + +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.ServiceConfigurationError; +import java.util.ServiceLoader; +import java.util.Set; + +import javax.security.auth.callback.CallbackHandler; + +import org.wildfly.security.http.HttpAuthenticationException; +import org.wildfly.security.http.HttpServerAuthenticationMechanism; +import org.wildfly.security.http.HttpServerAuthenticationMechanismFactory; + +/** + * A {@link HttpServerAuthenticationMechanismFactory} which locates further factory implementations by iterating a {@link ServiceLoader} + * + * @author Darran Lofthouse + */ +public final class ServiceLoaderServerMechanismFactory implements HttpServerAuthenticationMechanismFactory { + + private final ServiceLoader serviceLoader; + + /** + * Constructs a new instance with a previously created {@link ServiceLoader} + * + * This class synchronizes on the supplied service loader, if the same is synchronized against outside then {@link ServiceLoader#reload()} can safely be called. + * + * @param serviceLoader the {@link ServiceLoader} to use to locate {@link HttpServerAuthenticationMechanismFactory} instances. + */ + public ServiceLoaderServerMechanismFactory(ServiceLoader serviceLoader) { + this.serviceLoader = checkNotNullParam("serviceLoader", serviceLoader); + } + + /** + * Constructs a new instance, a {@link ServiceLoader} will be created from the supplied {@link ClassLoader} + * + * @param classLoader the {@link ClassLoader} to use to construct a {@link ServiceLoader}. + */ + public ServiceLoaderServerMechanismFactory(ClassLoader classLoader) { + this(ServiceLoader.load(HttpServerAuthenticationMechanismFactory.class, checkNotNullParam("classLoader", classLoader))); + } + + /** + * @see org.wildfly.security.http.HttpServerAuthenticationMechanismFactory#getMechanismNames(Map) + */ + @Override + public String[] getMechanismNames(Map properties) { + Set names = new LinkedHashSet<>(); + synchronized(serviceLoader) { + Iterator factoryIterator = serviceLoader.iterator(); + try { + while (factoryIterator.hasNext()) { + HttpServerAuthenticationMechanismFactory current = factoryIterator.next(); + Collections.addAll(names, current.getMechanismNames(properties)); + } + } catch (ServiceConfigurationError e) { + log.debug("Unable to read service configuration", e); + } + } + if (log.isTraceEnabled()) { + log.tracef("No %s provided by service loader in %s: %s", HttpServerAuthenticationMechanismFactory.class.getSimpleName(), getClass().getSimpleName(), serviceLoader.toString()); + } + return names.toArray(new String[names.size()]); + } + + /** + * @see org.wildfly.security.http.HttpServerAuthenticationMechanismFactory#createAuthenticationMechanism(String, Map, CallbackHandler) + */ + @Override + public HttpServerAuthenticationMechanism createAuthenticationMechanism(String mechanismName, Map properties, + CallbackHandler callbackHandler) throws HttpAuthenticationException { + synchronized(serviceLoader) { + Iterator factoryIterator = serviceLoader.iterator(); + try { + while (factoryIterator.hasNext()) { + HttpServerAuthenticationMechanismFactory current = factoryIterator.next(); + HttpServerAuthenticationMechanism authenticationMechanism = current.createAuthenticationMechanism(mechanismName, properties, callbackHandler); + if (authenticationMechanism != null) { + return authenticationMechanism; + } + } + } catch (ServiceConfigurationError e) { + log.debug("Unable to read service configuration", e); + } + } + if (log.isTraceEnabled()) { + log.tracef("No %s provided by service loader in %s: %s", HttpServerAuthenticationMechanismFactory.class.getSimpleName(), getClass().getSimpleName(), serviceLoader.toString()); + } + return null; + } + +} \ No newline at end of file Index: 3rdParty_sources/elytron/org/wildfly/security/http/util/SetMechanismInformationMechanismFactory.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/http/util/SetMechanismInformationMechanismFactory.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/http/util/SetMechanismInformationMechanismFactory.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,132 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2016 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.http.util; + +import static org.wildfly.security.http.HttpConstants.HOST; +import static org.wildfly.security.http.util.ElytronMessages.log; + +import java.util.Map; + +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; + +import org.wildfly.security.auth.callback.MechanismInformationCallback; +import org.wildfly.security.auth.server.MechanismInformation; +import org.wildfly.security.http.HttpAuthenticationException; +import org.wildfly.security.http.HttpServerAuthenticationMechanism; +import org.wildfly.security.http.HttpServerAuthenticationMechanismFactory; +import org.wildfly.security.http.HttpServerRequest; + +/** + * A wrapper {@link HttpServerAuthenticationMechanismFactory} to ensure that mechanism information for the current + * authentication request is set before the first authentication callbacks. + * + * The host name and protocol are derived from the current request being authenticated. + * + * @author Darran Lofthouse + */ +public class SetMechanismInformationMechanismFactory implements HttpServerAuthenticationMechanismFactory { + + private HttpServerAuthenticationMechanismFactory delegate; + + /** + * Construct a wrapping mechanism factory instance. + * + * @param delegate the wrapped mechanism factory + */ + public SetMechanismInformationMechanismFactory(final HttpServerAuthenticationMechanismFactory delegate) { + this.delegate = delegate; + } + + @Override + public String[] getMechanismNames(Map properties) { + return delegate.getMechanismNames(properties); + } + + @Override + public HttpServerAuthenticationMechanism createAuthenticationMechanism(final String mechanismName, Map properties, + final CallbackHandler callbackHandler) throws HttpAuthenticationException { + final HttpServerAuthenticationMechanism mechanism = delegate.createAuthenticationMechanism(mechanismName, properties, callbackHandler); + return mechanism != null ? new HttpServerAuthenticationMechanism() { + + @Override + public String getMechanismName() { + return mechanism.getMechanismName(); + } + + @Override + public void evaluateRequest(HttpServerRequest request) throws HttpAuthenticationException { + String host = request.getFirstRequestHeaderValue(HOST); + String resolvedHostName = null; + if (host != null) { + if (host.startsWith("[")) { + int close = host.indexOf(']'); + if (close > 0) { + resolvedHostName = host.substring(0, close + 1); + } + } else { + int colon = host.lastIndexOf(':'); + resolvedHostName = colon > 0 ? host.substring(0, colon) : host; + } + } + + try { + final String mechanismName = getMechanismName(); + final String hostName = resolvedHostName; + final String protocol = request.getRequestURI().getScheme(); + callbackHandler.handle(new Callback[] { new MechanismInformationCallback(new MechanismInformation() { + + @Override + public String getProtocol() { + return protocol; + } + + @Override + public String getMechanismType() { + return "HTTP"; + } + + @Override + public String getMechanismName() { + return mechanismName; + } + + @Override + public String getHostName() { + return hostName; + } + })}); + + } catch (Throwable e) { + throw log.unableToLocateMechanismConfiguration(e).toHttpAuthenticationException(); + } + + mechanism.evaluateRequest(request); + } + + @Override + public void dispose() { + mechanism.dispose(); + } + + } : null; + } + + + +} Index: 3rdParty_sources/elytron/org/wildfly/security/http/util/SetRequestInformationCallbackMechanismFactory.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/http/util/SetRequestInformationCallbackMechanismFactory.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/http/util/SetRequestInformationCallbackMechanismFactory.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,94 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2023 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.http.util; + +import org.wildfly.security.auth.callback.RequestInformationCallback; +import org.wildfly.security.http.HttpAuthenticationException; +import org.wildfly.security.http.HttpServerAuthenticationMechanism; +import org.wildfly.security.http.HttpServerAuthenticationMechanismFactory; +import org.wildfly.security.http.HttpServerRequest; + +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.UnsupportedCallbackException; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Function; + +import static org.wildfly.common.Assert.checkNotNullParam; + +/** + * A wrapper {@link HttpServerAuthenticationMechanismFactory} that sets the request information using the current authentication request. + * + * @author Diana Krepinska + */ +public class SetRequestInformationCallbackMechanismFactory implements HttpServerAuthenticationMechanismFactory { + + private final HttpServerAuthenticationMechanismFactory delegate; + private final HashMap> httpServerRequestInformationMap; + + /** + * Construct a wrapping mechanism factory instance. + * + * @param delegate the wrapped mechanism factory + */ + public SetRequestInformationCallbackMechanismFactory(final HttpServerAuthenticationMechanismFactory delegate, HashMap> httpServerRequestInformationMap) { + this.delegate = checkNotNullParam("delegate", delegate); + this.httpServerRequestInformationMap = checkNotNullParam("httpServerRequestInformationMap", httpServerRequestInformationMap); + } + + @Override + public String[] getMechanismNames(Map properties) { + return delegate.getMechanismNames(properties); + } + + @Override + public HttpServerAuthenticationMechanism createAuthenticationMechanism(final String mechanismName, Map properties, + final CallbackHandler callbackHandler) throws HttpAuthenticationException { + final HttpServerAuthenticationMechanism mechanism = delegate.createAuthenticationMechanism(mechanismName, properties, callbackHandler); + return mechanism != null ? new HttpServerAuthenticationMechanism() { + + @Override + public String getMechanismName() { + return mechanism.getMechanismName(); + } + + @Override + public void evaluateRequest(HttpServerRequest request) throws HttpAuthenticationException { + try { + HashMap props = new HashMap<>(); + for (Map.Entry> entry : httpServerRequestInformationMap.entrySet()) { + props.put(entry.getKey(), entry.getValue().apply(request)); + } + callbackHandler.handle(new Callback[]{new RequestInformationCallback(props)}); + } catch (IOException | UnsupportedCallbackException e) { + throw new HttpAuthenticationException(e); + } + + mechanism.evaluateRequest(request); + } + + @Override + public void dispose() { + mechanism.dispose(); + } + + } : null; + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/http/util/SimpleHttpServerCookie.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/http/util/SimpleHttpServerCookie.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/http/util/SimpleHttpServerCookie.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,92 @@ +/* + * Copyright 2021 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.http.util; + +import org.wildfly.security.http.HttpServerCookie; + +/** + * A simple implementation of {@link HttpServerCookie}. + * + * @author Darran Lofthouse + */ +public class SimpleHttpServerCookie implements HttpServerCookie { + + private final String name; + private final String value; + private final String domain; + private final int maxAge; + private final String path; + private final boolean secure; + private final int version; + private final boolean httpOnly; + + SimpleHttpServerCookie(String name, String value, String domain, int maxAge, String path, boolean secure, int version, boolean httpOnly) { + super(); + this.name = name; + this.value = value; + this.domain = domain; + this.maxAge = maxAge; + this.path = path; + this.secure = secure; + this.version = version; + this.httpOnly = httpOnly; + } + + @Override + public String getName() { + return name; + } + + @Override + public String getValue() { + return value; + } + + @Override + public String getDomain() { + return domain; + } + + @Override + public int getMaxAge() { + return maxAge; + } + + @Override + public String getPath() { + return path; + } + + @Override + public boolean isSecure() { + return secure; + } + + @Override + public int getVersion() { + return version; + } + + @Override + public boolean isHttpOnly() { + return httpOnly; + } + + public static HttpServerCookie newInstance(String name, String value, String domain, int maxAge, String path, boolean secure, int version, boolean httpOnly) { + return new SimpleHttpServerCookie(name, value, domain, maxAge, path, secure, version, httpOnly); + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/http/util/SocketAddressCallbackServerMechanismFactory.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/http/util/SocketAddressCallbackServerMechanismFactory.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/http/util/SocketAddressCallbackServerMechanismFactory.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,98 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2020 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.http.util; + +import static org.wildfly.common.Assert.checkNotNullParam; + +import java.io.IOException; +import java.net.InetSocketAddress; +import java.util.Map; + +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.UnsupportedCallbackException; + +import org.wildfly.security.auth.callback.SocketAddressCallback; +import org.wildfly.security.http.HttpAuthenticationException; +import org.wildfly.security.http.HttpServerAuthenticationMechanism; +import org.wildfly.security.http.HttpServerAuthenticationMechanismFactory; +import org.wildfly.security.http.HttpServerRequest; + +/** + * A wrapper {@link HttpServerAuthenticationMechanismFactory} that sets the peer address using the current + * authentication request. + * + * @author Farah Juma + */ +public final class SocketAddressCallbackServerMechanismFactory implements HttpServerAuthenticationMechanismFactory { + + private final HttpServerAuthenticationMechanismFactory delegate; + + /** + * Construct a wrapping mechanism factory instance. + * + * @param delegate the wrapped mechanism factory + */ + public SocketAddressCallbackServerMechanismFactory(final HttpServerAuthenticationMechanismFactory delegate) { + this.delegate = checkNotNullParam("delegate", delegate); + } + + @Override + public String[] getMechanismNames(Map properties) { + return delegate.getMechanismNames(properties); + } + + @Override + public HttpServerAuthenticationMechanism createAuthenticationMechanism(String mechanismName, Map properties, CallbackHandler callbackHandler) throws HttpAuthenticationException { + final HttpServerAuthenticationMechanism mechanism = delegate.createAuthenticationMechanism(mechanismName, properties, callbackHandler); + return mechanism != null ? new HttpServerAuthenticationMechanism() { + + @Override + public String getMechanismName() { + return mechanism.getMechanismName(); + } + + @Override + public void evaluateRequest(HttpServerRequest request) throws HttpAuthenticationException { + final InetSocketAddress peerAddress = request.getSourceAddress(); + if (peerAddress != null) { + final SocketAddressCallback peerCallback = new SocketAddressCallback(peerAddress, SocketAddressCallback.Kind.PEER); + try { + callbackHandler.handle(new Callback[] { peerCallback }); + } catch (IOException | UnsupportedCallbackException e) { + throw new HttpAuthenticationException(e); + } + } + + mechanism.evaluateRequest(request); + } + + @Override + public Object getNegotiatedProperty(String propertyName) { + return mechanism.getNegotiatedProperty(propertyName); + } + + @Override + public void dispose() { + mechanism.dispose(); + } + + } : null; + } +} + Index: 3rdParty_sources/elytron/org/wildfly/security/http/util/SortedServerMechanismFactory.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/http/util/SortedServerMechanismFactory.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/http/util/SortedServerMechanismFactory.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,61 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2017 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.http.util; + +import static org.wildfly.common.Assert.checkNotNullParam; + +import java.util.Arrays; +import java.util.Comparator; +import java.util.Map; + +import javax.security.auth.callback.CallbackHandler; + +import org.wildfly.security.http.HttpAuthenticationException; +import org.wildfly.security.http.HttpServerAuthenticationMechanism; +import org.wildfly.security.http.HttpServerAuthenticationMechanismFactory; + +/** + * A {@link HttpServerAuthenticationMechanismFactory} which sorts the mechanism names returned using the provided + * {@link Comparator}. + * + * @author Darran Lofthouse + */ +public final class SortedServerMechanismFactory implements HttpServerAuthenticationMechanismFactory { + + private final HttpServerAuthenticationMechanismFactory delegate; + private final Comparator mechanismNameComparator; + + public SortedServerMechanismFactory(final HttpServerAuthenticationMechanismFactory delegate, final Comparator mechanismNameComparator) { + this.delegate = checkNotNullParam("delegate", delegate); + this.mechanismNameComparator = checkNotNullParam("mechanismNameComparator", mechanismNameComparator); + } + + @Override + public String[] getMechanismNames(Map properties) { + String[] mechanismNames = delegate.getMechanismNames(properties); + Arrays.sort(mechanismNames, mechanismNameComparator); + return mechanismNames; + } + + @Override + public HttpServerAuthenticationMechanism createAuthenticationMechanism(String mechanismName, Map properties, + CallbackHandler callbackHandler) throws HttpAuthenticationException { + return delegate.createAuthenticationMechanism(mechanismName, properties, callbackHandler); + } + +} Index: 3rdParty_sources/elytron/org/wildfly/security/http/util/package-info.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/http/util/package-info.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/http/util/package-info.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,24 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2016 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 containing utility classes for HTTP authentication, predominantly special factory wrappers. + * + * @author Darran Lofthouse + */ +package org.wildfly.security.http.util; \ No newline at end of file Index: 3rdParty_sources/elytron/org/wildfly/security/mechanism/AuthenticationMechanismException.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/mechanism/AuthenticationMechanismException.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/mechanism/AuthenticationMechanismException.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,113 @@ +/* + * 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; + +import java.io.IOException; + +import javax.security.sasl.SaslException; + +import org.wildfly.common.Assert; +import org.wildfly.security.http.HttpAuthenticationException; + +/** + * A network authentication mechanism exception. + * + * @author David M. Lloyd + */ +public class AuthenticationMechanismException extends IOException { + private static final long serialVersionUID = -436234128057297342L; + + /** + * Constructs a new {@code AuthenticationMechanismException} instance. The message is left blank ({@code null}), + * and no cause is specified. + */ + public AuthenticationMechanismException() { + } + + /** + * Constructs a new {@code AuthenticationMechanismException} instance with an initial message. No cause is + * specified. + * + * @param msg the message + */ + public AuthenticationMechanismException(final String msg) { + super(msg); + } + + /** + * Constructs a new {@code AuthenticationMechanismException} instance with an initial cause. If a non-{@code null} + * cause is specified, its message is used to initialize the message of this {@code + * AuthenticationMechanismException}; otherwise the message is left blank ({@code null}). + * + * @param cause the cause + */ + public AuthenticationMechanismException(final Throwable cause) { + super(cause); + } + + /** + * Constructs a new {@code AuthenticationMechanismException} instance with an initial message and cause. + * + * @param msg the message + * @param cause the cause + */ + public AuthenticationMechanismException(final String msg, final Throwable cause) { + super(msg, cause); + } + + /** + * Convert this exception to a SASL exception with the same message and stack trace. + * + * @return the SASL exception + */ + public SaslException toSaslException() { + return copyContents(this, new SaslException(getMessage(), getCause())); + } + + /** + * Convert this exception to an HTTP exception with the same message and stack trace. + * + * @return the HTTP exception + */ + public HttpAuthenticationException toHttpAuthenticationException() { + return copyContents(this, new HttpAuthenticationException(getMessage(), getCause())); + } + + /** + * Convert the given exception to an {@code AuthenticationMechanismException}. If the given exception is + * already a {@code AuthenticationMechanismException}, it is returned as-is. + * + * @param source the source exception (must not be {@code null}) + * @return the new exception instance (not {@code null}) + */ + public static AuthenticationMechanismException fromException(final Exception source) { + Assert.checkNotNullParam("source", source); + if (source instanceof AuthenticationMechanismException) return (AuthenticationMechanismException) source; + return copyContents(source, new AuthenticationMechanismException(source.getMessage(), source.getCause())); + } + + private static T copyContents(final Exception source, final T throwable) { + throwable.setStackTrace(source.getStackTrace()); + final Throwable[] suppressed = source.getSuppressed(); + if (suppressed != null) for (final Throwable t : suppressed) { + throwable.addSuppressed(t); + } + return throwable; + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/mechanism/MechanismUtil.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/mechanism/MechanismUtil.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/mechanism/MechanismUtil.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,115 @@ +/* + * 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; + +import static org.wildfly.security.mechanism._private.ElytronMessages.log; + +import java.security.Provider; +import java.security.spec.AlgorithmParameterSpec; +import java.util.function.Supplier; + +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.UnsupportedCallbackException; + +import org.wildfly.security.mechanism._private.ElytronMessages; +import org.wildfly.security.password.Password; +import org.wildfly.security.password.PasswordFactory; + +/** + * Utils to be used by authentication mechanism (SASL or HTTP) implementations. + * + * @author David M. Lloyd + * + * @deprecated Should not be part of public API. Moved into internal {@link org.wildfly.security.mechanism._private.MechanismUtil}. + */ +@Deprecated +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 + * @return the password + */ + @Deprecated + public static S getPasswordCredential(String userName, CallbackHandler callbackHandler, Class passwordType, String passwordAlgorithm, AlgorithmParameterSpec matchParameters, AlgorithmParameterSpec generateParameters, Supplier providers) throws AuthenticationMechanismException { + return getPasswordCredential(userName, callbackHandler, passwordType, passwordAlgorithm, matchParameters, generateParameters, providers, ElytronMessages.log); + } + + /** + * 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 + */ + @Deprecated + public static S getPasswordCredential(String userName, CallbackHandler callbackHandler, Class passwordType, String passwordAlgorithm, AlgorithmParameterSpec matchParameters, AlgorithmParameterSpec generateParameters, Supplier providers, ElytronMessages log) throws AuthenticationMechanismException { + return org.wildfly.security.mechanism._private.MechanismUtil.getPasswordCredential(userName, callbackHandler, passwordType, passwordAlgorithm, matchParameters, generateParameters, providers, log); + } + + /** + * 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 + */ + @Deprecated + public static void handleCallbacks(ElytronMessages log, CallbackHandler callbackHandler, Callback... callbacks) throws AuthenticationMechanismException, UnsupportedCallbackException { + org.wildfly.security.mechanism._private.MechanismUtil.handleCallbacks(log, callbackHandler, callbacks); + } + + /** + * A varargs wrapper method for callback handler invocation. + * + * @param mechName the mechanism name to report 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 + * @deprecated Use {@link #handleCallbacks(ElytronMessages, CallbackHandler, Callback...)} instead + */ + @Deprecated + public static void handleCallbacks(String mechName, CallbackHandler callbackHandler, Callback... callbacks) throws AuthenticationMechanismException, UnsupportedCallbackException { + handleCallbacks(log, callbackHandler, callbacks); + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/mechanism/ScramServerErrorCode.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/mechanism/ScramServerErrorCode.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/mechanism/ScramServerErrorCode.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,144 @@ +/* + * 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; + +import java.nio.charset.StandardCharsets; +import java.util.EnumSet; +import java.util.Locale; + +/** + * This enum lists server error codes for SCRAM authentication mechanism. + * @author David M. Lloyd + */ +public enum ScramServerErrorCode { + INVALID_ENCODING, + EXTENSIONS_NOT_SUPPORTED, + INVALID_PROOF, + CHANNEL_BINDINGS_DONT_MATCH, + SERVER_DOES_NOT_SUPPORT_CHANNEL_BINDING, + SERVER_DOES_SUPPORT_CHANNEL_BINDING, + CHANNEL_BINDING_NOT_SUPPORTED, + CHANNEL_BINDING_NOT_PROVIDED, + UNSUPPORTED_CHANNEL_BINDING_TYPE, + UNKNOWN_USER, + INVALID_USERNAME_ENCODING, + NO_RESOURCES, + OTHER_ERROR, + ; + + private final String text; + private final byte[] messageBytes; + + ScramServerErrorCode() { + text = name().replace('_', '-').toLowerCase(Locale.US); + final int length = text.length(); + byte[] msg = new byte[length + 2]; + msg[0] = 'e'; msg[1] = '='; + System.arraycopy(text.getBytes(StandardCharsets.UTF_8), 0, msg, 2, length); + messageBytes = msg; + } + + public String getText() { + return text; + } + + public byte[] getMessageBytes() { + return messageBytes.clone(); + } + + byte[] getRawMessageBytes() { + return messageBytes; + } + + /** + * Convert the error string to the respective {@code ScramServerErrorCode} enum value, or to the {@code OTHER_ERROR} value if can't match. + * @param value the error value as string + * @return the respective {@code ScramServerErrorCode} enum value from error string, {@code OTHER_ERROR} otherwise + */ + public static ScramServerErrorCode fromErrorString(String value) { + try { + return valueOf(value.replace('-', '_').toUpperCase(Locale.US)); + } catch (IllegalArgumentException ignored) { + return OTHER_ERROR; + } + } + + private static final int fullSize = values().length; + + /** + * Determine whether the given set is fully populated (or "full"), meaning it contains all possible values. + * + * @param set the set + * + * @return {@code true} if the set is full, {@code false} otherwise + */ + public static boolean isFull(final EnumSet set) { + return set != null && set.size() == fullSize; + } + + /** + * Determine whether this instance is equal to one of the given instances. + * + * @param v1 the first instance + * + * @return {@code true} if one of the instances matches this one, {@code false} otherwise + */ + public boolean in(final ScramServerErrorCode v1) { + return this == v1; + } + + /** + * Determine whether this instance is equal to one of the given instances. + * + * @param v1 the first instance + * @param v2 the second instance + * + * @return {@code true} if one of the instances matches this one, {@code false} otherwise + */ + public boolean in(final ScramServerErrorCode v1, final ScramServerErrorCode v2) { + return this == v1 || this == v2; + } + + /** + * Determine whether this instance is equal to one of the given instances. + * + * @param v1 the first instance + * @param v2 the second instance + * @param v3 the third instance + * + * @return {@code true} if one of the instances matches this one, {@code false} otherwise + */ + public boolean in(final ScramServerErrorCode v1, final ScramServerErrorCode v2, final ScramServerErrorCode v3) { + return this == v1 || this == v2 || this == v3; + } + + /** + * Determine whether this instance is equal to one of the given instances. + * + * @param values the possible values + * + * @return {@code true} if one of the instances matches this one, {@code false} otherwise + */ + public boolean in(final ScramServerErrorCode... values) { + if (values != null) for (ScramServerErrorCode value : values) { + if (this == value) return true; + } + return false; + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/mechanism/ScramServerException.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/mechanism/ScramServerException.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/mechanism/ScramServerException.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,89 @@ +/* + * 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; + +import org.wildfly.common.Assert; + +/** + * A SCRAM server-side exception with an error code. + * + * @author David M. Lloyd + */ +public class ScramServerException extends AuthenticationMechanismException { + private static final long serialVersionUID = 5410786267588390307L; + + private final ScramServerErrorCode error; + + /** + * Constructs a new {@code ScramServerException} instance. The message is left blank ({@code null}), and no cause + * is specified. + * + * @param error the server error code + */ + public ScramServerException(final ScramServerErrorCode error) { + Assert.checkNotNullParam("error", error); + this.error = error; + } + + /** + * Constructs a new {@code ScramServerException} instance with an initial message. No cause is specified. + * + * @param msg the message + * @param error the server error code + */ + public ScramServerException(final String msg, final ScramServerErrorCode error) { + super(msg); + Assert.checkNotNullParam("error", error); + this.error = error; + } + + /** + * Constructs a new {@code ScramServerException} instance with an initial cause. If a non-{@code null} cause is + * specified, its message is used to initialize the message of this {@code ScramServerException}; otherwise the + * message is left blank ({@code null}). + * + * @param cause the cause + * @param error the server error code + */ + public ScramServerException(final Throwable cause, final ScramServerErrorCode error) { + super(cause); + Assert.checkNotNullParam("error", error); + this.error = error; + } + + /** + * Constructs a new {@code ScramServerException} instance with an initial message and cause. + * @param msg the message + * @param cause the cause + * @param error the server error code + */ + public ScramServerException(final String msg, final Throwable cause, final ScramServerErrorCode error) { + super(msg, cause); + Assert.checkNotNullParam("error", error); + this.error = error; + } + + public String getMessage() { + return super.getMessage() + ": " + error.getText(); + } + + public ScramServerErrorCode getError() { + return error; + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/mechanism/_private/ElytronMessages.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/mechanism/_private/ElytronMessages.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/mechanism/_private/ElytronMessages.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,540 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2014 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 static org.jboss.logging.Logger.Level.WARN; + +import org.jboss.logging.BasicLogger; +import org.jboss.logging.Logger; +import org.jboss.logging.annotations.Cause; +import org.jboss.logging.annotations.LogMessage; +import org.jboss.logging.annotations.Message; +import org.jboss.logging.annotations.MessageLogger; +import org.jboss.logging.annotations.Param; +import org.jboss.logging.annotations.ValidIdRange; +import org.jboss.logging.annotations.ValidIdRanges; +import org.wildfly.security.asn1.ASN1Exception; +import org.wildfly.security.http.HttpAuthenticationException; +import org.wildfly.security.mechanism.AuthenticationMechanismException; +import org.wildfly.security.mechanism.ScramServerErrorCode; +import org.wildfly.security.mechanism.ScramServerException; + +/** + * Log messages and exceptions for Elytron. + * + * @author David M. Lloyd + * @author Darran Lofthouse + */ +@MessageLogger(projectCode = "ELY", length = 5) +@ValidIdRanges({ + @ValidIdRange(min = 7, max = 7), + @ValidIdRange(min = 1151, max = 1151), + @ValidIdRange(min = 5001, max = 5177), + @ValidIdRange(min = 6001, max = 6022), + @ValidIdRange(min = 7001, max = 7004) +}) +public interface ElytronMessages extends BasicLogger { + + ElytronMessages log = Logger.getMessageLogger(ElytronMessages.class, "org.wildfly.security"); + ElytronMessages sasl = Logger.getMessageLogger(ElytronMessages.class, "org.wildfly.security.sasl"); + ElytronMessages saslAnonymous = Logger.getMessageLogger(ElytronMessages.class, "org.wildfly.security.sasl.anonymous"); + ElytronMessages saslDigest = Logger.getMessageLogger(ElytronMessages.class, "org.wildfly.security.sasl.digest"); + ElytronMessages saslEntity = Logger.getMessageLogger(ElytronMessages.class, "org.wildfly.security.sasl.entity"); + ElytronMessages saslExternal = Logger.getMessageLogger(ElytronMessages.class, "org.wildfly.security.sasl.external"); + ElytronMessages saslGs2 = Logger.getMessageLogger(ElytronMessages.class, "org.wildfly.security.sasl.gs2"); + ElytronMessages saslGssapi = Logger.getMessageLogger(ElytronMessages.class, "org.wildfly.security.sasl.gssapi"); + ElytronMessages saslLocal = Logger.getMessageLogger(ElytronMessages.class, "org.wildfly.security.sasl.local"); + ElytronMessages saslOAuth2 = Logger.getMessageLogger(ElytronMessages.class, "org.wildfly.security.sasl.oauth2"); + ElytronMessages saslOTP = Logger.getMessageLogger(ElytronMessages.class, "org.wildfly.security.sasl.otp"); + ElytronMessages saslPlain = Logger.getMessageLogger(ElytronMessages.class, "org.wildfly.security.sasl.plain"); + ElytronMessages saslScram = Logger.getMessageLogger(ElytronMessages.class, "org.wildfly.security.sasl.scram"); + ElytronMessages httpSpnego = Logger.getMessageLogger(ElytronMessages.class, "org.wildfly.security.http.spnego"); + ElytronMessages httpClientCert = Logger.getMessageLogger(ElytronMessages.class, "org.wildfly.security.http.cert"); + ElytronMessages httpDigest = Logger.getMessageLogger(ElytronMessages.class, "org.wildfly.security.http.digest"); + ElytronMessages httpExternal = Logger.getMessageLogger(ElytronMessages.class, "org.wildfly.security.http.external"); + ElytronMessages httpUserPass = Logger.getMessageLogger(ElytronMessages.class, "org.wildfly.security.http.password"); + ElytronMessages httpForm = Logger.getMessageLogger(ElytronMessages.class, "org.wildfly.security.http.form"); + ElytronMessages httpBearer = Logger.getMessageLogger(ElytronMessages.class, "org.wildfly.security.http.bearer"); + ElytronMessages httpBasic = Logger.getMessageLogger(ElytronMessages.class, "org.wildfly.security.http.basic"); + + @LogMessage(level = WARN) + @Message(id = 7, value = "Credential destroying failed") + void credentialDestroyingFailed(@Cause Throwable cause); + + @Message(id = 1151, value = "Evidence Verification Failed.") + SecurityException authenticationFailedEvidenceVerification(); + + @Message(id = 5001, value = "Authentication mechanism exchange received a message after authentication was already complete") + AuthenticationMechanismException mechMessageAfterComplete(); + + @Message(id = 5002, value = "Authentication mechanism user name contains an invalid or disallowed character") + AuthenticationMechanismException mechUserNameContainsInvalidCharacter(); + + @Message(id = 5004, value = "Authentication mechanism authorization failed") + AuthenticationMechanismException mechAuthorizationFailed(@Cause Throwable cause); + + @Message(id = 5005, value = "Authentication mechanism authentication is not yet complete") + IllegalStateException mechAuthenticationNotComplete(); + + @Message(id = 5006, value = "Authentication mechanism does not support security layer (wrapping/unwrapping)") + IllegalStateException mechNoSecurityLayer(); + + @Message(id = 5007, value = "Invalid authentication mechanism negotiation message received") + AuthenticationMechanismException mechInvalidMessageReceived(); + + @Message(id = 5008, value = "No authentication mechanism login name was given") + AuthenticationMechanismException mechNoLoginNameGiven(); + + @Message(id = 5009, value = "No authentication mechanism password was given") + AuthenticationMechanismException mechNoPasswordGiven(); + + @Message(id = 5010, value = "Authentication mechanism authentication failed due to one or more malformed fields") + AuthenticationMechanismException mechMalformedFields(@Cause IllegalArgumentException ex); + + @Message(id = 5011, value = "Authentication mechanism message is too long") + AuthenticationMechanismException mechMessageTooLong(); + + @Message(id = 5012, value = "Authentication mechanism server-side authentication failed") + AuthenticationMechanismException mechServerSideAuthenticationFailed(@Cause Exception e); + + @Message(id = 5013, value = "Authentication mechanism password not verified") + AuthenticationMechanismException mechPasswordNotVerified(); + + @Message(id = 5014, value = "Authentication mechanism authorization failed: \"%s\" running as \"%s\"") + AuthenticationMechanismException mechAuthorizationFailed(String userName, String authorizationId); + + @Message(id = 5018, value = "Channel binding data changed") + AuthenticationMechanismException mechChannelBindingChanged(); + + @Message(id = 5019, value = "No token was given") + AuthenticationMechanismException mechNoTokenGiven(); + + @Message(id = 5022, value = "Initial challenge must be empty") + AuthenticationMechanismException mechInitialChallengeMustBeEmpty(); + + @Message(id = 5023, value = "Unable to set channel binding") + AuthenticationMechanismException mechUnableToSetChannelBinding(@Cause Exception e); + + @Message(id = 5024, value = "Failed to determine channel binding status") + AuthenticationMechanismException mechFailedToDetermineChannelBindingStatus(@Cause Exception e); + + @Message(id = 5025, value = "Mutual authentication not enabled") + AuthenticationMechanismException mechMutualAuthenticationNotEnabled(); + + @Message(id = 5026, value = "Unable to map SASL mechanism name to a GSS-API OID") + AuthenticationMechanismException mechMechanismToOidMappingFailed(@Cause Exception e); + + @Message(id = 5027, value = "Unable to dispose of GSSContext") + AuthenticationMechanismException mechUnableToDisposeGssContext(@Cause Exception e); + + @Message(id = 5028, value = "Unable to create name for acceptor") + AuthenticationMechanismException mechUnableToCreateNameForAcceptor(@Cause Exception e); + + @Message(id = 5029, value = "Unable to create GSSContext") + AuthenticationMechanismException mechUnableToCreateGssContext(@Cause Exception e); + + @Message(id = 5030, value = "Unable to set GSSContext request flags") + AuthenticationMechanismException mechUnableToSetGssContextRequestFlags(@Cause Exception e); + + @Message(id = 5031, value = "Unable to accept SASL client message") + AuthenticationMechanismException mechUnableToAcceptClientMessage(@Cause Exception e); + + @Message(id = 5032, value = "GSS-API mechanism mismatch between SASL client and server") + AuthenticationMechanismException mechGssApiMechanismMismatch(); + + @Message(id = 5033, value = "Channel binding not supported for this SASL mechanism") + AuthenticationMechanismException mechChannelBindingNotSupported(); + + @Message(id = 5034, value = "Channel binding type mismatch between SASL client and server") + AuthenticationMechanismException mechChannelBindingTypeMismatch(); + + @Message(id = 5035, value = "Channel binding not provided by client") + AuthenticationMechanismException mechChannelBindingNotProvided(); + + @Message(id = 5036, value = "Unable to determine peer name") + AuthenticationMechanismException mechUnableToDeterminePeerName(@Cause Exception e); + + @Message(id = 5037, value = "Authentication mechanism client refuses to initiate authentication") + AuthenticationMechanismException mechClientRefusesToInitiateAuthentication(); + + @Message(id = 5038, value = "Nonces do not match") + AuthenticationMechanismException mechNoncesDoNotMatch(); + + @Message(id = 5039, value = "Invalid length of nonce received") + AuthenticationMechanismException invalidNonceLength(); + + @Message(id = 5040, value = "Iteration count %d is below the minimum of %d") + AuthenticationMechanismException mechIterationCountIsTooLow(int iterationCount, int minimumIterationCount); + + @Message(id = 5041, value = "Iteration count %d is above the maximum of %d") + AuthenticationMechanismException mechIterationCountIsTooHigh(int iterationCount, int maximumIterationCount); + + @Message(id = 5043, value = "Invalid server message") + AuthenticationMechanismException mechInvalidServerMessage(); + + @Message(id = 5044, value = "Invalid server message") + AuthenticationMechanismException mechInvalidServerMessageWithCause(@Cause Throwable cause); + + @Message(id = 5045, value = "Invalid client message") + AuthenticationMechanismException mechInvalidClientMessage(); + + @Message(id = 5046, value = "Invalid client message") + AuthenticationMechanismException mechInvalidClientMessageWithCause(@Cause Throwable cause); + + @Message(id = 5047, value = "[%s] Authentication mechanism message is for mismatched mechanism \"%s\"") + AuthenticationMechanismException mechUnmatchedMechanism(String mechName, String otherMechName); + + @Message(id = 5049, value = "Server authenticity cannot be verified") + AuthenticationMechanismException mechServerAuthenticityCannotBeVerified(); + + @Message(id = 5050, value = "Callback handler does not support user name") + AuthenticationMechanismException mechCallbackHandlerDoesNotSupportUserName(@Cause Throwable cause); + + @Message(id = 5051, value = "Callback handler does not support credential acquisition") + AuthenticationMechanismException mechCallbackHandlerDoesNotSupportCredentialAcquisition(@Cause Throwable cause); + + @Message(id = 5052, value = "Callback handler does not support authorization") + AuthenticationMechanismException mechAuthorizationUnsupported(@Cause Throwable cause); + + @Message(id = 5053, value = "Callback handler failed for unknown reason") + AuthenticationMechanismException mechCallbackHandlerFailedForUnknownReason(@Cause Throwable cause); + + @Message(id = 5055, value = "Authentication rejected (invalid proof)") + AuthenticationMechanismException mechAuthenticationRejectedInvalidProof(); + + @Message(id = 5056, value = "Client sent extra message") + AuthenticationMechanismException mechClientSentExtraMessage(); + + @Message(id = 5057, value = "Server sent extra message") + AuthenticationMechanismException mechServerSentExtraMessage(); + + @Message(id = 5058, value = "Authentication failed") + AuthenticationMechanismException mechAuthenticationFailed(); + + @Message(id = 5060, value = "Empty number") + NumberFormatException emptyNumber(); + + @Message(id = 5061, value = "Invalid numeric character") + NumberFormatException invalidNumericCharacter(); + + @Message(id = 5062, value = "Too big number") + NumberFormatException tooBigNumber(); + + @Message(id = 5063, value = "Cannot get clear password from two way password") + AuthenticationMechanismException mechCannotGetTwoWayPasswordChars(@Cause Throwable cause); + + @Message(id = 5064, value = "Hashing algorithm not supported") + AuthenticationMechanismException mechMacAlgorithmNotSupported(@Cause Throwable cause); + + @Message(id = 5065, value = "keyword cannot be empty") + AuthenticationMechanismException mechKeywordCannotBeEmpty(); + + @Message(id = 5066, value = "No value found for keyword: %s") + AuthenticationMechanismException mechNoValueFoundForKeyword(String keyword); + + @Message(id = 5067, value = "'=' expected after keyword: %s") + AuthenticationMechanismException mechKeywordNotFollowedByEqual(String keyword); + + @Message(id = 5068, value = "Unmatched quote found for value: %s") + AuthenticationMechanismException mechUnmatchedQuoteFoundForValue(String value); + + @Message(id = 5069, value = "Expecting comma or linear whitespace after quoted string: %s") + AuthenticationMechanismException mechExpectingCommaOrLinearWhitespaceAfterQuoted(String value); + + @Message(id = 5070, value = "MessageType must equal to %d, but it is %d") + AuthenticationMechanismException mechMessageTypeMustEqual(int expected, int actual); + + @Message(id = 5071, value = "Bad sequence number while unwrapping: expected %d, but %d received") + AuthenticationMechanismException mechBadSequenceNumberWhileUnwrapping(int expected, int actual); + + @Message(id = 5072, value = "Problem during crypt") + AuthenticationMechanismException mechProblemDuringCrypt(@Cause Throwable cause); + + @Message(id = 5073, value = "Problem during decrypt") + AuthenticationMechanismException mechProblemDuringDecrypt(@Cause Throwable cause); + + @Message(id = 5074, value = "Unknown cipher \"%s\"") + AuthenticationMechanismException mechUnknownCipher(String cipher); + + @Message(id = 5075, value = "Authorization ID changed unexpectedly") + AuthenticationMechanismException mechAuthorizationIdChanged(); + + @Message(id = 5076, value = "Problem getting required cipher. Check your transformation mapper settings.") + AuthenticationMechanismException mechProblemGettingRequiredCipher(@Cause Throwable cause); + + @Message(id = 5077, value = "No common protection layer between client and server") + AuthenticationMechanismException mechNoCommonProtectionLayer(); + + @Message(id = 5078, value = "No common cipher between client and server") + AuthenticationMechanismException mechNoCommonCipher(); + + @Message(id = 5079, value = "No ciphers offered by server") + AuthenticationMechanismException mechNoCiphersOfferedByServer(); + + @Message(id = 5080, value = "Callback handler not provided user name") + AuthenticationMechanismException mechNotProvidedUserName(); + + @Message(id = 5083, value = "Missing \"%s\" directive") + AuthenticationMechanismException mechMissingDirective(String directive); + + @Message(id = 5084, value = "nonce-count must equal to %d, but it is %d") + AuthenticationMechanismException mechNonceCountMustEqual(int expected, int actual); + + @Message(id = 5085, value = "Server is set to not support %s charset") + AuthenticationMechanismException mechUnsupportedCharset(String charset); + + @Message(id = 5086, value = "Charset can be only \"utf-8\" or unspecified (to use ISO 8859-1)") + AuthenticationMechanismException mechUnknownCharset(); + + @Message(id = 5087, value = "Client selected realm not offered by server (%s)") + AuthenticationMechanismException mechDisallowedClientRealm(String clientRealm); + + @Message(id = 5088, value = "digest-uri \"%s\" not accepted") + AuthenticationMechanismException mechMismatchedWrongDigestUri(String actual); + + @Message(id = 5089, value = "Unexpected qop value: \"%s\"") + AuthenticationMechanismException mechUnexpectedQop(String qop); + + @Message(id = 5090, value = "Wrapping is not configured") + IllegalStateException wrappingNotConfigured(); + + @Message(id = 5091, value = "Authentication name string is too long") + AuthenticationMechanismException mechAuthenticationNameTooLong(); + + @Message(id = 5092, value = "Authentication name is empty") + AuthenticationMechanismException mechAuthenticationNameIsEmpty(); + + @Message(id = 5093, value = "Authorization for anonymous access is denied") + AuthenticationMechanismException mechAnonymousAuthorizationDenied(); + + @Message(id = 5094, value = "Required padded length (%d) is less than length of conversion result (%d)") + IllegalArgumentException requiredNegativePadding(int totalLength, int hexLength); + + @Message(id = 5095, value = "Invalid key provided for Digest HMAC computing") + AuthenticationMechanismException mechInvalidKeyForDigestHMAC(); + + @Message(id = 5097, value = "Unable to determine subject name from X.509 certificate") + IllegalStateException unableToDetermineSubjectName(@Cause Throwable cause); + + @Message(id = 5098, value = "Unable to verify client signature") + AuthenticationMechanismException mechUnableToVerifyClientSignature(@Cause Throwable cause); + + @Message(id = 5099, value = "Unable to verify server signature") + AuthenticationMechanismException mechUnableToVerifyServerSignature(@Cause Throwable cause); + + @Message(id = 5101, value = "Callback handler not provided server certificate") + AuthenticationMechanismException mechCallbackHandlerNotProvidedServerCertificate(); + + @Message(id = 5102, value = "Callback handler not provided client certificate") + AuthenticationMechanismException mechCallbackHandlerNotProvidedClientCertificate(); + + @Message(id = 5103, value = "Server identifier mismatch") + AuthenticationMechanismException mechServerIdentifierMismatch(); + + @Message(id = 5104, value = "Client identifier mismatch") + AuthenticationMechanismException mechClientIdentifierMismatch(); + + @Message(id = 5105, value = "Unable to determine client name") + AuthenticationMechanismException mechUnableToDetermineClientName(@Cause Throwable cause); + + @Message(id = 5106, value = "Callback handler not provided private key") + AuthenticationMechanismException mechCallbackHandlerNotProvidedPrivateKey(); + + @Message(id = 5107, value = "Unable to create signature") + AuthenticationMechanismException mechUnableToCreateSignature(@Cause Throwable cause); + + @Message(id = 5108, value = "Unable to create response token") + AuthenticationMechanismException mechUnableToCreateResponseToken(@Cause Throwable cause); + + @Message(id = 5109, value = "Unable to create response token") + AuthenticationMechanismException mechUnableToCreateResponseTokenWithCause(@Cause Throwable cause); + + @Message(id = 5112, value = "Getting authentication mechanisms supported by GSS-API failed") + AuthenticationMechanismException mechGettingSupportedMechanismsFailed(@Cause Throwable cause); + + @Message(id = 5113, value = "Unable to initialize OID of Kerberos V5") + RuntimeException unableToInitialiseOid(@Cause Throwable cause); + + @Message(id = 5114, value = "Receive buffer requested '%d' is greater than supported maximum '%d'") + AuthenticationMechanismException mechReceiveBufferIsGreaterThanMaximum(int requested, int maximum); + + @Message(id = 5115, value = "Unable to wrap message") + AuthenticationMechanismException mechUnableToWrapMessage(@Cause Throwable cause); + + @Message(id = 5116, value = "Unable to unwrap message") + AuthenticationMechanismException mechUnableToUnwrapMessage(@Cause Throwable cause); + + @Message(id = 5117, value = "Unable to unwrap security layer negotiation message") + AuthenticationMechanismException mechUnableToUnwrapSecurityLayerNegotiationMessage(@Cause Throwable cause); + + @Message(id = 5118, value = "Invalid message of length %d on unwrapping") + AuthenticationMechanismException mechInvalidMessageOnUnwrapping(int length); + + @Message(id = 5119, value = "Negotiated mechanism was not Kerberos V5") + AuthenticationMechanismException mechNegotiatedMechanismWasNotKerberosV5(); + + @Message(id = 5120, value = "Insufficient levels of protection available for supported security layers") + AuthenticationMechanismException mechInsufficientQopsAvailable(); + + @Message(id = 5121, value = "Unable to generate security layer challenge") + AuthenticationMechanismException mechUnableToGenerateChallenge(@Cause Throwable cause); + + @Message(id = 5122, value = "Client selected a security layer that was not offered by server") + AuthenticationMechanismException mechSelectedUnofferedQop(); + + @Message(id = 5123, value = "No security layer selected but message length received") + AuthenticationMechanismException mechNoSecurityLayerButLengthReceived(); + + @Message(id = 5124, value = "Unable to get maximum size of message before wrap") + AuthenticationMechanismException mechUnableToGetMaximumSizeOfMessage(@Cause Throwable cause); + + @Message(id = 5125, value = "Unable to handle response from server") + AuthenticationMechanismException mechUnableToHandleResponseFromServer(@Cause Throwable cause); + + @Message(id = 5126, value = "Bad length of message for negotiating security layer") + AuthenticationMechanismException mechBadLengthOfMessageForNegotiatingSecurityLayer(); + + @Message(id = 5127, value = "No security layer supported by server but maximum message size received: \"%d\"") + AuthenticationMechanismException mechReceivedMaxMessageSizeWhenNoSecurityLayer(int length); + + @Message(id = 5128, value = "Failed to read challenge file") + AuthenticationMechanismException mechFailedToReadChallengeFile(@Cause Throwable cause); + + @Message(id = 5129, value = "Failed to create challenge file") + AuthenticationMechanismException mechFailedToCreateChallengeFile(@Cause Throwable cause); + + @Message(id = 5150, value = "Authentication mechanism authorization ID is too long") + AuthenticationMechanismException mechAuthorizationIdTooLong(); + + @Message(id = 5151, value = "Invalid OTP algorithm \"%s\"") + AuthenticationMechanismException mechInvalidOTPAlgorithm(String algorithm); + + @Message(id = 5152, value = "Invalid OTP response type") + AuthenticationMechanismException mechInvalidOTPResponseType(); + + @Message(id = 5153, value = "Incorrect parity in SASL client message") + AuthenticationMechanismException mechIncorrectParity(); + + @Message(id = 5154, value = "Invalid character in seed") + AuthenticationMechanismException mechInvalidCharacterInSeed(); + + @Message(id = 5155, value = "Invalid OTP seed, must be between 1 and 16 characters long") + AuthenticationMechanismException mechInvalidOTPSeed(); + + @Message(id = 5156, value = "Invalid OTP pass phrase, must be between 10 and 63 characters long") + AuthenticationMechanismException mechInvalidOTPPassPhrase(); + + @Message(id = 5157, value = "Invalid OTP sequence number") + AuthenticationMechanismException mechInvalidOTPSequenceNumber(); + + @Message(id = 5158, value = "Invalid OTP") + AuthenticationMechanismException mechInvalidOTP(); + + @Message(id = 5159, value = "OTP pass phrase and seed must not match") + AuthenticationMechanismException mechOTPPassPhraseAndSeedMustNotMatch(); + + @Message(id = 5160, value = "Invalid OTP alternate dictionary") + AuthenticationMechanismException mechInvalidOTPAlternateDictionary(); + + @Message(id = 5161, value = "Unable to retrieve password for \"%s\"") + AuthenticationMechanismException mechUnableToRetrievePassword(String userName); + + @Message(id = 5162, value = "Unable to update password for \"%s\"") + AuthenticationMechanismException mechUnableToUpdatePassword(String userName); + + @Message(id = 5163, value = "Authentication mechanism server timed out") + AuthenticationMechanismException mechServerTimedOut(); + + @Message(id = 5164, value = "Unable to obtain exclusive access for \"%s\"") + AuthenticationMechanismException mechUnableToObtainExclusiveAccess(String userName); + + @Message(id = 5165, value = "OTP re-initialization failed") + AuthenticationMechanismException mechOTPReinitializationFailed(@Cause Throwable cause); + + @Message(id = 5166, value = "Server rejected authentication") + ScramServerException scramServerRejectedAuthentication(@Param ScramServerErrorCode errorCode); + + @Message(id = 5167, value = "Invalid OTP password format type") + AuthenticationMechanismException mechInvalidOTPPasswordFormatType(); + + @Message(id = 5168, value = "Unsupported algorithm selected \"%s\"") + AuthenticationMechanismException mechUnsupportedAlgorithm(String algorithm); + + @Message(id = 5169, value = "[%s] Clients response token does not match expected token") + String mechResponseTokenMismatch(String mechName); + + @Message(id = 5170, value = "Problem during crypt: The encrypted result is null. The input data has a length of zero or too short to result in a new block.") + AuthenticationMechanismException mechProblemDuringCryptResultIsNull(); + + @Message(id = 5171, value = "Problem during decrypt: The decrypted result is null. The input data has a length of zero or too short to result in a new block.") + AuthenticationMechanismException mechProblemDuringDecryptResultIsNull(); + + @Message(id = 5173, value = "Unable to obtain server credential.") + AuthenticationMechanismException unableToObtainServerCredential(); + + @Message(id = 5174, value = "Callback handler has not chosen realm") + AuthenticationMechanismException mechNotChosenRealm(); + + @Message(id = 5175, value = "Unable to determine bound server name") + AuthenticationMechanismException mechUnableToDetermineBoundServerName(@Cause Exception e); + + @Message(id = 5176, value = "Unsupported callback") + AuthenticationMechanismException mechCallbackHandlerUnsupportedCallback(@Cause Throwable cause); + + @Message(id = 5177, value = "One of \"%s\" and \"%s\" directives has to be defined") + AuthenticationMechanismException mechOneOfDirectivesHasToBeDefined(String directive1, String directive2); + + @Message(id = 6001, value = "An incorrectly formatted '%s'header was encountered.") + String incorrectlyFormattedHeader(String headerName); + + @Message(id = 6002, value = "An authentication attempt for user '%s' failed validation using mechanism '%s'.") + String authenticationFailed(String username, String mechanismName); + + @Message(id = 6003, value = "An authentication attempt failed validation.") + String authenticationFailed(); + + @Message(id = 6006, value = "An authorization check for user '%s' failed.") + String authorizationFailed(String username); + + @Message(id = 6007, value = "Username or password missing from authentication attempt.") + String usernameOrPasswordMissing(); + + @Message(id = 6015, value = "Unable to authenticate using DIGEST mechanism - realm name needs to be specified") + HttpAuthenticationException digestMechanismRequireRealm(); + + @Message(id = 6019, value = "Unable to authenticate using DIGEST mechanism - mechanism realm name (%s) is not valid") + HttpAuthenticationException digestMechanismInvalidRealm(String realm); + + @Message(id = 6020, value = "Scope unsuitable for use with authentication state '%s'") + IllegalArgumentException unsuitableScope(String scopeName); + + @Message(id = 6021, value = "Unable to identify suitable HttpScope for mechanism state storage") + IllegalArgumentException unableToIdentifyHttpScope(); + + @Message(id = 6022, value = "Invalid nonce count %s") + HttpAuthenticationException invalidNonceCount(int nonceCount); + + @Message(id = 7001, value = "Unrecognized encoding algorithm [%s]") + ASN1Exception asnUnrecognisedAlgorithm(String algorithm); + + @Message(id = 7002, value = "Invalid general name type") + ASN1Exception asnInvalidGeneralNameType(); + + @Message(id = 7004, value = "Unexpected ASN.1 tag encountered") + ASN1Exception asnUnexpectedTag(); +} Index: 3rdParty_sources/elytron/org/wildfly/security/mechanism/_private/ElytronMessages_$logger.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/mechanism/_private/ElytronMessages_$logger.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/mechanism/_private/ElytronMessages_$logger.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,1426 @@ +package org.wildfly.security.mechanism._private; + +import java.util.Locale; +import java.lang.IllegalStateException; +import java.io.Serializable; +import javax.annotation.Generated; +import org.wildfly.security.mechanism.ScramServerException; +import org.jboss.logging.DelegatingBasicLogger; +import org.wildfly.security.http.HttpAuthenticationException; +import org.wildfly.security.asn1.ASN1Exception; +import java.lang.NumberFormatException; +import java.lang.SecurityException; +import java.lang.String; +import org.jboss.logging.Logger; +import org.wildfly.security.mechanism.ScramServerErrorCode; +import java.lang.Exception; +import java.lang.RuntimeException; +import org.jboss.logging.BasicLogger; +import java.lang.Throwable; +import java.util.Arrays; +import java.lang.IllegalArgumentException; +import org.wildfly.security.mechanism.AuthenticationMechanismException; + + +import static org.jboss.logging.Logger.Level.WARN; + +/** + * Warning this class consists of generated code. + */ +@Generated(value = "org.jboss.logging.processor.generator.model.MessageLoggerImplementor", date = "2024-01-15T21:47:14+0100") +public class ElytronMessages_$logger extends DelegatingBasicLogger implements ElytronMessages, BasicLogger, Serializable { + private static final long serialVersionUID = 1L; + private static final String FQCN = ElytronMessages_$logger.class.getName(); + public ElytronMessages_$logger(final Logger log) { + super(log); + } + private static final Locale LOCALE = Locale.ROOT; + protected Locale getLoggingLocale() { + return LOCALE; + } + @Override + public final void credentialDestroyingFailed(final Throwable cause) { + super.log.logf(FQCN, WARN, cause, credentialDestroyingFailed$str()); + } + protected String credentialDestroyingFailed$str() { + return "ELY00007: Credential destroying failed"; + } + protected String authenticationFailedEvidenceVerification$str() { + return "ELY01151: Evidence Verification Failed."; + } + @Override + public final SecurityException authenticationFailedEvidenceVerification() { + final SecurityException result = new SecurityException(String.format(getLoggingLocale(), authenticationFailedEvidenceVerification$str())); + _copyStackTraceMinusOne(result); + return result; + } + private static void _copyStackTraceMinusOne(final Throwable e) { + final StackTraceElement[] st = e.getStackTrace(); + e.setStackTrace(Arrays.copyOfRange(st, 1, st.length)); + } + protected String mechMessageAfterComplete$str() { + return "ELY05001: Authentication mechanism exchange received a message after authentication was already complete"; + } + @Override + public final AuthenticationMechanismException mechMessageAfterComplete() { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechMessageAfterComplete$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechUserNameContainsInvalidCharacter$str() { + return "ELY05002: Authentication mechanism user name contains an invalid or disallowed character"; + } + @Override + public final AuthenticationMechanismException mechUserNameContainsInvalidCharacter() { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechUserNameContainsInvalidCharacter$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechAuthorizationFailed0$str() { + return "ELY05004: Authentication mechanism authorization failed"; + } + @Override + public final AuthenticationMechanismException mechAuthorizationFailed(final Throwable cause) { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechAuthorizationFailed0$str()), cause); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechAuthenticationNotComplete$str() { + return "ELY05005: Authentication mechanism authentication is not yet complete"; + } + @Override + public final IllegalStateException mechAuthenticationNotComplete() { + final IllegalStateException result = new IllegalStateException(String.format(getLoggingLocale(), mechAuthenticationNotComplete$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechNoSecurityLayer$str() { + return "ELY05006: Authentication mechanism does not support security layer (wrapping/unwrapping)"; + } + @Override + public final IllegalStateException mechNoSecurityLayer() { + final IllegalStateException result = new IllegalStateException(String.format(getLoggingLocale(), mechNoSecurityLayer$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechInvalidMessageReceived$str() { + return "ELY05007: Invalid authentication mechanism negotiation message received"; + } + @Override + public final AuthenticationMechanismException mechInvalidMessageReceived() { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechInvalidMessageReceived$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechNoLoginNameGiven$str() { + return "ELY05008: No authentication mechanism login name was given"; + } + @Override + public final AuthenticationMechanismException mechNoLoginNameGiven() { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechNoLoginNameGiven$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechNoPasswordGiven$str() { + return "ELY05009: No authentication mechanism password was given"; + } + @Override + public final AuthenticationMechanismException mechNoPasswordGiven() { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechNoPasswordGiven$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechMalformedFields$str() { + return "ELY05010: Authentication mechanism authentication failed due to one or more malformed fields"; + } + @Override + public final AuthenticationMechanismException mechMalformedFields(final IllegalArgumentException ex) { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechMalformedFields$str()), ex); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechMessageTooLong$str() { + return "ELY05011: Authentication mechanism message is too long"; + } + @Override + public final AuthenticationMechanismException mechMessageTooLong() { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechMessageTooLong$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechServerSideAuthenticationFailed$str() { + return "ELY05012: Authentication mechanism server-side authentication failed"; + } + @Override + public final AuthenticationMechanismException mechServerSideAuthenticationFailed(final Exception e) { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechServerSideAuthenticationFailed$str()), e); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechPasswordNotVerified$str() { + return "ELY05013: Authentication mechanism password not verified"; + } + @Override + public final AuthenticationMechanismException mechPasswordNotVerified() { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechPasswordNotVerified$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechAuthorizationFailed2$str() { + return "ELY05014: Authentication mechanism authorization failed: \"%s\" running as \"%s\""; + } + @Override + public final AuthenticationMechanismException mechAuthorizationFailed(final String userName, final String authorizationId) { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechAuthorizationFailed2$str(), userName, authorizationId)); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechChannelBindingChanged$str() { + return "ELY05018: Channel binding data changed"; + } + @Override + public final AuthenticationMechanismException mechChannelBindingChanged() { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechChannelBindingChanged$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechNoTokenGiven$str() { + return "ELY05019: No token was given"; + } + @Override + public final AuthenticationMechanismException mechNoTokenGiven() { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechNoTokenGiven$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechInitialChallengeMustBeEmpty$str() { + return "ELY05022: Initial challenge must be empty"; + } + @Override + public final AuthenticationMechanismException mechInitialChallengeMustBeEmpty() { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechInitialChallengeMustBeEmpty$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechUnableToSetChannelBinding$str() { + return "ELY05023: Unable to set channel binding"; + } + @Override + public final AuthenticationMechanismException mechUnableToSetChannelBinding(final Exception e) { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechUnableToSetChannelBinding$str()), e); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechFailedToDetermineChannelBindingStatus$str() { + return "ELY05024: Failed to determine channel binding status"; + } + @Override + public final AuthenticationMechanismException mechFailedToDetermineChannelBindingStatus(final Exception e) { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechFailedToDetermineChannelBindingStatus$str()), e); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechMutualAuthenticationNotEnabled$str() { + return "ELY05025: Mutual authentication not enabled"; + } + @Override + public final AuthenticationMechanismException mechMutualAuthenticationNotEnabled() { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechMutualAuthenticationNotEnabled$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechMechanismToOidMappingFailed$str() { + return "ELY05026: Unable to map SASL mechanism name to a GSS-API OID"; + } + @Override + public final AuthenticationMechanismException mechMechanismToOidMappingFailed(final Exception e) { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechMechanismToOidMappingFailed$str()), e); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechUnableToDisposeGssContext$str() { + return "ELY05027: Unable to dispose of GSSContext"; + } + @Override + public final AuthenticationMechanismException mechUnableToDisposeGssContext(final Exception e) { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechUnableToDisposeGssContext$str()), e); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechUnableToCreateNameForAcceptor$str() { + return "ELY05028: Unable to create name for acceptor"; + } + @Override + public final AuthenticationMechanismException mechUnableToCreateNameForAcceptor(final Exception e) { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechUnableToCreateNameForAcceptor$str()), e); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechUnableToCreateGssContext$str() { + return "ELY05029: Unable to create GSSContext"; + } + @Override + public final AuthenticationMechanismException mechUnableToCreateGssContext(final Exception e) { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechUnableToCreateGssContext$str()), e); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechUnableToSetGssContextRequestFlags$str() { + return "ELY05030: Unable to set GSSContext request flags"; + } + @Override + public final AuthenticationMechanismException mechUnableToSetGssContextRequestFlags(final Exception e) { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechUnableToSetGssContextRequestFlags$str()), e); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechUnableToAcceptClientMessage$str() { + return "ELY05031: Unable to accept SASL client message"; + } + @Override + public final AuthenticationMechanismException mechUnableToAcceptClientMessage(final Exception e) { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechUnableToAcceptClientMessage$str()), e); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechGssApiMechanismMismatch$str() { + return "ELY05032: GSS-API mechanism mismatch between SASL client and server"; + } + @Override + public final AuthenticationMechanismException mechGssApiMechanismMismatch() { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechGssApiMechanismMismatch$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechChannelBindingNotSupported$str() { + return "ELY05033: Channel binding not supported for this SASL mechanism"; + } + @Override + public final AuthenticationMechanismException mechChannelBindingNotSupported() { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechChannelBindingNotSupported$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechChannelBindingTypeMismatch$str() { + return "ELY05034: Channel binding type mismatch between SASL client and server"; + } + @Override + public final AuthenticationMechanismException mechChannelBindingTypeMismatch() { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechChannelBindingTypeMismatch$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechChannelBindingNotProvided$str() { + return "ELY05035: Channel binding not provided by client"; + } + @Override + public final AuthenticationMechanismException mechChannelBindingNotProvided() { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechChannelBindingNotProvided$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechUnableToDeterminePeerName$str() { + return "ELY05036: Unable to determine peer name"; + } + @Override + public final AuthenticationMechanismException mechUnableToDeterminePeerName(final Exception e) { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechUnableToDeterminePeerName$str()), e); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechClientRefusesToInitiateAuthentication$str() { + return "ELY05037: Authentication mechanism client refuses to initiate authentication"; + } + @Override + public final AuthenticationMechanismException mechClientRefusesToInitiateAuthentication() { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechClientRefusesToInitiateAuthentication$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechNoncesDoNotMatch$str() { + return "ELY05038: Nonces do not match"; + } + @Override + public final AuthenticationMechanismException mechNoncesDoNotMatch() { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechNoncesDoNotMatch$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String invalidNonceLength$str() { + return "ELY05039: Invalid length of nonce received"; + } + @Override + public final AuthenticationMechanismException invalidNonceLength() { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), invalidNonceLength$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechIterationCountIsTooLow$str() { + return "ELY05040: Iteration count %d is below the minimum of %d"; + } + @Override + public final AuthenticationMechanismException mechIterationCountIsTooLow(final int iterationCount, final int minimumIterationCount) { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechIterationCountIsTooLow$str(), iterationCount, minimumIterationCount)); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechIterationCountIsTooHigh$str() { + return "ELY05041: Iteration count %d is above the maximum of %d"; + } + @Override + public final AuthenticationMechanismException mechIterationCountIsTooHigh(final int iterationCount, final int maximumIterationCount) { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechIterationCountIsTooHigh$str(), iterationCount, maximumIterationCount)); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechInvalidServerMessage$str() { + return "ELY05043: Invalid server message"; + } + @Override + public final AuthenticationMechanismException mechInvalidServerMessage() { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechInvalidServerMessage$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechInvalidServerMessageWithCause$str() { + return "ELY05044: Invalid server message"; + } + @Override + public final AuthenticationMechanismException mechInvalidServerMessageWithCause(final Throwable cause) { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechInvalidServerMessageWithCause$str()), cause); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechInvalidClientMessage$str() { + return "ELY05045: Invalid client message"; + } + @Override + public final AuthenticationMechanismException mechInvalidClientMessage() { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechInvalidClientMessage$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechInvalidClientMessageWithCause$str() { + return "ELY05046: Invalid client message"; + } + @Override + public final AuthenticationMechanismException mechInvalidClientMessageWithCause(final Throwable cause) { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechInvalidClientMessageWithCause$str()), cause); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechUnmatchedMechanism$str() { + return "ELY05047: [%s] Authentication mechanism message is for mismatched mechanism \"%s\""; + } + @Override + public final AuthenticationMechanismException mechUnmatchedMechanism(final String mechName, final String otherMechName) { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechUnmatchedMechanism$str(), mechName, otherMechName)); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechServerAuthenticityCannotBeVerified$str() { + return "ELY05049: Server authenticity cannot be verified"; + } + @Override + public final AuthenticationMechanismException mechServerAuthenticityCannotBeVerified() { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechServerAuthenticityCannotBeVerified$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechCallbackHandlerDoesNotSupportUserName$str() { + return "ELY05050: Callback handler does not support user name"; + } + @Override + public final AuthenticationMechanismException mechCallbackHandlerDoesNotSupportUserName(final Throwable cause) { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechCallbackHandlerDoesNotSupportUserName$str()), cause); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechCallbackHandlerDoesNotSupportCredentialAcquisition$str() { + return "ELY05051: Callback handler does not support credential acquisition"; + } + @Override + public final AuthenticationMechanismException mechCallbackHandlerDoesNotSupportCredentialAcquisition(final Throwable cause) { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechCallbackHandlerDoesNotSupportCredentialAcquisition$str()), cause); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechAuthorizationUnsupported$str() { + return "ELY05052: Callback handler does not support authorization"; + } + @Override + public final AuthenticationMechanismException mechAuthorizationUnsupported(final Throwable cause) { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechAuthorizationUnsupported$str()), cause); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechCallbackHandlerFailedForUnknownReason$str() { + return "ELY05053: Callback handler failed for unknown reason"; + } + @Override + public final AuthenticationMechanismException mechCallbackHandlerFailedForUnknownReason(final Throwable cause) { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechCallbackHandlerFailedForUnknownReason$str()), cause); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechAuthenticationRejectedInvalidProof$str() { + return "ELY05055: Authentication rejected (invalid proof)"; + } + @Override + public final AuthenticationMechanismException mechAuthenticationRejectedInvalidProof() { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechAuthenticationRejectedInvalidProof$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechClientSentExtraMessage$str() { + return "ELY05056: Client sent extra message"; + } + @Override + public final AuthenticationMechanismException mechClientSentExtraMessage() { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechClientSentExtraMessage$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechServerSentExtraMessage$str() { + return "ELY05057: Server sent extra message"; + } + @Override + public final AuthenticationMechanismException mechServerSentExtraMessage() { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechServerSentExtraMessage$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechAuthenticationFailed$str() { + return "ELY05058: Authentication failed"; + } + @Override + public final AuthenticationMechanismException mechAuthenticationFailed() { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechAuthenticationFailed$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String emptyNumber$str() { + return "ELY05060: Empty number"; + } + @Override + public final NumberFormatException emptyNumber() { + final NumberFormatException result = new NumberFormatException(String.format(getLoggingLocale(), emptyNumber$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String invalidNumericCharacter$str() { + return "ELY05061: Invalid numeric character"; + } + @Override + public final NumberFormatException invalidNumericCharacter() { + final NumberFormatException result = new NumberFormatException(String.format(getLoggingLocale(), invalidNumericCharacter$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String tooBigNumber$str() { + return "ELY05062: Too big number"; + } + @Override + public final NumberFormatException tooBigNumber() { + final NumberFormatException result = new NumberFormatException(String.format(getLoggingLocale(), tooBigNumber$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechCannotGetTwoWayPasswordChars$str() { + return "ELY05063: Cannot get clear password from two way password"; + } + @Override + public final AuthenticationMechanismException mechCannotGetTwoWayPasswordChars(final Throwable cause) { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechCannotGetTwoWayPasswordChars$str()), cause); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechMacAlgorithmNotSupported$str() { + return "ELY05064: Hashing algorithm not supported"; + } + @Override + public final AuthenticationMechanismException mechMacAlgorithmNotSupported(final Throwable cause) { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechMacAlgorithmNotSupported$str()), cause); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechKeywordCannotBeEmpty$str() { + return "ELY05065: keyword cannot be empty"; + } + @Override + public final AuthenticationMechanismException mechKeywordCannotBeEmpty() { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechKeywordCannotBeEmpty$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechNoValueFoundForKeyword$str() { + return "ELY05066: No value found for keyword: %s"; + } + @Override + public final AuthenticationMechanismException mechNoValueFoundForKeyword(final String keyword) { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechNoValueFoundForKeyword$str(), keyword)); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechKeywordNotFollowedByEqual$str() { + return "ELY05067: '=' expected after keyword: %s"; + } + @Override + public final AuthenticationMechanismException mechKeywordNotFollowedByEqual(final String keyword) { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechKeywordNotFollowedByEqual$str(), keyword)); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechUnmatchedQuoteFoundForValue$str() { + return "ELY05068: Unmatched quote found for value: %s"; + } + @Override + public final AuthenticationMechanismException mechUnmatchedQuoteFoundForValue(final String value) { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechUnmatchedQuoteFoundForValue$str(), value)); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechExpectingCommaOrLinearWhitespaceAfterQuoted$str() { + return "ELY05069: Expecting comma or linear whitespace after quoted string: %s"; + } + @Override + public final AuthenticationMechanismException mechExpectingCommaOrLinearWhitespaceAfterQuoted(final String value) { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechExpectingCommaOrLinearWhitespaceAfterQuoted$str(), value)); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechMessageTypeMustEqual$str() { + return "ELY05070: MessageType must equal to %d, but it is %d"; + } + @Override + public final AuthenticationMechanismException mechMessageTypeMustEqual(final int expected, final int actual) { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechMessageTypeMustEqual$str(), expected, actual)); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechBadSequenceNumberWhileUnwrapping$str() { + return "ELY05071: Bad sequence number while unwrapping: expected %d, but %d received"; + } + @Override + public final AuthenticationMechanismException mechBadSequenceNumberWhileUnwrapping(final int expected, final int actual) { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechBadSequenceNumberWhileUnwrapping$str(), expected, actual)); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechProblemDuringCrypt$str() { + return "ELY05072: Problem during crypt"; + } + @Override + public final AuthenticationMechanismException mechProblemDuringCrypt(final Throwable cause) { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechProblemDuringCrypt$str()), cause); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechProblemDuringDecrypt$str() { + return "ELY05073: Problem during decrypt"; + } + @Override + public final AuthenticationMechanismException mechProblemDuringDecrypt(final Throwable cause) { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechProblemDuringDecrypt$str()), cause); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechUnknownCipher$str() { + return "ELY05074: Unknown cipher \"%s\""; + } + @Override + public final AuthenticationMechanismException mechUnknownCipher(final String cipher) { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechUnknownCipher$str(), cipher)); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechAuthorizationIdChanged$str() { + return "ELY05075: Authorization ID changed unexpectedly"; + } + @Override + public final AuthenticationMechanismException mechAuthorizationIdChanged() { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechAuthorizationIdChanged$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechProblemGettingRequiredCipher$str() { + return "ELY05076: Problem getting required cipher. Check your transformation mapper settings."; + } + @Override + public final AuthenticationMechanismException mechProblemGettingRequiredCipher(final Throwable cause) { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechProblemGettingRequiredCipher$str()), cause); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechNoCommonProtectionLayer$str() { + return "ELY05077: No common protection layer between client and server"; + } + @Override + public final AuthenticationMechanismException mechNoCommonProtectionLayer() { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechNoCommonProtectionLayer$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechNoCommonCipher$str() { + return "ELY05078: No common cipher between client and server"; + } + @Override + public final AuthenticationMechanismException mechNoCommonCipher() { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechNoCommonCipher$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechNoCiphersOfferedByServer$str() { + return "ELY05079: No ciphers offered by server"; + } + @Override + public final AuthenticationMechanismException mechNoCiphersOfferedByServer() { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechNoCiphersOfferedByServer$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechNotProvidedUserName$str() { + return "ELY05080: Callback handler not provided user name"; + } + @Override + public final AuthenticationMechanismException mechNotProvidedUserName() { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechNotProvidedUserName$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechMissingDirective$str() { + return "ELY05083: Missing \"%s\" directive"; + } + @Override + public final AuthenticationMechanismException mechMissingDirective(final String directive) { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechMissingDirective$str(), directive)); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechNonceCountMustEqual$str() { + return "ELY05084: nonce-count must equal to %d, but it is %d"; + } + @Override + public final AuthenticationMechanismException mechNonceCountMustEqual(final int expected, final int actual) { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechNonceCountMustEqual$str(), expected, actual)); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechUnsupportedCharset$str() { + return "ELY05085: Server is set to not support %s charset"; + } + @Override + public final AuthenticationMechanismException mechUnsupportedCharset(final String charset) { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechUnsupportedCharset$str(), charset)); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechUnknownCharset$str() { + return "ELY05086: Charset can be only \"utf-8\" or unspecified (to use ISO 8859-1)"; + } + @Override + public final AuthenticationMechanismException mechUnknownCharset() { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechUnknownCharset$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechDisallowedClientRealm$str() { + return "ELY05087: Client selected realm not offered by server (%s)"; + } + @Override + public final AuthenticationMechanismException mechDisallowedClientRealm(final String clientRealm) { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechDisallowedClientRealm$str(), clientRealm)); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechMismatchedWrongDigestUri$str() { + return "ELY05088: digest-uri \"%s\" not accepted"; + } + @Override + public final AuthenticationMechanismException mechMismatchedWrongDigestUri(final String actual) { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechMismatchedWrongDigestUri$str(), actual)); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechUnexpectedQop$str() { + return "ELY05089: Unexpected qop value: \"%s\""; + } + @Override + public final AuthenticationMechanismException mechUnexpectedQop(final String qop) { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechUnexpectedQop$str(), qop)); + _copyStackTraceMinusOne(result); + return result; + } + protected String wrappingNotConfigured$str() { + return "ELY05090: Wrapping is not configured"; + } + @Override + public final IllegalStateException wrappingNotConfigured() { + final IllegalStateException result = new IllegalStateException(String.format(getLoggingLocale(), wrappingNotConfigured$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechAuthenticationNameTooLong$str() { + return "ELY05091: Authentication name string is too long"; + } + @Override + public final AuthenticationMechanismException mechAuthenticationNameTooLong() { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechAuthenticationNameTooLong$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechAuthenticationNameIsEmpty$str() { + return "ELY05092: Authentication name is empty"; + } + @Override + public final AuthenticationMechanismException mechAuthenticationNameIsEmpty() { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechAuthenticationNameIsEmpty$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechAnonymousAuthorizationDenied$str() { + return "ELY05093: Authorization for anonymous access is denied"; + } + @Override + public final AuthenticationMechanismException mechAnonymousAuthorizationDenied() { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechAnonymousAuthorizationDenied$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String requiredNegativePadding$str() { + return "ELY05094: Required padded length (%d) is less than length of conversion result (%d)"; + } + @Override + public final IllegalArgumentException requiredNegativePadding(final int totalLength, final int hexLength) { + final IllegalArgumentException result = new IllegalArgumentException(String.format(getLoggingLocale(), requiredNegativePadding$str(), totalLength, hexLength)); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechInvalidKeyForDigestHMAC$str() { + return "ELY05095: Invalid key provided for Digest HMAC computing"; + } + @Override + public final AuthenticationMechanismException mechInvalidKeyForDigestHMAC() { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechInvalidKeyForDigestHMAC$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String unableToDetermineSubjectName$str() { + return "ELY05097: Unable to determine subject name from X.509 certificate"; + } + @Override + public final IllegalStateException unableToDetermineSubjectName(final Throwable cause) { + final IllegalStateException result = new IllegalStateException(String.format(getLoggingLocale(), unableToDetermineSubjectName$str()), cause); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechUnableToVerifyClientSignature$str() { + return "ELY05098: Unable to verify client signature"; + } + @Override + public final AuthenticationMechanismException mechUnableToVerifyClientSignature(final Throwable cause) { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechUnableToVerifyClientSignature$str()), cause); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechUnableToVerifyServerSignature$str() { + return "ELY05099: Unable to verify server signature"; + } + @Override + public final AuthenticationMechanismException mechUnableToVerifyServerSignature(final Throwable cause) { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechUnableToVerifyServerSignature$str()), cause); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechCallbackHandlerNotProvidedServerCertificate$str() { + return "ELY05101: Callback handler not provided server certificate"; + } + @Override + public final AuthenticationMechanismException mechCallbackHandlerNotProvidedServerCertificate() { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechCallbackHandlerNotProvidedServerCertificate$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechCallbackHandlerNotProvidedClientCertificate$str() { + return "ELY05102: Callback handler not provided client certificate"; + } + @Override + public final AuthenticationMechanismException mechCallbackHandlerNotProvidedClientCertificate() { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechCallbackHandlerNotProvidedClientCertificate$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechServerIdentifierMismatch$str() { + return "ELY05103: Server identifier mismatch"; + } + @Override + public final AuthenticationMechanismException mechServerIdentifierMismatch() { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechServerIdentifierMismatch$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechClientIdentifierMismatch$str() { + return "ELY05104: Client identifier mismatch"; + } + @Override + public final AuthenticationMechanismException mechClientIdentifierMismatch() { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechClientIdentifierMismatch$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechUnableToDetermineClientName$str() { + return "ELY05105: Unable to determine client name"; + } + @Override + public final AuthenticationMechanismException mechUnableToDetermineClientName(final Throwable cause) { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechUnableToDetermineClientName$str()), cause); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechCallbackHandlerNotProvidedPrivateKey$str() { + return "ELY05106: Callback handler not provided private key"; + } + @Override + public final AuthenticationMechanismException mechCallbackHandlerNotProvidedPrivateKey() { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechCallbackHandlerNotProvidedPrivateKey$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechUnableToCreateSignature$str() { + return "ELY05107: Unable to create signature"; + } + @Override + public final AuthenticationMechanismException mechUnableToCreateSignature(final Throwable cause) { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechUnableToCreateSignature$str()), cause); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechUnableToCreateResponseToken$str() { + return "ELY05108: Unable to create response token"; + } + @Override + public final AuthenticationMechanismException mechUnableToCreateResponseToken(final Throwable cause) { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechUnableToCreateResponseToken$str()), cause); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechUnableToCreateResponseTokenWithCause$str() { + return "ELY05109: Unable to create response token"; + } + @Override + public final AuthenticationMechanismException mechUnableToCreateResponseTokenWithCause(final Throwable cause) { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechUnableToCreateResponseTokenWithCause$str()), cause); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechGettingSupportedMechanismsFailed$str() { + return "ELY05112: Getting authentication mechanisms supported by GSS-API failed"; + } + @Override + public final AuthenticationMechanismException mechGettingSupportedMechanismsFailed(final Throwable cause) { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechGettingSupportedMechanismsFailed$str()), cause); + _copyStackTraceMinusOne(result); + return result; + } + protected String unableToInitialiseOid$str() { + return "ELY05113: Unable to initialize OID of Kerberos V5"; + } + @Override + public final RuntimeException unableToInitialiseOid(final Throwable cause) { + final RuntimeException result = new RuntimeException(String.format(getLoggingLocale(), unableToInitialiseOid$str()), cause); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechReceiveBufferIsGreaterThanMaximum$str() { + return "ELY05114: Receive buffer requested '%d' is greater than supported maximum '%d'"; + } + @Override + public final AuthenticationMechanismException mechReceiveBufferIsGreaterThanMaximum(final int requested, final int maximum) { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechReceiveBufferIsGreaterThanMaximum$str(), requested, maximum)); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechUnableToWrapMessage$str() { + return "ELY05115: Unable to wrap message"; + } + @Override + public final AuthenticationMechanismException mechUnableToWrapMessage(final Throwable cause) { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechUnableToWrapMessage$str()), cause); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechUnableToUnwrapMessage$str() { + return "ELY05116: Unable to unwrap message"; + } + @Override + public final AuthenticationMechanismException mechUnableToUnwrapMessage(final Throwable cause) { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechUnableToUnwrapMessage$str()), cause); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechUnableToUnwrapSecurityLayerNegotiationMessage$str() { + return "ELY05117: Unable to unwrap security layer negotiation message"; + } + @Override + public final AuthenticationMechanismException mechUnableToUnwrapSecurityLayerNegotiationMessage(final Throwable cause) { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechUnableToUnwrapSecurityLayerNegotiationMessage$str()), cause); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechInvalidMessageOnUnwrapping$str() { + return "ELY05118: Invalid message of length %d on unwrapping"; + } + @Override + public final AuthenticationMechanismException mechInvalidMessageOnUnwrapping(final int length) { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechInvalidMessageOnUnwrapping$str(), length)); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechNegotiatedMechanismWasNotKerberosV5$str() { + return "ELY05119: Negotiated mechanism was not Kerberos V5"; + } + @Override + public final AuthenticationMechanismException mechNegotiatedMechanismWasNotKerberosV5() { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechNegotiatedMechanismWasNotKerberosV5$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechInsufficientQopsAvailable$str() { + return "ELY05120: Insufficient levels of protection available for supported security layers"; + } + @Override + public final AuthenticationMechanismException mechInsufficientQopsAvailable() { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechInsufficientQopsAvailable$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechUnableToGenerateChallenge$str() { + return "ELY05121: Unable to generate security layer challenge"; + } + @Override + public final AuthenticationMechanismException mechUnableToGenerateChallenge(final Throwable cause) { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechUnableToGenerateChallenge$str()), cause); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechSelectedUnofferedQop$str() { + return "ELY05122: Client selected a security layer that was not offered by server"; + } + @Override + public final AuthenticationMechanismException mechSelectedUnofferedQop() { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechSelectedUnofferedQop$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechNoSecurityLayerButLengthReceived$str() { + return "ELY05123: No security layer selected but message length received"; + } + @Override + public final AuthenticationMechanismException mechNoSecurityLayerButLengthReceived() { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechNoSecurityLayerButLengthReceived$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechUnableToGetMaximumSizeOfMessage$str() { + return "ELY05124: Unable to get maximum size of message before wrap"; + } + @Override + public final AuthenticationMechanismException mechUnableToGetMaximumSizeOfMessage(final Throwable cause) { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechUnableToGetMaximumSizeOfMessage$str()), cause); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechUnableToHandleResponseFromServer$str() { + return "ELY05125: Unable to handle response from server"; + } + @Override + public final AuthenticationMechanismException mechUnableToHandleResponseFromServer(final Throwable cause) { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechUnableToHandleResponseFromServer$str()), cause); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechBadLengthOfMessageForNegotiatingSecurityLayer$str() { + return "ELY05126: Bad length of message for negotiating security layer"; + } + @Override + public final AuthenticationMechanismException mechBadLengthOfMessageForNegotiatingSecurityLayer() { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechBadLengthOfMessageForNegotiatingSecurityLayer$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechReceivedMaxMessageSizeWhenNoSecurityLayer$str() { + return "ELY05127: No security layer supported by server but maximum message size received: \"%d\""; + } + @Override + public final AuthenticationMechanismException mechReceivedMaxMessageSizeWhenNoSecurityLayer(final int length) { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechReceivedMaxMessageSizeWhenNoSecurityLayer$str(), length)); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechFailedToReadChallengeFile$str() { + return "ELY05128: Failed to read challenge file"; + } + @Override + public final AuthenticationMechanismException mechFailedToReadChallengeFile(final Throwable cause) { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechFailedToReadChallengeFile$str()), cause); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechFailedToCreateChallengeFile$str() { + return "ELY05129: Failed to create challenge file"; + } + @Override + public final AuthenticationMechanismException mechFailedToCreateChallengeFile(final Throwable cause) { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechFailedToCreateChallengeFile$str()), cause); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechAuthorizationIdTooLong$str() { + return "ELY05150: Authentication mechanism authorization ID is too long"; + } + @Override + public final AuthenticationMechanismException mechAuthorizationIdTooLong() { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechAuthorizationIdTooLong$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechInvalidOTPAlgorithm$str() { + return "ELY05151: Invalid OTP algorithm \"%s\""; + } + @Override + public final AuthenticationMechanismException mechInvalidOTPAlgorithm(final String algorithm) { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechInvalidOTPAlgorithm$str(), algorithm)); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechInvalidOTPResponseType$str() { + return "ELY05152: Invalid OTP response type"; + } + @Override + public final AuthenticationMechanismException mechInvalidOTPResponseType() { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechInvalidOTPResponseType$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechIncorrectParity$str() { + return "ELY05153: Incorrect parity in SASL client message"; + } + @Override + public final AuthenticationMechanismException mechIncorrectParity() { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechIncorrectParity$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechInvalidCharacterInSeed$str() { + return "ELY05154: Invalid character in seed"; + } + @Override + public final AuthenticationMechanismException mechInvalidCharacterInSeed() { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechInvalidCharacterInSeed$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechInvalidOTPSeed$str() { + return "ELY05155: Invalid OTP seed, must be between 1 and 16 characters long"; + } + @Override + public final AuthenticationMechanismException mechInvalidOTPSeed() { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechInvalidOTPSeed$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechInvalidOTPPassPhrase$str() { + return "ELY05156: Invalid OTP pass phrase, must be between 10 and 63 characters long"; + } + @Override + public final AuthenticationMechanismException mechInvalidOTPPassPhrase() { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechInvalidOTPPassPhrase$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechInvalidOTPSequenceNumber$str() { + return "ELY05157: Invalid OTP sequence number"; + } + @Override + public final AuthenticationMechanismException mechInvalidOTPSequenceNumber() { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechInvalidOTPSequenceNumber$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechInvalidOTP$str() { + return "ELY05158: Invalid OTP"; + } + @Override + public final AuthenticationMechanismException mechInvalidOTP() { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechInvalidOTP$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechOTPPassPhraseAndSeedMustNotMatch$str() { + return "ELY05159: OTP pass phrase and seed must not match"; + } + @Override + public final AuthenticationMechanismException mechOTPPassPhraseAndSeedMustNotMatch() { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechOTPPassPhraseAndSeedMustNotMatch$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechInvalidOTPAlternateDictionary$str() { + return "ELY05160: Invalid OTP alternate dictionary"; + } + @Override + public final AuthenticationMechanismException mechInvalidOTPAlternateDictionary() { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechInvalidOTPAlternateDictionary$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechUnableToRetrievePassword$str() { + return "ELY05161: Unable to retrieve password for \"%s\""; + } + @Override + public final AuthenticationMechanismException mechUnableToRetrievePassword(final String userName) { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechUnableToRetrievePassword$str(), userName)); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechUnableToUpdatePassword$str() { + return "ELY05162: Unable to update password for \"%s\""; + } + @Override + public final AuthenticationMechanismException mechUnableToUpdatePassword(final String userName) { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechUnableToUpdatePassword$str(), userName)); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechServerTimedOut$str() { + return "ELY05163: Authentication mechanism server timed out"; + } + @Override + public final AuthenticationMechanismException mechServerTimedOut() { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechServerTimedOut$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechUnableToObtainExclusiveAccess$str() { + return "ELY05164: Unable to obtain exclusive access for \"%s\""; + } + @Override + public final AuthenticationMechanismException mechUnableToObtainExclusiveAccess(final String userName) { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechUnableToObtainExclusiveAccess$str(), userName)); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechOTPReinitializationFailed$str() { + return "ELY05165: OTP re-initialization failed"; + } + @Override + public final AuthenticationMechanismException mechOTPReinitializationFailed(final Throwable cause) { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechOTPReinitializationFailed$str()), cause); + _copyStackTraceMinusOne(result); + return result; + } + protected String scramServerRejectedAuthentication$str() { + return "ELY05166: Server rejected authentication"; + } + @Override + public final ScramServerException scramServerRejectedAuthentication(final ScramServerErrorCode errorCode) { + final ScramServerException result = new ScramServerException(String.format(getLoggingLocale(), scramServerRejectedAuthentication$str()), errorCode); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechInvalidOTPPasswordFormatType$str() { + return "ELY05167: Invalid OTP password format type"; + } + @Override + public final AuthenticationMechanismException mechInvalidOTPPasswordFormatType() { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechInvalidOTPPasswordFormatType$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechUnsupportedAlgorithm$str() { + return "ELY05168: Unsupported algorithm selected \"%s\""; + } + @Override + public final AuthenticationMechanismException mechUnsupportedAlgorithm(final String algorithm) { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechUnsupportedAlgorithm$str(), algorithm)); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechResponseTokenMismatch$str() { + return "ELY05169: [%s] Clients response token does not match expected token"; + } + @Override + public final String mechResponseTokenMismatch(final String mechName) { + return String.format(getLoggingLocale(), mechResponseTokenMismatch$str(), mechName); + } + protected String mechProblemDuringCryptResultIsNull$str() { + return "ELY05170: Problem during crypt: The encrypted result is null. The input data has a length of zero or too short to result in a new block."; + } + @Override + public final AuthenticationMechanismException mechProblemDuringCryptResultIsNull() { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechProblemDuringCryptResultIsNull$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechProblemDuringDecryptResultIsNull$str() { + return "ELY05171: Problem during decrypt: The decrypted result is null. The input data has a length of zero or too short to result in a new block."; + } + @Override + public final AuthenticationMechanismException mechProblemDuringDecryptResultIsNull() { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechProblemDuringDecryptResultIsNull$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String unableToObtainServerCredential$str() { + return "ELY05173: Unable to obtain server credential."; + } + @Override + public final AuthenticationMechanismException unableToObtainServerCredential() { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), unableToObtainServerCredential$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechNotChosenRealm$str() { + return "ELY05174: Callback handler has not chosen realm"; + } + @Override + public final AuthenticationMechanismException mechNotChosenRealm() { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechNotChosenRealm$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechUnableToDetermineBoundServerName$str() { + return "ELY05175: Unable to determine bound server name"; + } + @Override + public final AuthenticationMechanismException mechUnableToDetermineBoundServerName(final Exception e) { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechUnableToDetermineBoundServerName$str()), e); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechCallbackHandlerUnsupportedCallback$str() { + return "ELY05176: Unsupported callback"; + } + @Override + public final AuthenticationMechanismException mechCallbackHandlerUnsupportedCallback(final Throwable cause) { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechCallbackHandlerUnsupportedCallback$str()), cause); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechOneOfDirectivesHasToBeDefined$str() { + return "ELY05177: One of \"%s\" and \"%s\" directives has to be defined"; + } + @Override + public final AuthenticationMechanismException mechOneOfDirectivesHasToBeDefined(final String directive1, final String directive2) { + final AuthenticationMechanismException result = new AuthenticationMechanismException(String.format(getLoggingLocale(), mechOneOfDirectivesHasToBeDefined$str(), directive1, directive2)); + _copyStackTraceMinusOne(result); + return result; + } + protected String incorrectlyFormattedHeader$str() { + return "ELY06001: An incorrectly formatted '%s'header was encountered."; + } + @Override + public final String incorrectlyFormattedHeader(final String headerName) { + return String.format(getLoggingLocale(), incorrectlyFormattedHeader$str(), headerName); + } + protected String authenticationFailed2$str() { + return "ELY06002: An authentication attempt for user '%s' failed validation using mechanism '%s'."; + } + @Override + public final String authenticationFailed(final String username, final String mechanismName) { + return String.format(getLoggingLocale(), authenticationFailed2$str(), username, mechanismName); + } + protected String authenticationFailed0$str() { + return "ELY06003: An authentication attempt failed validation."; + } + @Override + public final String authenticationFailed() { + return String.format(getLoggingLocale(), authenticationFailed0$str()); + } + protected String authorizationFailed$str() { + return "ELY06006: An authorization check for user '%s' failed."; + } + @Override + public final String authorizationFailed(final String username) { + return String.format(getLoggingLocale(), authorizationFailed$str(), username); + } + protected String usernameOrPasswordMissing$str() { + return "ELY06007: Username or password missing from authentication attempt."; + } + @Override + public final String usernameOrPasswordMissing() { + return String.format(getLoggingLocale(), usernameOrPasswordMissing$str()); + } + protected String digestMechanismRequireRealm$str() { + return "ELY06015: Unable to authenticate using DIGEST mechanism - realm name needs to be specified"; + } + @Override + public final HttpAuthenticationException digestMechanismRequireRealm() { + final HttpAuthenticationException result = new HttpAuthenticationException(String.format(getLoggingLocale(), digestMechanismRequireRealm$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String digestMechanismInvalidRealm$str() { + return "ELY06019: Unable to authenticate using DIGEST mechanism - mechanism realm name (%s) is not valid"; + } + @Override + public final HttpAuthenticationException digestMechanismInvalidRealm(final String realm) { + final HttpAuthenticationException result = new HttpAuthenticationException(String.format(getLoggingLocale(), digestMechanismInvalidRealm$str(), realm)); + _copyStackTraceMinusOne(result); + return result; + } + protected String unsuitableScope$str() { + return "ELY06020: Scope unsuitable for use with authentication state '%s'"; + } + @Override + public final IllegalArgumentException unsuitableScope(final String scopeName) { + final IllegalArgumentException result = new IllegalArgumentException(String.format(getLoggingLocale(), unsuitableScope$str(), scopeName)); + _copyStackTraceMinusOne(result); + return result; + } + protected String unableToIdentifyHttpScope$str() { + return "ELY06021: Unable to identify suitable HttpScope for mechanism state storage"; + } + @Override + public final IllegalArgumentException unableToIdentifyHttpScope() { + final IllegalArgumentException result = new IllegalArgumentException(String.format(getLoggingLocale(), unableToIdentifyHttpScope$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String invalidNonceCount$str() { + return "ELY06022: Invalid nonce count %s"; + } + @Override + public final HttpAuthenticationException invalidNonceCount(final int nonceCount) { + final HttpAuthenticationException result = new HttpAuthenticationException(String.format(getLoggingLocale(), invalidNonceCount$str(), nonceCount)); + _copyStackTraceMinusOne(result); + return result; + } + protected String asnUnrecognisedAlgorithm$str() { + return "ELY07001: Unrecognized encoding algorithm [%s]"; + } + @Override + public final ASN1Exception asnUnrecognisedAlgorithm(final String algorithm) { + final ASN1Exception result = new ASN1Exception(String.format(getLoggingLocale(), asnUnrecognisedAlgorithm$str(), algorithm)); + _copyStackTraceMinusOne(result); + return result; + } + protected String asnInvalidGeneralNameType$str() { + return "ELY07002: Invalid general name type"; + } + @Override + public final ASN1Exception asnInvalidGeneralNameType() { + final ASN1Exception result = new ASN1Exception(String.format(getLoggingLocale(), asnInvalidGeneralNameType$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String asnUnexpectedTag$str() { + return "ELY07004: Unexpected ASN.1 tag encountered"; + } + @Override + public final ASN1Exception asnUnexpectedTag() { + final ASN1Exception result = new ASN1Exception(String.format(getLoggingLocale(), asnUnexpectedTag$str())); + _copyStackTraceMinusOne(result); + return result; + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/mechanism/_private/MechanismUtil.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/mechanism/_private/MechanismUtil.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/mechanism/_private/MechanismUtil.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,192 @@ +/* + * 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 + */ + 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}) + * @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; + } + } + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/mechanism/http/ElytronMessages.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/mechanism/http/ElytronMessages.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/mechanism/http/ElytronMessages.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,35 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2014 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.http; + +import org.jboss.logging.BasicLogger; +import org.jboss.logging.Logger; +import org.jboss.logging.annotations.MessageLogger; + +/** + * Log messages and exceptions for Elytron. + * + * @author David M. Lloyd + * @author Darran Lofthouse + */ +@MessageLogger(projectCode = "ELY", length = 5) +interface ElytronMessages extends BasicLogger { + + ElytronMessages httpUserPass = Logger.getMessageLogger(ElytronMessages.class, "org.wildfly.security.http.password"); +} Index: 3rdParty_sources/elytron/org/wildfly/security/mechanism/http/ElytronMessages_$logger.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/mechanism/http/ElytronMessages_$logger.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/mechanism/http/ElytronMessages_$logger.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,24 @@ +package org.wildfly.security.mechanism.http; + +import java.util.Locale; +import java.io.Serializable; +import javax.annotation.Generated; +import org.jboss.logging.DelegatingBasicLogger; +import org.jboss.logging.BasicLogger; +import org.jboss.logging.Logger; + +/** + * Warning this class consists of generated code. + */ +@Generated(value = "org.jboss.logging.processor.generator.model.MessageLoggerImplementor", date = "2024-01-15T21:49:32+0100") +public class ElytronMessages_$logger extends DelegatingBasicLogger implements ElytronMessages, BasicLogger, Serializable { + private static final long serialVersionUID = 1L; + private static final String FQCN = ElytronMessages_$logger.class.getName(); + public ElytronMessages_$logger(final Logger log) { + super(log); + } + private static final Locale LOCALE = Locale.ROOT; + protected Locale getLoggingLocale() { + return LOCALE; + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/mechanism/http/UsernamePasswordAuthenticationMechanism.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/mechanism/http/UsernamePasswordAuthenticationMechanism.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/mechanism/http/UsernamePasswordAuthenticationMechanism.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,122 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2016 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.http; + +import java.io.IOException; + +import javax.security.auth.DestroyFailedException; +import javax.security.auth.callback.Callback; +import javax.security.auth.callback.CallbackHandler; +import javax.security.auth.callback.NameCallback; +import javax.security.auth.callback.UnsupportedCallbackException; +import javax.security.sasl.AuthorizeCallback; +import javax.security.sasl.RealmCallback; + +import org.wildfly.security.auth.callback.AuthenticationCompleteCallback; +import org.wildfly.security.auth.callback.EvidenceVerifyCallback; +import org.wildfly.security.auth.callback.IdentityCredentialCallback; +import org.wildfly.security.credential.PasswordCredential; +import org.wildfly.security.evidence.PasswordGuessEvidence; +import org.wildfly.security.http.HttpAuthenticationException; +import org.wildfly.security.http.HttpServerAuthenticationMechanism; +import org.wildfly.security.password.interfaces.ClearPassword; + +import static org.wildfly.security.mechanism.http.ElytronMessages.httpUserPass; + +/** + * A base class for HTTP mechanisms that operate on validation of plain text usernames and passwords. + * + * @author Darran Lofthouse + */ +public abstract class UsernamePasswordAuthenticationMechanism implements HttpServerAuthenticationMechanism { + + protected final CallbackHandler callbackHandler; + + /** + * @param callbackHandler + */ + protected UsernamePasswordAuthenticationMechanism(CallbackHandler callbackHandler) { + super(); + this.callbackHandler = callbackHandler; + } + + protected boolean authenticate(String realmName, String username, char[] password) throws HttpAuthenticationException { + RealmCallback realmCallback = realmName != null ? new RealmCallback("User realm", realmName) : null; + NameCallback nameCallback = new NameCallback("Remote Authentication Name", username); + nameCallback.setName(username); + final PasswordGuessEvidence evidence = new PasswordGuessEvidence(password); + final ClearPassword clearPwd = ClearPassword.createRaw(ClearPassword.ALGORITHM_CLEAR, password); + EvidenceVerifyCallback evidenceVerifyCallback = new EvidenceVerifyCallback(evidence); + + httpUserPass.debugf("Username authentication. Realm: [%s], Username: [%s].", + realmName, username); + + try { + final Callback[] callbacks; + if (realmCallback != null) { + callbacks = new Callback[] { realmCallback, nameCallback, evidenceVerifyCallback }; + } else { + callbacks = new Callback[] { nameCallback, evidenceVerifyCallback }; + } + + callbackHandler.handle(callbacks); + + if(evidenceVerifyCallback.isVerified()) { + IdentityCredentialCallback credentialUpdateCallback = new IdentityCredentialCallback(new PasswordCredential(clearPwd), true); + callbackHandler.handle(new Callback[]{credentialUpdateCallback}); + return true; + } else { + clearPwd.destroy(); + return false; + } + } catch (UnsupportedCallbackException|DestroyFailedException e) { + return false; + } catch (IOException e) { + throw new HttpAuthenticationException(e); + } finally { + evidence.destroy(); + } + } + + protected boolean authorize(String username) throws HttpAuthenticationException { + httpUserPass.debugf("Username authorization. Username: [%s].", + username); + + AuthorizeCallback authorizeCallback = new AuthorizeCallback(username, username); + + try { + callbackHandler.handle(new Callback[] {authorizeCallback}); + + return authorizeCallback.isAuthorized(); + } catch (UnsupportedCallbackException e) { + return false; + } catch (IOException e) { + throw new HttpAuthenticationException(e); + } + } + + protected void succeed() throws IOException, UnsupportedCallbackException { + callbackHandler.handle(new Callback[] { AuthenticationCompleteCallback.SUCCEEDED }); + } + + protected void fail() throws IOException, UnsupportedCallbackException { + callbackHandler.handle(new Callback[] { AuthenticationCompleteCallback.FAILED }); + } + +} Index: 3rdParty_sources/elytron/org/wildfly/security/package-info.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/package-info.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/package-info.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,22 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2017 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. + */ + +/** + * WildFly security base package. + */ +package org.wildfly.security; Index: 3rdParty_sources/elytron/org/wildfly/security/password/impl/AbstractPasswordImpl.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/password/impl/AbstractPasswordImpl.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/password/impl/AbstractPasswordImpl.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,76 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2014 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.password.impl; + +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.KeySpec; +import java.text.Normalizer; + +import org.wildfly.security.password.Password; + +/** + * @author David M. Lloyd + */ +abstract class AbstractPasswordImpl implements Password { + + private static final long serialVersionUID = -7527865607883174548L; + + abstract S getKeySpec(final Class keySpecType) throws InvalidKeySpecException; + + abstract boolean verify(final char[] guess) throws InvalidKeyException; + + boolean verify(final char[] guess, Charset hashCharset) throws InvalidKeyException { + throw new UnsupportedOperationException(); + } + + abstract boolean convertibleTo(final Class keySpecType); + + public abstract AbstractPasswordImpl clone(); + + Password translate(final AlgorithmParameterSpec parameterSpec) throws InvalidKeyException, InvalidAlgorithmParameterException { + throw new InvalidAlgorithmParameterException(); + } + + static byte[] getNormalizedPasswordBytes(final char[] characters) { + return getNormalizedPasswordBytes(characters, StandardCharsets.UTF_8); + } + + static byte[] getNormalizedPasswordBytes(final char[] characters, Charset charset) { + return Normalizer.normalize(new String(characters), Normalizer.Form.NFKC).getBytes(charset); + } + + @Override + public String getFormat() { + return null; + } + + @Override + public byte[] getEncoded() { + return null; + } + + public abstract int hashCode(); + + public abstract boolean equals(final Object obj); +} Index: 3rdParty_sources/elytron/org/wildfly/security/password/impl/BCryptPasswordImpl.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/password/impl/BCryptPasswordImpl.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/password/impl/BCryptPasswordImpl.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,631 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2014 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.password.impl; + +import static org.wildfly.common.math.HashMath.multiHashOrdered; +import static org.wildfly.security.password.impl.ElytronMessages.log; + +import java.io.NotSerializableException; +import java.io.ObjectInputStream; +import java.io.Serializable; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.security.InvalidKeyException; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.KeySpec; +import java.util.Arrays; + +import org.wildfly.common.Assert; +import org.wildfly.security.password.spec.IteratedPasswordAlgorithmSpec; +import org.wildfly.security.password.spec.SaltedPasswordAlgorithmSpec; +import org.wildfly.security.password.util.ModularCrypt; +import org.wildfly.security.password.interfaces.BCryptPassword; +import org.wildfly.security.password.spec.ClearPasswordSpec; +import org.wildfly.security.password.spec.IteratedSaltedPasswordAlgorithmSpec; +import org.wildfly.security.password.spec.IteratedSaltedHashPasswordSpec; +import org.wildfly.security.password.spec.SaltedHashPasswordSpec; + +/** + *

+ * Implementation of the bcrypt password. + *

+ * + * @author Stefan Guilhen + */ +class BCryptPasswordImpl extends AbstractPasswordImpl implements BCryptPassword { + + private static final long serialVersionUID = - 4400030650839115036L; + + private final byte[] hash; + private final byte[] salt; + private final int iterationCount; + + BCryptPasswordImpl(final byte[] hash, final byte[] salt, final int iterationCount) { + this.hash = hash; + this.salt = salt; + this.iterationCount = iterationCount; + } + + BCryptPasswordImpl(final BCryptPassword bCryptPassword) { + this(bCryptPassword.getHash().clone(), bCryptPassword.getSalt().clone(), bCryptPassword.getIterationCount()); + } + + BCryptPasswordImpl(final IteratedSaltedHashPasswordSpec passwordSpec) { + this(passwordSpec.getHash().clone(), passwordSpec.getSalt().clone(), passwordSpec.getIterationCount()); + } + + BCryptPasswordImpl(final SaltedHashPasswordSpec passwordSpec) { + this(passwordSpec.getHash().clone(), passwordSpec.getSalt().clone(), DEFAULT_ITERATION_COUNT); + } + + BCryptPasswordImpl(final ClearPasswordSpec clearPasswordSpec) { + this.salt = PasswordUtil.generateRandomSalt(BCRYPT_SALT_SIZE); + this.iterationCount = DEFAULT_ITERATION_COUNT; + this.hash = bcrypt(this.iterationCount, this.salt, getNormalizedPasswordBytes(clearPasswordSpec.getEncodedPassword())); + } + + BCryptPasswordImpl(final char[] password, final Charset hashCharset) throws InvalidKeySpecException { + this.salt = PasswordUtil.generateRandomSalt(BCRYPT_SALT_SIZE); + this.iterationCount = DEFAULT_ITERATION_COUNT; + this.hash = bcrypt(this.iterationCount, this.salt, getNormalizedPasswordBytes(password, hashCharset)); + } + + BCryptPasswordImpl(final char[] password, final IteratedSaltedPasswordAlgorithmSpec spec, final Charset hashCharset) throws InvalidKeySpecException { + this.salt = spec.getSalt().clone(); + this.iterationCount = spec.getIterationCount(); + this.hash = bcrypt(this.iterationCount, this.salt, getNormalizedPasswordBytes(password, hashCharset)); + } + + BCryptPasswordImpl(final char[] password, final SaltedPasswordAlgorithmSpec spec, final Charset hashCharset) throws InvalidKeySpecException { + this.salt = spec.getSalt().clone(); + this.iterationCount = DEFAULT_ITERATION_COUNT; + this.hash = bcrypt(this.iterationCount, this.salt, getNormalizedPasswordBytes(password, hashCharset)); + } + + BCryptPasswordImpl(final char[] password, final IteratedPasswordAlgorithmSpec spec, final Charset hashCharset) throws InvalidKeySpecException { + this.salt = PasswordUtil.generateRandomSalt(BCRYPT_SALT_SIZE); + this.iterationCount = spec.getIterationCount(); + this.hash = bcrypt(this.iterationCount, this.salt, getNormalizedPasswordBytes(password, hashCharset)); + } + + @Override + public String getAlgorithm() { + return ALGORITHM_BCRYPT; + } + + @Override + public byte[] getHash() { + try { + return this.hash.clone(); + } catch (NullPointerException npe) { + throw new IllegalStateException(); + } + } + + @Override + public byte[] getSalt() { + try { + return this.salt.clone(); + } catch (NullPointerException npe) { + throw new IllegalStateException(); + } + } + + @Override + public int getIterationCount() { + return this.iterationCount; + } + + @Override + S getKeySpec(Class keySpecType) throws InvalidKeySpecException { + if (keySpecType.isAssignableFrom(IteratedSaltedHashPasswordSpec.class)) { + return keySpecType.cast(new IteratedSaltedHashPasswordSpec(this.getHash(), this.getSalt(), this.getIterationCount())); + } + throw new InvalidKeySpecException(); + } + + @Override + boolean verify(char[] guess) throws InvalidKeyException { + return verify(guess, StandardCharsets.UTF_8); + } + + @Override + boolean verify(char[] guess, Charset hashCharset) throws InvalidKeyException { + byte[] output = bcrypt(this.iterationCount, this.getSalt(), getNormalizedPasswordBytes(guess, hashCharset)); + return Arrays.equals(this.hash, output); + } + + @Override + boolean convertibleTo(Class keySpecType) { + return keySpecType.isAssignableFrom(IteratedSaltedHashPasswordSpec.class); + } + + private static final int[] Parray = { + 0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, 0xa4093822, 0x299f31d0, 0x082efa98, 0xec4e6c89, + 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c, 0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, + 0x9216d5d9, 0x8979fb1b + }; + + private static final int[] Sboxes = { + 0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, 0xb8e1afed, 0x6a267e96, 0xba7c9045, 0xf12c7f99, + 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16, 0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e, + 0x0d95748f, 0x728eb658, 0x718bcd58, 0x82154aee, 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013, + 0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, 0x8e79dcb0, 0x603a180e, 0x6c9e0e8b, 0xb01e8a3e, + 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60, 0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440, + 0x55ca396a, 0x2aab10b6, 0xb4cc5c34, 0x1141e8ce, 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a, + 0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, 0xafd6ba33, 0x6c24cf5c, 0x7a325381, 0x28958677, + 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193, 0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032, + 0xef845d5d, 0xe98575b1, 0xdc262302, 0xeb651b88, 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239, + 0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, 0x21c66842, 0xf6e96c9a, 0x670c9c61, 0xabd388f0, + 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3, 0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98, + 0xa1f1651d, 0x39af0176, 0x66ca593e, 0x82430e88, 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe, + 0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, 0x4ed3aa62, 0x363f7706, 0x1bfedf72, 0x429b023d, + 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b, 0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7, + 0xe3fe501a, 0xb6794c3b, 0x976ce0bd, 0x04c006ba, 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463, + 0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, 0x6dfc511f, 0x9b30952c, 0xcc814544, 0xaf5ebd09, + 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3, 0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb, + 0x5579c0bd, 0x1a60320a, 0xd6a100c6, 0x402c7279, 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8, + 0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, 0x323db5fa, 0xfd238760, 0x53317b48, 0x3e00df82, + 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db, 0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573, + 0x695b27b0, 0xbbca58c8, 0xe1ffa35d, 0xb8f011a0, 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b, + 0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, 0xe1ddf2da, 0xa4cb7e33, 0x62fb1341, 0xcee4c6e8, + 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4, 0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0, + 0xd08ed1d0, 0xafc725e0, 0x8e3c5b2f, 0x8e7594b7, 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c, + 0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad, 0x2f2f2218, 0xbe0e1777, 0xea752dfe, 0x8b021fa1, + 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299, 0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9, + 0x165fa266, 0x80957705, 0x93cc7314, 0x211a1477, 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf, + 0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, 0x00250e2d, 0x2071b35e, 0x226800bb, 0x57b8e0af, + 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa, 0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5, + 0x83260376, 0x6295cfa9, 0x11c81968, 0x4e734a41, 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915, + 0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, 0x08ba6fb5, 0x571be91f, 0xf296ec6b, 0x2a0dd915, + 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664, 0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a, + 0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, 0xad6ea6b0, 0x49a7df7d, 0x9cee60b8, 0x8fedb266, + 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1, 0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e, + 0x3f54989a, 0x5b429d65, 0x6b8fe4d6, 0x99f73fd6, 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1, + 0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, 0x09686b3f, 0x3ebaefc9, 0x3c971814, 0x6b6a70a1, + 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737, 0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8, + 0xb03ada37, 0xf0500c0d, 0xf01c1f04, 0x0200b3ff, 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd, + 0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, 0x3ae5e581, 0x37c2dadc, 0xc8b57634, 0x9af3dda7, + 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41, 0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331, + 0x4e548b38, 0x4f6db908, 0x6f420d03, 0xf60a04bf, 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af, + 0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e, 0x5512721f, 0x2e6b7124, 0x501adde6, 0x9f84cd87, + 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c, 0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2, + 0xef1c1847, 0x3215d908, 0xdd433b37, 0x24c2ba16, 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd, + 0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b, 0x043556f1, 0xd7a3c76b, 0x3c11183b, 0x5924a509, + 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e, 0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3, + 0x771fe71c, 0x4e3d06fa, 0x2965dcb9, 0x99e71d0f, 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a, + 0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, 0xf2f74ea7, 0x361d2b3d, 0x1939260f, 0x19c27960, + 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66, 0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28, + 0xc332ddef, 0xbe6c5aa5, 0x65582185, 0x68ab9802, 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84, + 0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, 0x13cca830, 0xeb61bd96, 0x0334fe1e, 0xaa0363cf, + 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14, 0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e, + 0x648b1eaf, 0x19bdf0ca, 0xa02369b9, 0x655abb50, 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7, + 0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, 0xf837889a, 0x97e32d77, 0x11ed935f, 0x16681281, + 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99, 0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696, + 0xcdb30aeb, 0x532e3054, 0x8fd948e4, 0x6dbc3128, 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73, + 0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, 0x45eee2b6, 0xa3aaabea, 0xdb6c4f15, 0xfacb4fd0, + 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105, 0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250, + 0xcf62a1f2, 0x5b8d2646, 0xfc8883a0, 0xc1c7b6a3, 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285, + 0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00, 0x58428d2a, 0x0c55f5ea, 0x1dadf43e, 0x233f7061, + 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb, 0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e, + 0xa6078084, 0x19f8509e, 0xe8efd855, 0x61d99735, 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc, + 0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, 0xdb73dbd3, 0x105588cd, 0x675fda79, 0xe3674340, + 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20, 0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7, + 0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, 0x411520f7, 0x7602d4f7, 0xbcf46b2e, 0xd4a20068, + 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af, 0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840, + 0x4d95fc1d, 0x96b591af, 0x70f4ddd3, 0x66a02f45, 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504, + 0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a, 0x28507825, 0x530429f4, 0x0a2c86da, 0xe9b66dfb, + 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee, 0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6, + 0xaace1e7c, 0xd3375fec, 0xce78a399, 0x406b2a42, 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b, + 0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, 0x3a6efa74, 0xdd5b4332, 0x6841e7f7, 0xca7820fb, + 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527, 0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b, + 0x55a867bc, 0xa1159a58, 0xcca92963, 0x99e1db33, 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c, + 0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3, 0x95c11548, 0xe4c66d22, 0x48c1133f, 0xc70f86dc, + 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17, 0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564, + 0x257b7834, 0x602a9c60, 0xdff8e8a3, 0x1f636c1b, 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115, + 0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922, 0x85b2a20e, 0xe6ba0d99, 0xde720c8c, 0x2da2f728, + 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0, 0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e, + 0x0a476341, 0x992eff74, 0x3a6f6eab, 0xf4f8fd37, 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d, + 0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804, 0xf1290dc7, 0xcc00ffa3, 0xb5390f92, 0x690fed0b, + 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3, 0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb, + 0x37392eb3, 0xcc115979, 0x8026e297, 0xf42e312d, 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c, + 0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350, 0x1a6b1018, 0x11caedfa, 0x3d25bdd8, 0xe2e1c3c9, + 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a, 0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe, + 0x9dbc8057, 0xf0f7c086, 0x60787bf8, 0x6003604d, 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc, + 0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, 0x77a057be, 0xbde8ae24, 0x55464299, 0xbf582e61, + 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2, 0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9, + 0x7aeb2661, 0x8b1ddf84, 0x846a0e79, 0x915f95e2, 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c, + 0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e, 0xb77f19b6, 0xe0a9dc09, 0x662d09a1, 0xc4324633, + 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10, 0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169, + 0xdcb7da83, 0x573906fe, 0xa1e2ce9b, 0x4fcd7f52, 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027, + 0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, 0xf0177a28, 0xc0f586e0, 0x006058aa, 0x30dc7d62, + 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634, 0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76, + 0x6f05e409, 0x4b7c0188, 0x39720a3d, 0x7c927c24, 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc, + 0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, 0x1e50ef5e, 0xb161e6f8, 0xa28514d9, 0x6c51133c, + 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837, 0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0, + 0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, 0x5cb0679e, 0x4fa33742, 0xd3822740, 0x99bc9bbe, + 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b, 0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4, + 0x5748ab2f, 0xbc946e79, 0xc6a376d2, 0x6549c2c8, 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6, + 0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, 0xa1fad5f0, 0x6a2d519a, 0x63ef8ce2, 0x9a86ee22, + 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4, 0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6, + 0x2826a2f9, 0xa73a3ae1, 0x4ba99586, 0xef5562e9, 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59, + 0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, 0xe990fd5a, 0x9e34d797, 0x2cf0b7d9, 0x022b8b51, + 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28, 0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c, + 0xe029ac71, 0xe019a5e6, 0x47b0acfd, 0xed93fa9b, 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28, + 0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, 0x15056dd4, 0x88f46dba, 0x03a16125, 0x0564f0bd, + 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a, 0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319, + 0x7533d928, 0xb155fdf5, 0x03563482, 0x8aba3cbb, 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f, + 0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991, 0xea7a90c2, 0xfb3e7bce, 0x5121ce64, 0x774fbe32, + 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680, 0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166, + 0xb39a460a, 0x6445c0dd, 0x586cdecf, 0x1c20c8ae, 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb, + 0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, 0x72eacea8, 0xfa6484bb, 0x8d6612ae, 0xbf3c6f47, + 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370, 0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d, + 0x4040cb08, 0x4eb4e2cc, 0x34d2466a, 0x0115af84, 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048, + 0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8, 0x611560b1, 0xe7933fdc, 0xbb3a792b, 0x344525bd, + 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9, 0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7, + 0x1a908749, 0xd44fbd9a, 0xd0dadecb, 0xd50ada38, 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f, + 0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, 0xbf97222c, 0x15e6fc2a, 0x0f91fc71, 0x9b941525, + 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1, 0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442, + 0xe0ec6e0e, 0x1698db3b, 0x4c98a0be, 0x3278e964, 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e, + 0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, 0xdf359f8d, 0x9b992f2e, 0xe60b6f47, 0x0fe3f11d, + 0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f, 0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299, + 0xf523f357, 0xa6327623, 0x93a83531, 0x56cccd02, 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc, + 0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614, 0xe6c6c7bd, 0x327a140a, 0x45e1d006, 0xc3f27b9a, + 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6, 0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b, + 0x53113ec0, 0x1640e3d3, 0x38abbd60, 0x2547adf0, 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060, + 0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, 0x1948c25c, 0x02fb8a8c, 0x01c36ae4, 0xd6ebe1f9, + 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f, 0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6 + }; + + // 192-bit value "OrpheanBeholderScryDoubt" as int[] + private static final int[] orpheanBeholderScryDoubt = { + 0x4f727068, 0x65616e42, 0x65686f6c, 0x64657253, 0x63727944, 0x6f756274 + }; + + // 128-bit salt that has all bits set to zero (used in the enhanced key schedule). + private static final byte[] zeroedSalt = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 + }; + + /** + *

+ * Hashes the password with the given salt and cost using the bcrypt algorithm. The algorithm is defined as follows: + * + *

+     *     bcrypt(cost, salt, pwd)
+     *         state <- eksBlowfishSetup(cost, salt, pwd)
+     *         ctext <- "OrpheanBeholderScryDoubt"
+     *         repeat(64)
+     *             ctext <- EncryptECB(state, ctext)
+     *         return Concatenate(cost, salt, ctext)
+     * 
+ *

+ *

+ * This implementation differs from the above algorithm in that it returns the encrypted ctext in its raw form. The + * {@link ModularCrypt} class can be used to obtain the hashed password in its crypt + * string format (i.e. $2a$cost$encodedSaltencodedPassword). + *

+ *

+ * It is important to note that bcrypt has a limitation when it comes to the password size: only the first 72 bytes + * of the password are involved in the key schedule phase, so large keys that share the same first 72 bytes will produce + * the same hash. + *

+ * + * @param cost the cost of the bcrypt algorithm. It represents the log value of the number of rounds to be applied. Thus, + * a cost value of 6 means that a total of 2^6 = 64 rounds will be applied, while a cost value of 20 means + * that a total of 2^20 = 1.048.576 rounds will be applied. It must be a value between 4 and 31 (inclusive). + * @param salt the 128-bit salt to be used. It is represented by a 16-byte array. + * @param password the password to be hashed, in its byte array form. + * + * @return a byte[] containing the hashed password. + */ + static byte[] bcrypt(final int cost, final byte[] salt, final byte[] password) { + + Assert.checkNotNullParam("salt", salt); + Assert.checkNotNullParam("password", password); + + if (cost < 4 || cost > 31) + throw log.invalidNumberOfRoundsMustBeIntBetween(4, 31); + if (salt.length != BCRYPT_SALT_SIZE) + throw log.invalidSaltMustBeBytesLong(BCRYPT_SALT_SIZE); + + // check if null has been appended to the password. If not, add a null byte for compatibility with C implementations + byte[] key = password.clone(); + if (key.length == 0 || key[key.length - 1] != 0) { + byte[] paddedKey = new byte[key.length + 1]; + System.arraycopy(password, 0, paddedKey, 0, key.length); + paddedKey[key.length] = 0; + key = paddedKey; + } + + BCryptState state = eksBlowfishSetup(cost, salt, key); + int[] ctext = orpheanBeholderScryDoubt.clone(); + for (int i = 0; i < 64; i++) { + encrypt(state, ctext); + } + + // convert the ctext array to a byte array + byte[] result = new byte[4 * ctext.length - 1]; + for (int i = 0, j = 0; i < ctext.length; i++) { + result[j++] = (byte) ((ctext[i] >> 24) & 0xff); + result[j++] = (byte) ((ctext[i] >> 16) & 0xff); + result[j++] = (byte) ((ctext[i] >> 8) & 0xff); + // ignore the last byte for compatibility with C implementations + if (i != ctext.length - 1) + result[j++] = (byte) (ctext[i] & 0xff); + } + + // clear the bcrypt state and the encrypted orpheanBeholderScryDoubt string + Arrays.fill(ctext, 0); + state.clear(); + + return result; + } + + /** + *

+ * Performs the Blowfish expensive key schedule setup. This phase is defined as follows: + * + *

+     *     eksBlowfishSetup(cost, salt, key)
+     *         state <- InitState();
+     *         state <- expandKey(state, salt, key)
+     *         repeat(2^cost)
+     *             state <- expandKey(state, 0, key)
+     *             state <- expandKey(state, 0, salt)
+     *         return state
+     * 
+ *

+ * + * @param cost the cost of the bcrypt algorithm. It represents the log value of the number of rounds to be applied. Thus, + * a cost value of 6 means that a total of 2^6 = 64 rounds will be applied, while a cost value of 20 means + * that a total of 2^20 = 1.048.576 rounds will be applied. It must be a value between 4 and 31 (inclusive). + * @param salt the 128-bit salt to be used. It is represented by a 16-byte array. + * @param key the password being hashed, in its byte array form. + */ + private static BCryptState eksBlowfishSetup(final int cost, final byte[] salt, final byte[] key) { + // init the state + BCryptState state = new BCryptState(Parray.clone(), Sboxes.clone()); + // expand key using both salt and key + expandKey(state, salt, key); + // repeat (2^cost) rounds + final int rounds = 1 << cost; + for (int i = 0; i < rounds; i++) { + expandKey(state, zeroedSalt, key); + expandKey(state, zeroedSalt, salt); + } + return state; + } + + /** + *

+ * Performs the expandKey step in the expensive key schedule setup. This step is defined as follows: + * + *

+     *     expandKey(state, salt, key)
+     *         for(n = 1..18)
+     *             P[n] <- key[32(n-1)..32n-1] ^ P[n] // treat key as cyclic.
+     *         ctext <- encrypt(salt[0..63])
+     *         P[0] <- ctext[0..31]
+     *         P[1] <- ctext[32..63]
+     *         for(n = 1..8)
+     *             ctext <- encrypt(ctext ^ salt[64(n-1)..64n-1]) // encrypt using the current key schedule and treat the salt as cyclic.
+     *             P[2n] <- ctext[0..31]
+     *             P[2n+1] <- ctext[32..63]
+     *         for(i = 1..4]
+     *             for(n = 0..127]
+     *                 ctext <- encrypt(ctext ^ salt[64(n-1)..64n-1]) // as above
+     *                 Si[2n] <- ctext[0..31]
+     *                 Si[2n+1] <- ctext[32..63]
+     *         return state;
+     * 
+ *

+ * + * @param state the current bcrypt state (P-array and S-boxes). + * @param salt the 128-bit salt to be used. It is represented by a 16-byte array. + * @param key the password being hashed, in its byte array form. + */ + private static void expandKey(final BCryptState state, final byte[] salt, final byte[] key) { + CyclicByteBuffer keyBuffer = new CyclicByteBuffer(key); + CyclicByteBuffer saltBuffer = new CyclicByteBuffer(salt); + int[] P = state.getPArray(); + int[] S = state.getSBoxes(); + + for (int i = 0; i < P.length; i++) { + P[i] = P[i] ^ keyBuffer.readNextInt(); + } + int[] ctext = {0, 0}; + for (int i = 0; i < P.length; i += 2) { + ctext[0] ^= saltBuffer.readNextInt(); + ctext[1] ^= saltBuffer.readNextInt(); + encrypt(state, ctext); + P[i] = ctext[0]; + P[i+1] = ctext[1]; + } + for (int i = 0; i < S.length; i += 2) { + ctext[0] ^= saltBuffer.readNextInt(); + ctext[1] ^= saltBuffer.readNextInt(); + encrypt(state, ctext); + S[i] = ctext[0]; + S[i+1] = ctext[1]; + } + + } + + /** + *

+ * Encrypt splits 64-bit blocks into two 32-bit halves, left and right. The most significant half (left) is xored + * with subkey P[0] and used as input for function F (Feistel substitution). The result of that function is xored + * with the least significant half (right). The halves are then swapped and the process repeats for a total of 16 times: + * + *

+     *     L0 <- block[0..31]
+     *     R0 <- block[32..63]
+     *
+     *     for(i = 1..16)
+     *         Ri <- Li-1 ^ P[i-1]
+     *         Li <- Ri-1 ^ F(Ri)
+     * 
+ *

+ *

+ * After 16 rounds the halves are swapped again and xored with the final two subkeys: + * + *

+     *     R17 <- L16 ^ P[16]
+     *     L17 <- R16 ^ P[17]
+     * 
+ *

+ *

+ * This method takes the 64-bit blocks as pairs of integers (32-bits each) in the value parameter and encrypts each + * pair separately. Encryption happens in place - i.e. the encrypted values are written back to the incoming array, + * replacing the previous values. + *

+ * + * @param state the current bcrypt state (P-array and S-boxes). + * @param value a int[] containing the 32-bit pairs that must be encrypted. + */ + private static void encrypt(final BCryptState state, int[] value) { + + int[] P = state.getPArray(); + int[] S = state.getSBoxes(); + + for (int i = 0; i < value.length; i += 2) { + int left = value[i]; + int right = value[i+1]; + int previousRight = 0; + + for (int j = 1; j <= 16; j++) { + previousRight = right; + right = left ^ P[j-1]; + // F(Ri) - Feistel substitution + int n = S[(right >> 24) & 0xff]; + n += S[0x100 | ((right >> 16) & 0xff)]; + n ^= S[0x200 | ((right >> 8) & 0xff)]; + n += S[0x300 | (right & 0xff)]; + left = previousRight ^ n; + } + // last round: left side is xored directly with the final subkey (P[17]) + previousRight = right; + right = left ^ P[16]; + left = previousRight ^ P[17]; + + // set the encrypted values in the incoming array + value[i] = left; + value[i+1] = right; + } + } + + /** + *

+ * Class that represents the bcrypt state. + *

+ */ + private static class BCryptState implements Serializable { + + private int[] Parray; + private int[] Sboxes; + + BCryptState(int[] Parray, int[] Sboxes) { + this.Parray = Parray; + this.Sboxes = Sboxes; + } + + int[] getPArray() { + return this.Parray; + } + + int[] getSBoxes() { + return this.Sboxes; + } + + void clear() { + Arrays.fill(Parray, 0); + Arrays.fill(Sboxes, 0); + } + } + + /** + *

+ * This class implements a cyclic byte buffer. It defines utility methods to retrieve the next byte or int from the + * underlying byte[] in a cyclic manner. + *

+ */ + private static class CyclicByteBuffer implements Serializable { + + private byte[] buffer; + private int position; + + CyclicByteBuffer(byte[] buffer) { + Assert.checkNotNullParam("buffer", buffer); + if (buffer.length == 0) throw log.emptyParameter("buffer"); + this.buffer = buffer; + this.position = 0; + } + + /** + * Reads the next byte from the buffer. The current position is incremented by one (mod buffer.length). + * + * @return the byte that was read from the cyclic buffer. + */ + byte readNextByte() { + byte value = this.buffer[this.position]; + this.position = (this.position + 1) % this.buffer.length; + return value; + } + + /** + * Reads the next int (four bytes) from the buffer. The current position is incremented by four (mod buffer.length). + * + * @return the int that was read from the cyclic buffer. + */ + int readNextInt() { + int value = 0; + for (int i = 0; i < 4; i++) { + value = (value << 8) | (this.buffer[this.position] & 0xff); + this.position = (this.position + 1) % this.buffer.length; + } + return value; + } + } + + public int hashCode() { + return multiHashOrdered(multiHashOrdered(Arrays.hashCode(hash), Arrays.hashCode(salt)), iterationCount); + } + + public boolean equals(final Object obj) { + if (! (obj instanceof BCryptPasswordImpl)) { + return false; + } + BCryptPasswordImpl other = (BCryptPasswordImpl) obj; + return iterationCount == other.iterationCount && Arrays.equals(hash, other.hash) && Arrays.equals(salt, other.salt); + } + + private void readObject(ObjectInputStream ignored) throws NotSerializableException { + throw new NotSerializableException(); + } + + Object writeReplace() { + return BCryptPassword.createRaw(getAlgorithm(), hash, salt, iterationCount); + } + + public BCryptPasswordImpl clone() { + return this; + } + +} Index: 3rdParty_sources/elytron/org/wildfly/security/password/impl/BSDUnixDESCryptPasswordImpl.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/password/impl/BSDUnixDESCryptPasswordImpl.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/password/impl/BSDUnixDESCryptPasswordImpl.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,660 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2014 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.password.impl; + +import static org.wildfly.common.math.HashMath.multiHashOrdered; +import static org.wildfly.security.password.impl.ElytronMessages.log; + +import java.io.NotSerializableException; +import java.io.ObjectInputStream; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.security.InvalidKeyException; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.InvalidParameterSpecException; +import java.security.spec.KeySpec; +import java.util.Arrays; +import java.util.concurrent.ThreadLocalRandom; + +import org.wildfly.security.password.interfaces.BSDUnixDESCryptPassword; +import org.wildfly.security.password.spec.ClearPasswordSpec; +import org.wildfly.security.password.spec.IteratedPasswordAlgorithmSpec; +import org.wildfly.security.password.spec.IteratedSaltedPasswordAlgorithmSpec; +import org.wildfly.security.password.spec.IteratedSaltedHashPasswordSpec; +import org.wildfly.security.password.spec.SaltedHashPasswordSpec; +import org.wildfly.security.password.spec.SaltedPasswordAlgorithmSpec; + +/** + * Implementation of the BSD variant of the Unix DES Crypt password. + * + * @author Farah Juma + */ +class BSDUnixDESCryptPasswordImpl extends AbstractPasswordImpl implements BSDUnixDESCryptPassword { + + private static final long serialVersionUID = 4537505177089490619L; + private final int iterationCount; + private final int salt; + private final byte[] hash; + + BSDUnixDESCryptPasswordImpl(int salt, int iterationCount, byte[] hash) throws InvalidKeySpecException { + this.salt = salt; + this.iterationCount = iterationCount; + + if (hash == null || hash.length != BSDUnixDESCryptPassword.BSD_CRYPT_DES_HASH_SIZE) { + throw log.invalidKeySpecBsdDesCryptPasswordHashMustBeBytes(BSDUnixDESCryptPassword.BSD_CRYPT_DES_HASH_SIZE); + } + + this.hash = hash.clone(); + } + + BSDUnixDESCryptPasswordImpl(final IteratedSaltedHashPasswordSpec passwordSpec) throws InvalidKeySpecException, InvalidParameterSpecException { + this(getSaltValue(passwordSpec.getSalt()), passwordSpec.getIterationCount(), passwordSpec.getHash()); + } + + BSDUnixDESCryptPasswordImpl(final SaltedHashPasswordSpec passwordSpec) throws InvalidKeySpecException, InvalidParameterSpecException { + this(getSaltValue(passwordSpec.getSalt()), DEFAULT_ITERATION_COUNT, passwordSpec.getHash()); + } + + BSDUnixDESCryptPasswordImpl(final ClearPasswordSpec passwordSpec) throws InvalidKeySpecException { + this(passwordSpec.getEncodedPassword(), ThreadLocalRandom.current().nextInt() & 0xffffff, DEFAULT_ITERATION_COUNT); + } + + BSDUnixDESCryptPasswordImpl(final char[] password, final Charset hashCharset) throws InvalidKeySpecException, InvalidParameterSpecException { + this(password, ThreadLocalRandom.current().nextInt() & 0xffffff, DEFAULT_ITERATION_COUNT, hashCharset); + } + + BSDUnixDESCryptPasswordImpl(final char[] password, final IteratedSaltedPasswordAlgorithmSpec spec, final Charset hashCharset) throws InvalidKeySpecException, InvalidParameterSpecException { + this(password, getSaltValue(spec.getSalt()), spec.getIterationCount(), hashCharset); + } + + BSDUnixDESCryptPasswordImpl(final char[] password, final IteratedPasswordAlgorithmSpec spec, final Charset hashCharset) throws InvalidKeySpecException, InvalidParameterSpecException { + this(password, ThreadLocalRandom.current().nextInt() & 0xffffff, spec.getIterationCount(), hashCharset); + } + + BSDUnixDESCryptPasswordImpl(final char[] password, final SaltedPasswordAlgorithmSpec spec, final Charset hashCharset) throws InvalidKeySpecException, InvalidParameterSpecException { + this(password, getSaltValue(spec.getSalt()), DEFAULT_ITERATION_COUNT, hashCharset); + } + + BSDUnixDESCryptPasswordImpl(final char[] password, final int salt, final int iterationCount) throws InvalidKeySpecException { + this(salt, iterationCount, generateHash(salt, iterationCount, password)); + } + + BSDUnixDESCryptPasswordImpl(final char[] password, final int salt, final int iterationCount, final Charset hashCharset) throws InvalidKeySpecException { + this(salt, iterationCount, generateHash(salt, iterationCount, password, hashCharset)); + } + + BSDUnixDESCryptPasswordImpl(final BSDUnixDESCryptPassword password) throws InvalidKeyException { + this.salt = password.getSalt(); + this.iterationCount = password.getIterationCount(); + final byte[] hash = password.getHash(); + if (hash == null || hash.length != BSDUnixDESCryptPassword.BSD_CRYPT_DES_HASH_SIZE) { + throw log.invalidKeyBsdDesCryptPasswordHashMustBeBytes(BSDUnixDESCryptPassword.BSD_CRYPT_DES_HASH_SIZE); + } + this.hash = hash.clone(); + } + + private static int getSaltValue(final byte[] saltBytes) throws InvalidParameterSpecException { + if (saltBytes == null || saltBytes.length != BSDUnixDESCryptPassword.BSD_CRYPT_DES_SALT_SIZE) { + throw log.invalidParameterSpecSaltMustBeBytes(BSDUnixDESCryptPassword.BSD_CRYPT_DES_SALT_SIZE); + } + return (saltBytes[0] & 0xff) << 16 | (saltBytes[1] & 0xff) << 8 | saltBytes[2] & 0xff; + } + + private static byte[] getSaltBytes(final int saltVal) { + final byte[] bytes = new byte[3]; + bytes[0] = (byte) (saltVal >>> 16); + bytes[1] = (byte) (saltVal >>> 8); + bytes[2] = (byte) saltVal; + return bytes; + } + + S getKeySpec(final Class keySpecType) throws InvalidKeySpecException { + if (keySpecType.isAssignableFrom(IteratedSaltedHashPasswordSpec.class)) { + return keySpecType.cast(new IteratedSaltedHashPasswordSpec(hash.clone(), getSaltBytes(salt), iterationCount)); + } + throw new InvalidKeySpecException(); + } + + boolean verify(final char[] guess) throws InvalidKeyException { + return verify(guess, StandardCharsets.UTF_8); + } + + @Override + boolean verify(char[] guess, Charset hashCharset) throws InvalidKeyException { + return Arrays.equals(hash, generateHash(salt, iterationCount, guess, hashCharset)); + } + + boolean convertibleTo(final Class keySpecType) { + return keySpecType.isAssignableFrom(IteratedSaltedHashPasswordSpec.class); + } + + public String getAlgorithm() { + return BSDUnixDESCryptPassword.ALGORITHM_BSD_CRYPT_DES; + } + + @Override + public int getIterationCount() { + return iterationCount; + } + + public int getSalt() { + return salt; + } + + public byte[] getHash() { + return hash.clone(); + } + + private static byte[] generateHash(final int salt, int iterationCount, final char[] password) { + final byte[] bytes1 = getNormalizedPasswordBytes(password); + return crypt(bytes1, salt, iterationCount); + } + + private static byte[] generateHash(final int salt, int iterationCount, final char[] password, final Charset hashCharset) { + final byte[] bytes1 = getNormalizedPasswordBytes(password, hashCharset); + return crypt(bytes1, salt, iterationCount); + } + + // Note that the following DES tables and some of the methods below are based on + // tables and methods from the C implementation of the algorithm that's used by + // FreeBSD, NetBSD, and OpenBSD: + // http://svnweb.freebsd.org/base/head/secure/lib/libcrypt/crypt-des.c?view=markup + + private static boolean tablesInitialized = false; + + private static final byte[] IP = { + 58, 50, 42, 34, 26, 18, 10, 2, 60, 52, 44, 36, 28, 20, 12, 4, + 62, 54, 46, 38, 30, 22, 14, 6, 64, 56, 48, 40, 32, 24, 16, 8, + 57, 49, 41, 33, 25, 17, 9, 1, 59, 51, 43, 35, 27, 19, 11, 3, + 61, 53, 45, 37, 29, 21, 13, 5, 63, 55, 47, 39, 31, 23, 15, 7, + }; + private static final int[][] ipMaskLeft = new int[8][256]; + private static final int[][] ipMaskRight = new int[8][256]; + private static final int[][] fpMaskLeft = new int[8][256]; + private static final int[][] fpMaskRight = new int[8][256]; + private static final int[] initPerm = new int[64]; + private static final int[] finalPerm = new int[64]; + + private static final byte[] keyShifts = {1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1}; + + private static final byte[] keyPerm = { + 57, 49, 41, 33, 25, 17, 9, 1, 58, 50, 42, 34, 26, 18, + 10, 2, 59, 51, 43, 35, 27, 19, 11, 3, 60, 52, 44, 36, + 63, 55, 47, 39, 31, 23, 15, 7, 62, 54, 46, 38, 30, 22, + 14, 6, 61, 53, 45, 37, 29, 21, 13, 5, 28, 20, 12, 4 + }; + private static final int[] invKeyPerm = new int[64]; + private static final int[][] keyPermMaskLeft = new int[8][128]; + private static final int[][] keyPermMaskRight = new int[8][128]; + + private static final byte[] compPerm = { + 14, 17, 11, 24, 1, 5, 3, 28, 15, 6, 21, 10, + 23, 19, 12, 4, 26, 8, 16, 7, 27, 20, 13, 2, + 41, 52, 31, 37, 47, 55, 30, 40, 51, 45, 33, 48, + 44, 49, 39, 56, 34, 53, 46, 42, 50, 36, 29, 32 + }; + private static final int[] invCompPerm = new int[56]; + private static final int[][] compPermMaskLeft = new int[8][128]; + private static final int[][] compPermMaskRight = new int[8][128]; + + private static final byte[][] SBox = { + { + 14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7, + 0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8, + 4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0, + 15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13 + }, + { + 15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10, + 3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5, + 0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15, + 13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9 + }, + { + 10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8, + 13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1, + 13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7, + 1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12 + }, + { + 7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15, + 13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9, + 10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4, + 3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14 + }, + { + 2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9, + 14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6, + 4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14, + 11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3 + }, + { + 12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11, + 10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8, + 9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6, + 4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13 + }, + { + 4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1, + 13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6, + 1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2, + 6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12 + }, + { + 13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7, + 1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2, + 7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8, + 2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11 + } + }; + private static final int[][] mSBox = new int[4][4096]; + private static final byte[][] invSBox = new byte[8][64]; + + private static final byte[] PBox = { + 16, 7, 20, 21, 29, 12, 28, 17, 1, 15, 23, 26, 5, 18, 31, 10, + 2, 8, 24, 14, 32, 27, 3, 9, 19, 13, 30, 6, 22, 11, 4, 25 + }; + private static final byte[] invPBox = new byte[32]; + + private static final int[][] PSBox = new int[4][256]; + + private static final int[] bits32 = { + 0x80000000, 0x40000000, 0x20000000, 0x10000000, 0x08000000, 0x04000000, 0x02000000, 0x01000000, + 0x00800000, 0x00400000, 0x00200000, 0x00100000, 0x00080000, 0x00040000, 0x00020000, 0x00010000, + 0x00008000, 0x00004000, 0x00002000, 0x00001000, 0x00000800, 0x00000400, 0x00000200, 0x00000100, + 0x00000080, 0x00000040, 0x00000020, 0x00000010, 0x00000008, 0x00000004, 0x00000002, 0x00000001 + }; + + /** + * Initializes the DES tables. + */ + private static void setupTables() { + int inBit, outBit; + int bits28Offset = 4; + int bits24Offset = 8; + int bits8Offset = 24; + + // Invert the S-boxes and then convert them into 4 arrays + int b; + for (int i = 0; i < 8; i++) { + for (int j = 0; j < 64; j++) { + b = (j & 0x20) | ((j & 1) << 4) | ((j >>> 1) & 0xf); + invSBox[i][j] = SBox[i][b]; + } + } + + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 64; j++) { + for (int k = 0; k < 64; k++) { + mSBox[i][(j << 6) | k] = ((invSBox[(i << 1)][j] << 4) | invSBox[(i << 1) + 1][k]) & 0xff; + } + } + } + + // Compute the initial and final permutations and also initialize the inverted key permutation + for (int i = 0; i < 64; i++) { + finalPerm[i] = (IP[i] - 1) & 0xff; + initPerm[finalPerm[i]] = i; + invKeyPerm[i] = 255; + } + + // Invert the key permutation and initialize the inverted key compression permutation + for (int i = 0; i < 56; i++) { + invKeyPerm[(keyPerm[i] - 1) & 0xff] = i; + invCompPerm[i] = 255; + } + + // Invert the key compression permutation + for (int i = 0; i < 48; i++) { + invCompPerm[(compPerm[i] - 1) & 0xff] = i; + } + + // Set up mask arrays + for (int i = 0; i < 8; i++) { + for (int j = 0; j < 256; j++) { + ipMaskLeft[i][j] = 0; + ipMaskRight[i][j] = 0; + fpMaskLeft[i][j] = 0; + fpMaskRight[i][j] = 0; + for (int k = 0; k < 8; k++) { + inBit = 8 * i + k; + if ((j & bits32[bits8Offset + k]) != 0) { + outBit = initPerm[inBit]; + if (outBit < 32) { + ipMaskLeft[i][j] |= bits32[outBit]; + } else { + ipMaskRight[i][j] |= bits32[outBit - 32]; + } + + outBit = finalPerm[inBit]; + if (outBit < 32) { + fpMaskLeft[i][j] |= bits32[outBit]; + } else { + fpMaskRight[i][j] |= bits32[outBit - 32]; + } + } + } + } + for (int j = 0; j < 128; j++) { + keyPermMaskLeft[i][j] = 0; + keyPermMaskRight[i][j] = 0; + for (int k = 0; k < 7; k++) { + inBit = 8 * i + k; + if ((j & bits32[bits8Offset + k + 1]) != 0) { + outBit = invKeyPerm[inBit]; + if (outBit == 255) { + continue; + } else if (outBit < 28) { + keyPermMaskLeft[i][j] |= bits32[bits28Offset + outBit]; + } else { + keyPermMaskRight[i][j] |= bits32[bits28Offset + (outBit - 28)]; + } + } + } + + compPermMaskLeft[i][j] = 0; + compPermMaskRight[i][j] = 0; + for (int k = 0; k < 7; k++) { + inBit = 7 * i + k; + if ((j & bits32[bits8Offset + k + 1]) != 0) { + outBit = invCompPerm[inBit]; + if (outBit == 255) { + continue; + } else if (outBit < 24) { + compPermMaskLeft[i][j] |= bits32[bits24Offset + outBit]; + } else { + compPermMaskRight[i][j] |= bits32[bits24Offset + outBit - 24]; + } + } + } + } + } + + // Invert the P-box permutation + for (int i = 0; i < 32; i++) { + invPBox[(PBox[i] - 1) & 0xff] = (byte) i; + } + + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 256; j++) { + PSBox[i][j] = 0; + for (int k = 0; k < 8; k++) { + if ((j & bits32[bits8Offset + k]) != 0) { + PSBox[i][j] |= bits32[invPBox[8 * i + k] & 0xff]; + } + } + } + } + tablesInitialized = true; + } + + /** + * Hashes the given password using the BSD variant of the Unix DES Crypt algorithm. + * + * @param password the password to be hashed + * @param salt the 24-bit salt to be used + * @param iterationCount the number of iterations to use, must be between 1 and 16777215, inclusive + * @return a {@code byte[]} containing the hashed password + */ + static byte[] crypt(final byte[] password, final int salt, final int iterationCount) { + byte[] hash; + byte[] currentKey; + byte[] nextGroup; + int[] currentSchedule; + + if (iterationCount < 1 || iterationCount > 16777215) { + throw log.invalidNumberOfRoundsMustBeIntBetween(1, 16777215); + } + + if (!tablesInitialized) { + setupTables(); + } + + // The first group becomes the initial key + currentKey = getKeyGroup(password, 0); + + int nextStartIndex = 8; + int passwordLen = password.length; + while (nextStartIndex < passwordLen) { + currentSchedule = desSetKey(currentKey); + + // Encrypt the current key using itself as the input + hash = desCipher(currentSchedule, fourBytesToInt(currentKey, 0), fourBytesToInt(currentKey, 4), 0, 1); + + // XOR the result with the next group to get the next key + nextGroup = getKeyGroup(password, nextStartIndex); + for (int i = 0; i < nextGroup.length; i++) { + currentKey[i] = (byte)(hash[i] ^ nextGroup[i]); + } + + nextStartIndex += 8; + } + + currentSchedule = desSetKey(currentKey); + + // Encrypt the current key using an input of 0 + hash = desCipher(currentSchedule, 0, 0, salt, iterationCount); + return hash; + } + + /** + * Gets the key group from the given password that starts at the given index. The key + * group contains 8 bytes and is such that the byte at index i contains the lower + * 7 bits of the byte at {@code password[startIndex + i]}. + * + * @param password the password + * @param startIndex the index where the key group begins + * @return a {@code byte[]} containing the key group + */ + private static byte[] getKeyGroup(final byte[] password, int startIndex) { + final byte[] keyGroup = new byte[8]; + for (int i = 0; i < keyGroup.length; i++) { + keyGroup[i] = 0; + } + + int index = startIndex; + for (int i = 0; i < keyGroup.length && index < password.length; i++) { + final int iChar = password[index++]; + keyGroup[i] = (byte) (iChar << 1); + } + return keyGroup; + } + + /** + * Calculates the key schedule for the given key. The key schedule contains + * 16 subkeys, each of which can be represented by a pair of integers. + * + * @param key the key + * @return an {@code int[]} of size 32 containing the key schedule + */ + private static int[] desSetKey(final byte[] key) { + final int[] schedule = new int[32]; + int key0 = fourBytesToInt(key, 0); + int key1 = fourBytesToInt(key, 4); + + // Permute the key and split it into two 28-bit subkeys + int k0 = keyPermMaskLeft[0][key0 >>> 25] | keyPermMaskLeft[1][(key0 >>> 17) & 0x7f] + | keyPermMaskLeft[2][(key0 >>> 9) & 0x7f] | keyPermMaskLeft[3][(key0 >>> 1) & 0x7f] + | keyPermMaskLeft[4][key1 >>> 25] | keyPermMaskLeft[5][(key1 >>> 17) & 0x7f] + | keyPermMaskLeft[6][(key1 >>> 9) & 0x7f] | keyPermMaskLeft[7][(key1 >>> 1) & 0x7f]; + + int k1 = keyPermMaskRight[0][key0 >>> 25] | keyPermMaskRight[1][(key0 >>> 17) & 0x7f] + | keyPermMaskRight[2][(key0 >>> 9) & 0x7f] | keyPermMaskRight[3][(key0 >>> 1) & 0x7f] + | keyPermMaskRight[4][key1 >>> 25] | keyPermMaskRight[5][(key1 >>> 17) & 0x7f] + | keyPermMaskRight[6][(key1 >>> 9) & 0x7f] | keyPermMaskRight[7][(key1 >>> 1) & 0x7f]; + + // Rotate the subkeys and do the compression permutation + int shifts = 0; + int j = 0; + int t0, t1; + for (int i = 0; i < 16; i++) { + shifts += keyShifts[i] & 0xff; + t0 = (k0 << shifts) | (k0 >>> (28 - shifts)); + t1 = (k1 << shifts) | (k1 >>> (28 - shifts)); + + // Left half of the subkey + schedule[j++] = compPermMaskLeft[0][(t0 >>> 21) & 0x7f] | compPermMaskLeft[1][(t0 >>> 14) & 0x7f] + | compPermMaskLeft[2][(t0 >>> 7) & 0x7f] | compPermMaskLeft[3][t0 & 0x7f] + | compPermMaskLeft[4][(t1 >>> 21) & 0x7f] | compPermMaskLeft[5][(t1 >>> 14) & 0x7f] + | compPermMaskLeft[6][(t1 >>> 7) & 0x7f] | compPermMaskLeft[7][t1 & 0x7f]; + + // Right half of the subkey + schedule[j++] = compPermMaskRight[0][(t0 >>> 21) & 0x7f] | compPermMaskRight[1][(t0 >>> 14) & 0x7f] + | compPermMaskRight[2][(t0 >>> 7) & 0x7f] | compPermMaskRight[3][t0 & 0x7f] + | compPermMaskRight[4][(t1 >>> 21) & 0x7f] | compPermMaskRight[5][(t1 >>> 14) & 0x7f] + | compPermMaskRight[6][(t1 >>> 7) & 0x7f] | compPermMaskRight[7][t1 & 0x7f]; + } + return schedule; + } + + /** + * Performs DES encryption using the given key schedule, input block, salt, and iteration count. + * + * @param schedule the key schedule + * @param leftInput the most significant half of the input block + * @param rightInput the least signicant half of the input block + * @param salt the 24-bit salt to be used + * @param iterationCount the number of iterations to use + * @return a {@code byte[]} containing the hashed password + */ + private static byte[] desCipher(final int[] schedule, final int leftInput, final int rightInput, final int salt, final int iterationCount) { + int l, r; + int f = 0; + final byte[] hash = new byte[8]; + + int rearrangedSalt = setupSalt(salt); + + // Initial permutation + l = ipMaskLeft[0][leftInput >>> 24] | ipMaskLeft[1][(leftInput >>> 16) & 0xff] | ipMaskLeft[2][(leftInput >>> 8) & 0xff] + | ipMaskLeft[3][leftInput & 0xff] | ipMaskLeft[4][rightInput >>> 24] | ipMaskLeft[5][(rightInput >>> 16) & 0xff] + | ipMaskLeft[6][(rightInput >>> 8) & 0xff] | ipMaskLeft[7][rightInput & 0xff]; + + r = ipMaskRight[0][leftInput >>> 24] | ipMaskRight[1][(leftInput >>> 16) & 0xff] | ipMaskRight[2][(leftInput >>> 8) & 0xff] + | ipMaskRight[3][leftInput & 0xff] | ipMaskRight[4][rightInput >>> 24] | ipMaskRight[5][(rightInput >>> 16) & 0xff] + | ipMaskRight[6][(rightInput >>> 8) & 0xff] | ipMaskRight[7][rightInput & 0xff]; + + int rLeft, rRight; + int k; + for (int i = 0; i < iterationCount; i++) { + k = 0; + for (int j = 0; j < 16; j++) { + + // Expand r to 48 bits (simulates the E-box) + rLeft = ((r & 0x00000001) << 23) | ((r & 0xf8000000) >>> 9) | ((r & 0x1f800000) >>> 11) + | ((r & 0x01f80000) >>> 13) | ((r & 0x001f8000) >>> 15); + rRight = ((r & 0x0001f800) << 7) | ((r & 0x00001f80) << 5) | ((r & 0x000001f8) << 3) + | ((r & 0x0000001f) << 1) | ((r & 0x80000000) >>> 31); + + // The salt will flip certain bits + f = (rLeft ^ rRight) & rearrangedSalt; + rLeft ^= (f ^ schedule[k++]); + rRight ^= (f ^ schedule[k++]); + + // Perform S-box lookups and do the P-box permutation + f = PSBox[0][mSBox[0][rLeft >>> 12]] | PSBox[1][mSBox[1][rLeft & 0xfff]] | PSBox[2][mSBox[2][rRight >>> 12]] + | PSBox[3][mSBox[3][rRight & 0xfff]]; + + f ^= l; + l = r; + r = f; + } + + r = l; + l = f; + } + + // Final permutation + int leftOutput, rightOutput; + leftOutput = fpMaskLeft[0][l >>> 24] | fpMaskLeft[1][(l >>> 16) & 0xff] | fpMaskLeft[2][(l >>> 8) & 0xff] + | fpMaskLeft[3][l & 0xff] | fpMaskLeft[4][r >>> 24] | fpMaskLeft[5][(r >>> 16) & 0xff] + | fpMaskLeft[6][(r >>> 8) & 0xff] | fpMaskLeft[7][r & 0xff]; + + rightOutput = fpMaskRight[0][l >>> 24] | fpMaskRight[1][(l >>> 16) & 0xff] | fpMaskRight[2][(l >>> 8) & 0xff] + | fpMaskRight[3][l & 0xff] | fpMaskRight[4][r >>> 24] | fpMaskRight[5][(r >>> 16) & 0xff] + | fpMaskRight[6][(r >>> 8) & 0xff] | fpMaskRight[7][r & 0xff]; + + intToFourBytes(leftOutput, hash, 0); + intToFourBytes(rightOutput, hash, 4); + return hash; + } + + /** + * Rearranges the bits in the 24-bit salt. + */ + private static int setupSalt(int salt) { + int resultBit = 0x800000; + int saltBit = 1; + int result = 0; + + for (int i = 0; i < 24; i++) { + if ((salt & saltBit) != 0) { + result |= resultBit; + } + saltBit <<= 1; + resultBit >>= 1; + } + return result; + } + + private static int fourBytesToInt(final byte[] b, int offset) { + // Big-endian format + final byte b4 = b[offset++]; + int value = (b4 & 0xff) << 24; + final byte b3 = b[offset++]; + value |= (b3 & 0xff) << 16; + final byte b2 = b[offset++]; + value |= (b2 & 0xff) << 8; + final byte b1 = b[offset ]; + value |= b1 & 0xff; + return value; + } + + private static void intToFourBytes(final int iValue, final byte[] b, int offset) { + // Big-endian format + b[offset++] = (byte) (iValue >>> 24 & 0xff); + b[offset++] = (byte) (iValue >>> 16 & 0xff); + b[offset++] = (byte) (iValue >>> 8 & 0xff); + b[offset ] = (byte) (iValue & 0xff); + } + + private void readObject(ObjectInputStream ignored) throws NotSerializableException { + throw new NotSerializableException(); + } + + public int hashCode() { + return multiHashOrdered(multiHashOrdered(Arrays.hashCode(hash), salt), iterationCount); + } + + public boolean equals(final Object obj) { + if (! (obj instanceof BSDUnixDESCryptPasswordImpl)) { + return false; + } + BSDUnixDESCryptPasswordImpl other = (BSDUnixDESCryptPasswordImpl) obj; + return iterationCount == other.iterationCount && salt == other.salt && Arrays.equals(hash, other.hash); + } + + Object writeReplace() { + return BSDUnixDESCryptPassword.createRaw(getAlgorithm(), hash, salt, iterationCount); + } + + public BSDUnixDESCryptPasswordImpl clone() { + return this; + } + +} Index: 3rdParty_sources/elytron/org/wildfly/security/password/impl/ClearPasswordImpl.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/password/impl/ClearPasswordImpl.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/password/impl/ClearPasswordImpl.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,117 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2013 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.password.impl; + +import java.io.NotSerializableException; +import java.io.ObjectInputStream; +import java.nio.charset.Charset; +import java.security.InvalidKeyException; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.KeySpec; +import java.util.Arrays; + +import javax.security.auth.DestroyFailedException; + +import org.wildfly.security.password.interfaces.ClearPassword; +import org.wildfly.security.password.spec.ClearPasswordSpec; + +final class ClearPasswordImpl extends AbstractPasswordImpl implements ClearPassword { + + private static final long serialVersionUID = -3949572193624333918L; + + private char[] password; + + ClearPasswordImpl(final char[] password) { + this.password = password; + } + + ClearPasswordImpl(ClearPassword clearPassword) { + password = clearPassword.getPassword().clone(); + } + + public String getAlgorithm() { + return "clear"; + } + + public char[] getPassword() throws IllegalStateException { + try { + return password.clone(); + } catch (NullPointerException ignored) { + throw new IllegalStateException(); + } + } + + public void destroy() throws DestroyFailedException { + final char[] password = this.password; + this.password = null; + if (password != null) Arrays.fill(password, '\0'); + } + + public boolean isDestroyed() { + return password == null; + } + + S getKeySpec(final Class keySpecType) throws InvalidKeySpecException { + if (keySpecType.isAssignableFrom(ClearPasswordSpec.class)) { + final char[] password = getPassword(); + return keySpecType.cast(new ClearPasswordSpec(password.clone())); + } + throw new InvalidKeySpecException(); + } + + boolean verify(final char[] guess) { + return Arrays.equals(getPassword(), guess); + } + + @Override + boolean verify(char[] guess, Charset hashCharset) throws InvalidKeyException { + return verify(guess); + } + + boolean convertibleTo(final Class keySpecType) { + return keySpecType.isAssignableFrom(ClearPasswordSpec.class); + } + + private void readObject(ObjectInputStream ignored) throws NotSerializableException { + throw new NotSerializableException(); + } + + Object writeReplace() { + return ClearPassword.createRaw(getAlgorithm(), password); + } + + public ClearPasswordImpl clone() { + final char[] password = this.password; + if (password == null) { + return this; + } + return new ClearPasswordImpl(password.clone()); + } + + public int hashCode() { + // hashcode becomes 0 when destroyed! + return Arrays.hashCode(password); + } + + public boolean equals(final Object obj) { + final char[] password = this.password; + // destroyed passwords are equal to nothing + return obj instanceof ClearPasswordImpl && password != null && Arrays.equals(password, ((ClearPasswordImpl) obj).password); + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/password/impl/DigestPasswordAlgorithmParametersSpiImpl.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/password/impl/DigestPasswordAlgorithmParametersSpiImpl.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/password/impl/DigestPasswordAlgorithmParametersSpiImpl.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,60 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2016 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.password.impl; + +import java.security.AlgorithmParametersSpi; + +import org.wildfly.security.asn1.ASN1Decoder; +import org.wildfly.security.asn1.ASN1Encoder; +import org.wildfly.security.password.spec.DigestPasswordAlgorithmSpec; +import org.wildfly.security.util.AbstractAlgorithmParametersSpiImpl; + +/** + * An implementation of the {@link AlgorithmParametersSpi} SPI, in order to support encoding and decoding of + * password algorithm parameters. + * + * @author David M. Lloyd + */ +public final class DigestPasswordAlgorithmParametersSpiImpl extends AbstractAlgorithmParametersSpiImpl { + + /** + * Construct a new instance. + */ + public DigestPasswordAlgorithmParametersSpiImpl() { + } + + protected Class getParameterType() { + return DigestPasswordAlgorithmSpec.class; + } + + protected void engineEncode(final ASN1Encoder encoder, final DigestPasswordAlgorithmSpec parameterSpec) { + encoder.startSequence(); + encoder.encodeOctetString(parameterSpec.getUsername()); + encoder.encodeOctetString(parameterSpec.getRealm()); + encoder.endSequence(); + } + + protected DigestPasswordAlgorithmSpec engineDecode(final ASN1Decoder decoder) { + decoder.startSequence(); + final String username = decoder.decodeOctetStringAsString(); + final String realm = decoder.decodeOctetStringAsString(); + decoder.endSequence(); + return new DigestPasswordAlgorithmSpec(username, realm); + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/password/impl/DigestPasswordImpl.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/password/impl/DigestPasswordImpl.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/password/impl/DigestPasswordImpl.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,184 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2014 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.password.impl; + +import static org.wildfly.common.math.HashMath.multiHashOrdered; +import static org.wildfly.security.password.impl.ElytronMessages.log; +import static org.wildfly.security.password.impl.DigestUtil.userRealmPasswordDigest; + +import java.io.NotSerializableException; +import java.io.ObjectInputStream; +import java.nio.charset.Charset; +import java.security.InvalidKeyException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.KeySpec; +import java.util.Arrays; + +import org.wildfly.security.password.interfaces.DigestPassword; +import org.wildfly.security.password.spec.DigestPasswordAlgorithmSpec; +import org.wildfly.security.password.spec.DigestPasswordSpec; +import org.wildfly.security.password.spec.EncryptablePasswordSpec; + +/** + * Pre-digested (DigestMD5) credential type implementation. + * + * @author Peter Skopek. + * @author Darran Lofthouse + */ +class DigestPasswordImpl extends AbstractPasswordImpl implements DigestPassword { + + private static final long serialVersionUID = - 8454721263222529136L; + + private final String algorithm; + private final String username; + private final String realm; + private final byte[] digest; + + DigestPasswordImpl(final String algorithm, final String username, final String realm, final byte[] digest) { + this.algorithm = algorithm; + this.username = username; + this.realm = realm; + this.digest = digest; + } + + DigestPasswordImpl(final String algorithm, final DigestPasswordSpec spec) { + this(algorithm, spec.getUsername(), spec.getRealm(), spec.getDigest().clone()); + } + + DigestPasswordImpl(final DigestPassword password) { + this(password.getAlgorithm(), password.getUsername(), password.getRealm(), password.getDigest().clone()); + } + + // We can not support conversion from ClearPasswordSpec as we require additional + // information we can not generate ourselves. + + DigestPasswordImpl(final String algorithm, final EncryptablePasswordSpec spec) throws InvalidKeySpecException { + this(algorithm, spec.getPassword(), (DigestPasswordAlgorithmSpec) spec.getAlgorithmParameterSpec(), spec.getHashCharset()); + } + + DigestPasswordImpl(final String algorithm, final char[] password, final DigestPasswordAlgorithmSpec spec, final Charset hashCharset) throws InvalidKeySpecException { + this.algorithm = algorithm; + this.username = spec.getUsername(); + this.realm = spec.getRealm(); + try { + this.digest = userRealmPasswordDigest(getMessageDigest(algorithm), spec.getUsername(), spec.getRealm(), password, hashCharset); + } catch (NoSuchAlgorithmException e) { + throw log.invalidKeySpecNoSuchMessageDigestAlgorithm(algorithm); + } + } + + @Override + public String getAlgorithm() { + return algorithm; + } + + @Override + public String getUsername() { + return username; + } + + @Override + public String getRealm() { + return realm; + } + + @Override + public byte[] getDigest() { + return digest.clone(); + } + + @Override + S getKeySpec(Class keySpecType) throws InvalidKeySpecException { + if (keySpecType.isAssignableFrom(DigestPasswordSpec.class)) { + return keySpecType.cast(new DigestPasswordSpec(username, realm, digest.clone())); + } + throw new InvalidKeySpecException(); + } + + @Override + boolean verify(char[] guess) throws InvalidKeyException { + try { + byte[] guessDigest = userRealmPasswordDigest(getMessageDigest(algorithm), username, realm, guess); + return MessageDigest.isEqual(digest, guessDigest); + } catch (NoSuchAlgorithmException e) { + throw log.invalidKeyNoSuchMessageDigestAlgorithm(algorithm); + } + } + + @Override + boolean verify(char[] guess, Charset hashCharset) throws InvalidKeyException { + try { + byte[] guessDigest = userRealmPasswordDigest(getMessageDigest(algorithm), username, realm, guess, hashCharset); + // compare guessDigest equals the digest calculated by server + return MessageDigest.isEqual(digest, guessDigest); + } catch (NoSuchAlgorithmException e) { + throw log.invalidKeyNoSuchMessageDigestAlgorithm(algorithm); + } + } + + @Override + boolean convertibleTo(Class keySpecType) { + return keySpecType.isAssignableFrom(DigestPasswordSpec.class); + } + + private static MessageDigest getMessageDigest(final String algorithm) throws NoSuchAlgorithmException { + switch (algorithm) { + case ALGORITHM_DIGEST_MD5: + return MessageDigest.getInstance("MD5"); + case ALGORITHM_DIGEST_SHA: + return MessageDigest.getInstance("SHA-1"); + case ALGORITHM_DIGEST_SHA_256: + return MessageDigest.getInstance("SHA-256"); + case ALGORITHM_DIGEST_SHA_384: + return MessageDigest.getInstance("SHA-384"); + case ALGORITHM_DIGEST_SHA_512: + return MessageDigest.getInstance("SHA-512"); + case ALGORITHM_DIGEST_SHA_512_256: + return MessageDigest.getInstance("SHA-512-256"); + default: + throw log.noSuchAlgorithmInvalidAlgorithm(algorithm); + } + } + + public int hashCode() { + return multiHashOrdered(multiHashOrdered(multiHashOrdered(Arrays.hashCode(digest), username.hashCode()), realm.hashCode()), algorithm.hashCode()); + } + + public boolean equals(final Object obj) { + if (! (obj instanceof DigestPasswordImpl)) { + return false; + } + DigestPasswordImpl other = (DigestPasswordImpl) obj; + return MessageDigest.isEqual(digest, other.digest) && username.equals(other.username) && realm.equals(other.realm) && algorithm.equals(other.algorithm); + } + + private void readObject(ObjectInputStream ignored) throws NotSerializableException { + throw new NotSerializableException(); + } + + Object writeReplace() { + return DigestPassword.createRaw(algorithm, username, realm, digest); + } + + public DigestPasswordImpl clone() { + return this; + } + +} Index: 3rdParty_sources/elytron/org/wildfly/security/password/impl/DigestUtil.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/password/impl/DigestUtil.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/password/impl/DigestUtil.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,64 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2016 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.password.impl; + +import java.nio.charset.Charset; +import java.nio.charset.CharsetEncoder; +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; + +import org.wildfly.common.bytes.ByteStringBuilder; + +/** + * Common utility functions used by Digest authentication mechanisms. + * + * @author Darran Lofthouse + * @author Peter Skopek. + */ +class DigestUtil { + + public static byte[] userRealmPasswordDigest(MessageDigest messageDigest, String username, String realm, char[] password) { + return userRealmPasswordDigest(messageDigest, username, realm, password, StandardCharsets.UTF_8); + } + + public static byte[] userRealmPasswordDigest(MessageDigest messageDigest, String username, String realm, char[] password, Charset chosenCharset) { + CharsetEncoder latin1Encoder = StandardCharsets.ISO_8859_1.newEncoder(); + latin1Encoder.reset(); + boolean bothLatin1 = latin1Encoder.canEncode(username); + latin1Encoder.reset(); + if (bothLatin1) { + for (char c: password) { + bothLatin1 = bothLatin1 && latin1Encoder.canEncode(c); + } + } + + ByteStringBuilder urp = new ByteStringBuilder(); // username:realm:password + urp.append(username.getBytes(chosenCharset)); + urp.append(':'); + if (realm != null) { + urp.append(realm.getBytes((chosenCharset))); + } else { + urp.append(""); + } + urp.append(':'); + urp.append(new String(password).getBytes((chosenCharset))); + + return messageDigest.digest(urp.toArray()); + } + +} Index: 3rdParty_sources/elytron/org/wildfly/security/password/impl/ElytronMessages.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/password/impl/ElytronMessages.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/password/impl/ElytronMessages.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,110 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2014 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.password.impl; + +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.InvalidParameterSpecException; + +import javax.security.sasl.SaslException; + +import org.jboss.logging.BasicLogger; +import org.jboss.logging.Logger; +import org.jboss.logging.annotations.Cause; +import org.jboss.logging.annotations.Message; +import org.jboss.logging.annotations.MessageLogger; +import org.jboss.logging.annotations.ValidIdRange; +import org.jboss.logging.annotations.ValidIdRanges; + +/** + * Log messages and exceptions for Elytron. + * + * @author David M. Lloyd + * @author Darran Lofthouse + */ +@MessageLogger(projectCode = "ELY", length = 5) +@ValidIdRanges({ + @ValidIdRange(min = 2, max = 4), + @ValidIdRange(min = 5151, max = 5151), + @ValidIdRange(min = 8013, max = 8028), + @ValidIdRange(min = 21000, max = 21999) +}) +interface ElytronMessages extends BasicLogger { + + ElytronMessages log = Logger.getMessageLogger(ElytronMessages.class, "org.wildfly.security"); + + @Message(id = 2, value = "Parameter %s is empty") + IllegalArgumentException emptyParameter(String parameter); + + @Message(id = 4, value = "Unrecognized algorithm \"%s\"") + IllegalArgumentException unrecognizedAlgorithm(String algorithm); + + @Message(id = 5151, value = "Invalid OTP algorithm \"%s\"") + SaslException mechInvalidOTPAlgorithm(String algorithm); + + @Message(id = 8013, value = "No such MessageDigest algorithm for \"%s\"") + InvalidKeySpecException invalidKeySpecNoSuchMessageDigestAlgorithm(String algorithm); + + @Message(id = 8014, value = "No such MessageDigest algorithm for \"%s\"") + InvalidKeyException invalidKeyNoSuchMessageDigestAlgorithm(String algorithm); + + @Message(id = 8015, value = "Cannot verify password") + InvalidKeyException invalidKeyCannotVerifyPassword(@Cause Throwable cause); + + @Message(id = 8017, value = "DES crypt password hash must be %d bytes") + InvalidKeyException invalidKeyDesCryptPasswordHashMustBeBytes(int bytes); + + @Message(id = 8018, value = "Salt must be %d bytes (%d bits)") + InvalidParameterSpecException invalidParameterSpecSaltMustBeBytesBits(int bytes, int bits); + + @Message(id = 8020, value = "Invalid number of rounds. Must be an integer between %d and %d, inclusive") + IllegalArgumentException invalidNumberOfRoundsMustBeIntBetween(int min, int max); + + @Message(id = 8021, value = "Invalid salt: must be %d bytes long") + IllegalArgumentException invalidSaltMustBeBytesLong(int length); + + @Message(id = 8022, value = "BSD DES crypt password hash must be %d bytes") + InvalidKeySpecException invalidKeySpecBsdDesCryptPasswordHashMustBeBytes(int bytes); + + @Message(id = 8023, value = "Salt must be %d bytes") + InvalidParameterSpecException invalidParameterSpecSaltMustBeBytes(int bytes); + + @Message(id = 8024, value = "BSD DES crypt password hash must be %d bytes") + InvalidKeyException invalidKeyBsdDesCryptPasswordHashMustBeBytes(int bytes); + + @Message(id = 8025, value = "Expected to get a \"%s\" as spec, got \"%s\"") + InvalidKeySpecException invalidKeySpecExpectedSpecGotSpec(String expected, String got); + + @Message(id = 8026, value = "Unknown algorithm \"%s\" or incompatible PasswordSpec \"%s\"") + InvalidKeySpecException invalidKeySpecUnknownAlgorithmOrIncompatiblePasswordSpec(String algorithm, String passwordSpec); + + @Message(id = 8027, value = "Unknown password type or algorithm") + InvalidKeyException invalidKeyUnknownUnknownPasswordTypeOrAlgorithm(); + + @Message(id = 8028, value = "Invalid algorithm \"%s\"") + NoSuchAlgorithmException noSuchAlgorithmInvalidAlgorithm(String algorithm); + + @Message(id = 21000, value = "Invalid algorithm parameter specification") + InvalidAlgorithmParameterException invalidAlgorithmParameterSpecification(); + + @Message(id = 21001, value = "Invalid sequence number algorithm parameter \"%s\"") + InvalidAlgorithmParameterException invalidSequenceNumberAlgorithmParameter(int sequenceNumber); +} Index: 3rdParty_sources/elytron/org/wildfly/security/password/impl/ElytronMessages_$logger.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/password/impl/ElytronMessages_$logger.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/password/impl/ElytronMessages_$logger.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,209 @@ +package org.wildfly.security.password.impl; + +import java.util.Locale; +import java.io.Serializable; +import javax.annotation.Generated; +import java.security.InvalidAlgorithmParameterException; +import org.jboss.logging.DelegatingBasicLogger; +import java.lang.String; +import java.security.NoSuchAlgorithmException; +import org.jboss.logging.Logger; +import javax.security.sasl.SaslException; +import java.security.InvalidKeyException; +import org.jboss.logging.BasicLogger; +import java.lang.Throwable; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.InvalidParameterSpecException; +import java.util.Arrays; +import java.lang.IllegalArgumentException; + +/** + * Warning this class consists of generated code. + */ +@Generated(value = "org.jboss.logging.processor.generator.model.MessageLoggerImplementor", date = "2024-01-15T21:44:57+0100") +public class ElytronMessages_$logger extends DelegatingBasicLogger implements ElytronMessages, BasicLogger, Serializable { + private static final long serialVersionUID = 1L; + private static final String FQCN = ElytronMessages_$logger.class.getName(); + public ElytronMessages_$logger(final Logger log) { + super(log); + } + private static final Locale LOCALE = Locale.ROOT; + protected Locale getLoggingLocale() { + return LOCALE; + } + protected String emptyParameter$str() { + return "ELY00002: Parameter %s is empty"; + } + @Override + public final IllegalArgumentException emptyParameter(final String parameter) { + final IllegalArgumentException result = new IllegalArgumentException(String.format(getLoggingLocale(), emptyParameter$str(), parameter)); + _copyStackTraceMinusOne(result); + return result; + } + private static void _copyStackTraceMinusOne(final Throwable e) { + final StackTraceElement[] st = e.getStackTrace(); + e.setStackTrace(Arrays.copyOfRange(st, 1, st.length)); + } + protected String unrecognizedAlgorithm$str() { + return "ELY00004: Unrecognized algorithm \"%s\""; + } + @Override + public final IllegalArgumentException unrecognizedAlgorithm(final String algorithm) { + final IllegalArgumentException result = new IllegalArgumentException(String.format(getLoggingLocale(), unrecognizedAlgorithm$str(), algorithm)); + _copyStackTraceMinusOne(result); + return result; + } + protected String mechInvalidOTPAlgorithm$str() { + return "ELY05151: Invalid OTP algorithm \"%s\""; + } + @Override + public final SaslException mechInvalidOTPAlgorithm(final String algorithm) { + final SaslException result = new SaslException(String.format(getLoggingLocale(), mechInvalidOTPAlgorithm$str(), algorithm)); + _copyStackTraceMinusOne(result); + return result; + } + protected String invalidKeySpecNoSuchMessageDigestAlgorithm$str() { + return "ELY08013: No such MessageDigest algorithm for \"%s\""; + } + @Override + public final InvalidKeySpecException invalidKeySpecNoSuchMessageDigestAlgorithm(final String algorithm) { + final InvalidKeySpecException result = new InvalidKeySpecException(String.format(getLoggingLocale(), invalidKeySpecNoSuchMessageDigestAlgorithm$str(), algorithm)); + _copyStackTraceMinusOne(result); + return result; + } + protected String invalidKeyNoSuchMessageDigestAlgorithm$str() { + return "ELY08014: No such MessageDigest algorithm for \"%s\""; + } + @Override + public final InvalidKeyException invalidKeyNoSuchMessageDigestAlgorithm(final String algorithm) { + final InvalidKeyException result = new InvalidKeyException(String.format(getLoggingLocale(), invalidKeyNoSuchMessageDigestAlgorithm$str(), algorithm)); + _copyStackTraceMinusOne(result); + return result; + } + protected String invalidKeyCannotVerifyPassword$str() { + return "ELY08015: Cannot verify password"; + } + @Override + public final InvalidKeyException invalidKeyCannotVerifyPassword(final Throwable cause) { + final InvalidKeyException result = new InvalidKeyException(String.format(getLoggingLocale(), invalidKeyCannotVerifyPassword$str()), cause); + _copyStackTraceMinusOne(result); + return result; + } + protected String invalidKeyDesCryptPasswordHashMustBeBytes$str() { + return "ELY08017: DES crypt password hash must be %d bytes"; + } + @Override + public final InvalidKeyException invalidKeyDesCryptPasswordHashMustBeBytes(final int bytes) { + final InvalidKeyException result = new InvalidKeyException(String.format(getLoggingLocale(), invalidKeyDesCryptPasswordHashMustBeBytes$str(), bytes)); + _copyStackTraceMinusOne(result); + return result; + } + protected String invalidParameterSpecSaltMustBeBytesBits$str() { + return "ELY08018: Salt must be %d bytes (%d bits)"; + } + @Override + public final InvalidParameterSpecException invalidParameterSpecSaltMustBeBytesBits(final int bytes, final int bits) { + final InvalidParameterSpecException result = new InvalidParameterSpecException(String.format(getLoggingLocale(), invalidParameterSpecSaltMustBeBytesBits$str(), bytes, bits)); + _copyStackTraceMinusOne(result); + return result; + } + protected String invalidNumberOfRoundsMustBeIntBetween$str() { + return "ELY08020: Invalid number of rounds. Must be an integer between %d and %d, inclusive"; + } + @Override + public final IllegalArgumentException invalidNumberOfRoundsMustBeIntBetween(final int min, final int max) { + final IllegalArgumentException result = new IllegalArgumentException(String.format(getLoggingLocale(), invalidNumberOfRoundsMustBeIntBetween$str(), min, max)); + _copyStackTraceMinusOne(result); + return result; + } + protected String invalidSaltMustBeBytesLong$str() { + return "ELY08021: Invalid salt: must be %d bytes long"; + } + @Override + public final IllegalArgumentException invalidSaltMustBeBytesLong(final int length) { + final IllegalArgumentException result = new IllegalArgumentException(String.format(getLoggingLocale(), invalidSaltMustBeBytesLong$str(), length)); + _copyStackTraceMinusOne(result); + return result; + } + protected String invalidKeySpecBsdDesCryptPasswordHashMustBeBytes$str() { + return "ELY08022: BSD DES crypt password hash must be %d bytes"; + } + @Override + public final InvalidKeySpecException invalidKeySpecBsdDesCryptPasswordHashMustBeBytes(final int bytes) { + final InvalidKeySpecException result = new InvalidKeySpecException(String.format(getLoggingLocale(), invalidKeySpecBsdDesCryptPasswordHashMustBeBytes$str(), bytes)); + _copyStackTraceMinusOne(result); + return result; + } + protected String invalidParameterSpecSaltMustBeBytes$str() { + return "ELY08023: Salt must be %d bytes"; + } + @Override + public final InvalidParameterSpecException invalidParameterSpecSaltMustBeBytes(final int bytes) { + final InvalidParameterSpecException result = new InvalidParameterSpecException(String.format(getLoggingLocale(), invalidParameterSpecSaltMustBeBytes$str(), bytes)); + _copyStackTraceMinusOne(result); + return result; + } + protected String invalidKeyBsdDesCryptPasswordHashMustBeBytes$str() { + return "ELY08024: BSD DES crypt password hash must be %d bytes"; + } + @Override + public final InvalidKeyException invalidKeyBsdDesCryptPasswordHashMustBeBytes(final int bytes) { + final InvalidKeyException result = new InvalidKeyException(String.format(getLoggingLocale(), invalidKeyBsdDesCryptPasswordHashMustBeBytes$str(), bytes)); + _copyStackTraceMinusOne(result); + return result; + } + protected String invalidKeySpecExpectedSpecGotSpec$str() { + return "ELY08025: Expected to get a \"%s\" as spec, got \"%s\""; + } + @Override + public final InvalidKeySpecException invalidKeySpecExpectedSpecGotSpec(final String expected, final String got) { + final InvalidKeySpecException result = new InvalidKeySpecException(String.format(getLoggingLocale(), invalidKeySpecExpectedSpecGotSpec$str(), expected, got)); + _copyStackTraceMinusOne(result); + return result; + } + protected String invalidKeySpecUnknownAlgorithmOrIncompatiblePasswordSpec$str() { + return "ELY08026: Unknown algorithm \"%s\" or incompatible PasswordSpec \"%s\""; + } + @Override + public final InvalidKeySpecException invalidKeySpecUnknownAlgorithmOrIncompatiblePasswordSpec(final String algorithm, final String passwordSpec) { + final InvalidKeySpecException result = new InvalidKeySpecException(String.format(getLoggingLocale(), invalidKeySpecUnknownAlgorithmOrIncompatiblePasswordSpec$str(), algorithm, passwordSpec)); + _copyStackTraceMinusOne(result); + return result; + } + protected String invalidKeyUnknownUnknownPasswordTypeOrAlgorithm$str() { + return "ELY08027: Unknown password type or algorithm"; + } + @Override + public final InvalidKeyException invalidKeyUnknownUnknownPasswordTypeOrAlgorithm() { + final InvalidKeyException result = new InvalidKeyException(String.format(getLoggingLocale(), invalidKeyUnknownUnknownPasswordTypeOrAlgorithm$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String noSuchAlgorithmInvalidAlgorithm$str() { + return "ELY08028: Invalid algorithm \"%s\""; + } + @Override + public final NoSuchAlgorithmException noSuchAlgorithmInvalidAlgorithm(final String algorithm) { + final NoSuchAlgorithmException result = new NoSuchAlgorithmException(String.format(getLoggingLocale(), noSuchAlgorithmInvalidAlgorithm$str(), algorithm)); + _copyStackTraceMinusOne(result); + return result; + } + protected String invalidAlgorithmParameterSpecification$str() { + return "ELY21000: Invalid algorithm parameter specification"; + } + @Override + public final InvalidAlgorithmParameterException invalidAlgorithmParameterSpecification() { + final InvalidAlgorithmParameterException result = new InvalidAlgorithmParameterException(String.format(getLoggingLocale(), invalidAlgorithmParameterSpecification$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String invalidSequenceNumberAlgorithmParameter$str() { + return "ELY21001: Invalid sequence number algorithm parameter \"%s\""; + } + @Override + public final InvalidAlgorithmParameterException invalidSequenceNumberAlgorithmParameter(final int sequenceNumber) { + final InvalidAlgorithmParameterException result = new InvalidAlgorithmParameterException(String.format(getLoggingLocale(), invalidSequenceNumberAlgorithmParameter$str(), sequenceNumber)); + _copyStackTraceMinusOne(result); + return result; + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/password/impl/IteratedPasswordAlgorithmParametersSpiImpl.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/password/impl/IteratedPasswordAlgorithmParametersSpiImpl.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/password/impl/IteratedPasswordAlgorithmParametersSpiImpl.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,53 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2016 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.password.impl; + +import java.security.AlgorithmParametersSpi; + +import org.wildfly.security.asn1.ASN1Decoder; +import org.wildfly.security.asn1.ASN1Encoder; +import org.wildfly.security.password.spec.IteratedPasswordAlgorithmSpec; +import org.wildfly.security.util.AbstractAlgorithmParametersSpiImpl; + +/** + * An implementation of the {@link AlgorithmParametersSpi} SPI, in order to support encoding and decoding of + * password algorithm parameters. + * + * @author David M. Lloyd + */ +public final class IteratedPasswordAlgorithmParametersSpiImpl extends AbstractAlgorithmParametersSpiImpl { + + /** + * Construct a new instance. + */ + public IteratedPasswordAlgorithmParametersSpiImpl() { + } + + protected Class getParameterType() { + return IteratedPasswordAlgorithmSpec.class; + } + + protected void engineEncode(final ASN1Encoder encoder, final IteratedPasswordAlgorithmSpec parameterSpec) { + encoder.encodeInteger(parameterSpec.getIterationCount()); + } + + protected IteratedPasswordAlgorithmSpec engineDecode(final ASN1Decoder decoder) { + return new IteratedPasswordAlgorithmSpec(decoder.decodeInteger().intValue()); + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/password/impl/IteratedSaltedPasswordAlgorithmParametersSpiImpl.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/password/impl/IteratedSaltedPasswordAlgorithmParametersSpiImpl.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/password/impl/IteratedSaltedPasswordAlgorithmParametersSpiImpl.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,60 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2016 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.password.impl; + +import java.security.AlgorithmParametersSpi; + +import org.wildfly.security.asn1.ASN1Decoder; +import org.wildfly.security.asn1.ASN1Encoder; +import org.wildfly.security.password.spec.IteratedSaltedPasswordAlgorithmSpec; +import org.wildfly.security.util.AbstractAlgorithmParametersSpiImpl; + +/** + * An implementation of the {@link AlgorithmParametersSpi} SPI, in order to support encoding and decoding of + * password algorithm parameters. + * + * @author David M. Lloyd + */ +public final class IteratedSaltedPasswordAlgorithmParametersSpiImpl extends AbstractAlgorithmParametersSpiImpl { + + /** + * Construct a new instance. + */ + public IteratedSaltedPasswordAlgorithmParametersSpiImpl() { + } + + protected Class getParameterType() { + return IteratedSaltedPasswordAlgorithmSpec.class; + } + + protected void engineEncode(final ASN1Encoder encoder, final IteratedSaltedPasswordAlgorithmSpec parameterSpec) { + encoder.startSequence(); + encoder.encodeInteger(parameterSpec.getIterationCount()); + encoder.encodeOctetString(parameterSpec.getSalt()); + encoder.endSequence(); + } + + protected IteratedSaltedPasswordAlgorithmSpec engineDecode(final ASN1Decoder decoder) { + decoder.startSequence(); + final int iterationCount = decoder.decodeInteger().intValue(); + final byte[] salt = decoder.decodeOctetString(); + decoder.endSequence(); + return new IteratedSaltedPasswordAlgorithmSpec(iterationCount, salt); + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/password/impl/MaskedPasswordAlgorithmParametersSpiImpl.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/password/impl/MaskedPasswordAlgorithmParametersSpiImpl.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/password/impl/MaskedPasswordAlgorithmParametersSpiImpl.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,66 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2016 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.password.impl; + +import java.security.AlgorithmParametersSpi; + +import org.wildfly.security.asn1.ASN1Decoder; +import org.wildfly.security.asn1.ASN1Encoder; +import org.wildfly.security.password.spec.MaskedPasswordAlgorithmSpec; +import org.wildfly.security.util.AbstractAlgorithmParametersSpiImpl; + +/** + * An implementation of the {@link AlgorithmParametersSpi} SPI, in order to support encoding and decoding of + * password algorithm parameters. + * + * @author David M. Lloyd + */ +public final class MaskedPasswordAlgorithmParametersSpiImpl extends AbstractAlgorithmParametersSpiImpl { + + /** + * Construct a new instance. + */ + public MaskedPasswordAlgorithmParametersSpiImpl() { + } + + protected Class getParameterType() { + return MaskedPasswordAlgorithmSpec.class; + } + + protected void engineEncode(final ASN1Encoder encoder, final MaskedPasswordAlgorithmSpec parameterSpec) { + encoder.startSequence(); + encoder.encodeOctetString(new String(parameterSpec.getInitialKeyMaterial())); + encoder.encodeInteger(parameterSpec.getIterationCount()); + encoder.encodeOctetString(parameterSpec.getSalt()); + if (parameterSpec.getInitializationVector() != null) { + encoder.encodeOctetString(parameterSpec.getInitializationVector()); + } + encoder.endSequence(); + } + + protected MaskedPasswordAlgorithmSpec engineDecode(final ASN1Decoder decoder) { + decoder.startSequence(); + final char[] initialKeyMaterial = decoder.decodeOctetStringAsString().toCharArray(); + final int iterationCount = decoder.decodeInteger().intValue(); + final byte[] salt = decoder.decodeOctetString(); + final byte[] initializationVector = decoder.hasNextElement() ? decoder.decodeOctetString() : null; + decoder.endSequence(); + return new MaskedPasswordAlgorithmSpec(initialKeyMaterial, iterationCount, salt, initializationVector); + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/password/impl/MaskedPasswordImpl.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/password/impl/MaskedPasswordImpl.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/password/impl/MaskedPasswordImpl.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,232 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2016 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.password.impl; + +import static org.wildfly.common.math.HashMath.multiHashOrdered; + +import java.io.NotSerializableException; +import java.io.ObjectInputStream; +import java.nio.charset.Charset; +import java.security.GeneralSecurityException; +import java.security.InvalidKeyException; +import java.security.MessageDigest; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.KeySpec; +import java.util.Arrays; + +import javax.crypto.Cipher; +import javax.crypto.SecretKey; +import javax.crypto.SecretKeyFactory; +import javax.crypto.spec.PBEKeySpec; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.PBEParameterSpec; + +import org.wildfly.common.Assert; +import org.wildfly.common.iteration.ByteIterator; +import org.wildfly.common.iteration.CodePointIterator; +import org.wildfly.security.password.interfaces.MaskedPassword; +import org.wildfly.security.password.spec.ClearPasswordSpec; +import org.wildfly.security.password.spec.IteratedPasswordAlgorithmSpec; +import org.wildfly.security.password.spec.IteratedSaltedPasswordAlgorithmSpec; +import org.wildfly.security.password.spec.MaskedPasswordAlgorithmSpec; +import org.wildfly.security.password.spec.MaskedPasswordSpec; +import org.wildfly.security.password.spec.SaltedPasswordAlgorithmSpec; + +final class MaskedPasswordImpl extends AbstractPasswordImpl implements MaskedPassword { + private static final long serialVersionUID = - 4107081797004604247L; + + @SuppressWarnings("SpellCheckingInspection") + private static final char[] DEFAULT_PBE_KEY = "somearbitrarycrazystringthatdoesnotmatter".toCharArray(); + // Required size for many schemes according to RFC 2898 + private static final int DEFAULT_SALT_SIZE = 8; + // Recommended minimum by RFC 2898 + private static final int DEFAULT_ITERATION_COUNT = 1000; + + private final String algorithm; + private final char[] initialKeyMaterial; + private final int iterationCount; + private final byte[] salt; + private final byte[] maskedPasswordBytes; + private final byte[] initializationVector; + + + private MaskedPasswordImpl(final String algorithm, final char[] initialKeyMaterial, final int iterationCount, final byte[] salt, final byte[] initializationVector, final byte[] maskedPasswordBytes) throws InvalidKeySpecException { + Assert.checkMinimumParameter("iterationCount", 1, iterationCount); + this.algorithm = algorithm; + this.initialKeyMaterial = initialKeyMaterial; + this.iterationCount = iterationCount; + this.salt = salt; + this.maskedPasswordBytes = maskedPasswordBytes; + this.initializationVector = initializationVector; + unmask(); //preform an unmask to validate parameters + } + + private MaskedPasswordImpl(final String algorithm, final char[] initialKeyMaterial, final int iterationCount, final byte[] salt, final char[] chars) throws InvalidKeySpecException { + Assert.checkMinimumParameter("iterationCount", 1, iterationCount); + this.algorithm = algorithm; + this.initialKeyMaterial = initialKeyMaterial; + this.iterationCount = iterationCount; + this.salt = salt; + + try { + Cipher cipher = getCipher(Cipher.ENCRYPT_MODE, null); + this.maskedPasswordBytes = cipher.doFinal(CodePointIterator.ofChars(chars).asUtf8().drain()); + this.initializationVector = cipher.getIV(); + } catch (GeneralSecurityException e) { + throw new InvalidKeySpecException(e); + } + } + + MaskedPasswordImpl(final String algorithm, final MaskedPasswordSpec passwordSpec) throws InvalidKeySpecException { + this(algorithm, passwordSpec.getInitialKeyMaterial().clone(), passwordSpec.getIterationCount(), passwordSpec.getSalt().clone(), passwordSpec.getInitializationVector() == null ? null : passwordSpec.getInitializationVector().clone(), passwordSpec.getMaskedPasswordBytes().clone()); + } + + MaskedPasswordImpl(final String algorithm, final char[] clearPassword) throws InvalidKeySpecException { + this(algorithm, DEFAULT_PBE_KEY, DEFAULT_ITERATION_COUNT, PasswordUtil.generateRandomSalt(DEFAULT_SALT_SIZE), clearPassword); + } + + MaskedPasswordImpl(final String algorithm, final char[] clearPassword, final MaskedPasswordAlgorithmSpec parameterSpec) throws InvalidKeySpecException { + this(algorithm, parameterSpec.getInitialKeyMaterial().clone(), parameterSpec.getIterationCount(), parameterSpec.getSalt().clone(), clearPassword); + } + + MaskedPasswordImpl(final String algorithm, final char[] clearPassword, final IteratedSaltedPasswordAlgorithmSpec parameterSpec) throws InvalidKeySpecException { + this(algorithm, DEFAULT_PBE_KEY, parameterSpec.getIterationCount(), parameterSpec.getSalt().clone(), clearPassword); + } + + MaskedPasswordImpl(final String algorithm, final char[] clearPassword, final SaltedPasswordAlgorithmSpec parameterSpec) throws InvalidKeySpecException { + this(algorithm, DEFAULT_PBE_KEY, DEFAULT_ITERATION_COUNT, parameterSpec.getSalt().clone(), clearPassword); + } + + MaskedPasswordImpl(final String algorithm, final char[] clearPassword, final IteratedPasswordAlgorithmSpec parameterSpec) throws InvalidKeySpecException { + this(algorithm, DEFAULT_PBE_KEY, parameterSpec.getIterationCount(), PasswordUtil.generateRandomSalt(DEFAULT_SALT_SIZE), clearPassword); + } + + MaskedPasswordImpl(final String algorithm, final ClearPasswordSpec keySpec) throws InvalidKeySpecException { + this(algorithm, keySpec.getEncodedPassword()); + } + + MaskedPasswordImpl(final MaskedPassword password) throws InvalidKeySpecException { + this(password.getAlgorithm(), password.getInitialKeyMaterial().clone(), password.getIterationCount(), password.getSalt().clone(), password.getInitializationVector() == null ? null : password.getInitializationVector().clone(), password.getMaskedPasswordBytes().clone()); + } + + public String getAlgorithm() { + return algorithm; + } + + public char[] getInitialKeyMaterial() { + return initialKeyMaterial.clone(); + } + + public int getIterationCount() { + return iterationCount; + } + + public byte[] getSalt() { + return salt.clone(); + } + + public byte[] getMaskedPasswordBytes() { + return maskedPasswordBytes.clone(); + } + + public byte[] getInitializationVector() { + return initializationVector == null ? null : initializationVector.clone(); + } + + S getKeySpec(final Class keySpecType) throws InvalidKeySpecException { + if (keySpecType.isAssignableFrom(MaskedPasswordSpec.class)) { + return keySpecType.cast(new MaskedPasswordSpec(initialKeyMaterial.clone(), iterationCount, salt.clone(), maskedPasswordBytes.clone())); + } else if (keySpecType.isAssignableFrom(ClearPasswordSpec.class)) { + return keySpecType.cast(new ClearPasswordSpec(unmask())); + } else { + throw new InvalidKeySpecException(); + } + } + + boolean verify(final char[] guess) throws InvalidKeyException { + try { + return Arrays.equals(guess, unmask()); + } catch (InvalidKeySpecException e) { + throw new InvalidKeyException(e); + } + } + + @Override + boolean verify(char[] guess, Charset hashCharset) throws InvalidKeyException { + return verify(guess); + } + + boolean convertibleTo(final Class keySpecType) { + return keySpecType.isAssignableFrom(MaskedPasswordSpec.class) || keySpecType.isAssignableFrom(ClearPasswordSpec.class); + } + + public MaskedPasswordImpl clone() { + return this; + } + + private char[] unmask() throws InvalidKeySpecException { + try { + final Cipher cipher = getCipher(Cipher.DECRYPT_MODE, initializationVector); + return ByteIterator.ofBytes(cipher.doFinal(maskedPasswordBytes)).asUtf8String().drainToString().toCharArray(); + } catch (GeneralSecurityException e) { + throw new InvalidKeySpecException(e); + } + } + + private Cipher getCipher(int cipherMode, byte[] initializationVector) throws GeneralSecurityException { + final String pbeName = MaskedPassword.getPBEName(algorithm); + Assert.assertNotNull(pbeName); + final SecretKeyFactory factory = SecretKeyFactory.getInstance(pbeName); + final Cipher cipher = Cipher.getInstance(pbeName); + + final AlgorithmParameterSpec parameterSpec = initializationVector == null ? + null : new IvParameterSpec(initializationVector); + + // Create the PBE secret key + final PBEParameterSpec cipherSpec = new PBEParameterSpec(salt, iterationCount, parameterSpec); + final PBEKeySpec keySpec = new PBEKeySpec(initialKeyMaterial); + final SecretKey cipherKey = factory.generateSecret(keySpec); + + cipher.init(cipherMode, cipherKey, cipherSpec); + return cipher; + } + + public int hashCode() { + return multiHashOrdered(multiHashOrdered(multiHashOrdered(multiHashOrdered(multiHashOrdered(Arrays.hashCode(initialKeyMaterial), Arrays.hashCode(salt)), Arrays.hashCode(maskedPasswordBytes)), iterationCount), Arrays.hashCode(initializationVector)), algorithm.hashCode()); + } + + public boolean equals(final Object obj) { + if (! (obj instanceof MaskedPasswordImpl)) { + return false; + } + MaskedPasswordImpl other = (MaskedPasswordImpl) obj; + return iterationCount == other.iterationCount && Arrays.equals(initialKeyMaterial, other.initialKeyMaterial) + && MessageDigest.isEqual(salt, other.salt) && MessageDigest.isEqual(maskedPasswordBytes, other.maskedPasswordBytes) + && MessageDigest.isEqual(initializationVector, other.initializationVector) && algorithm.equals(other.algorithm); + } + + Object writeReplace() { + return MaskedPassword.createRaw(algorithm, initialKeyMaterial.clone(), iterationCount, salt.clone(), maskedPasswordBytes.clone()); + } + + private void readObject(ObjectInputStream ignored) throws NotSerializableException { + throw new NotSerializableException(); + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/password/impl/OneTimePasswordAlgorithmParametersSpiImpl.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/password/impl/OneTimePasswordAlgorithmParametersSpiImpl.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/password/impl/OneTimePasswordAlgorithmParametersSpiImpl.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,62 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2016 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.password.impl; + +import java.security.AlgorithmParametersSpi; + +import org.wildfly.security.asn1.ASN1Decoder; +import org.wildfly.security.asn1.ASN1Encoder; +import org.wildfly.security.password.spec.OneTimePasswordAlgorithmSpec; +import org.wildfly.security.util.AbstractAlgorithmParametersSpiImpl; + +/** + * An implementation of the {@link AlgorithmParametersSpi} SPI, in order to support encoding and decoding of + * password algorithm parameters. + * + * @author David M. Lloyd + */ +public final class OneTimePasswordAlgorithmParametersSpiImpl extends AbstractAlgorithmParametersSpiImpl { + + /** + * Construct a new instance. + */ + public OneTimePasswordAlgorithmParametersSpiImpl() { + } + + protected Class getParameterType() { + return OneTimePasswordAlgorithmSpec.class; + } + + protected void engineEncode(final ASN1Encoder encoder, final OneTimePasswordAlgorithmSpec parameterSpec) { + encoder.startSequence(); + encoder.encodeOctetString(parameterSpec.getAlgorithm()); + encoder.encodeIA5String(parameterSpec.getSeed()); + encoder.encodeInteger(parameterSpec.getSequenceNumber()); + encoder.endSequence(); + } + + protected OneTimePasswordAlgorithmSpec engineDecode(final ASN1Decoder decoder) { + decoder.startSequence(); + final String algorithm = decoder.decodeOctetStringAsString(); + final String seed = decoder.decodeIA5String(); + final int sequenceNumber = decoder.decodeInteger().intValue(); + decoder.endSequence(); + return new OneTimePasswordAlgorithmSpec(algorithm, seed, sequenceNumber); + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/password/impl/OneTimePasswordImpl.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/password/impl/OneTimePasswordImpl.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/password/impl/OneTimePasswordImpl.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,260 @@ +/* + * 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.password.impl; + +import static org.wildfly.common.math.HashMath.multiHashOrdered; +import static org.wildfly.security.password.impl.ElytronMessages.log; + +import java.io.NotSerializableException; +import java.io.ObjectInputStream; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.KeySpec; +import java.util.Arrays; +import java.util.Locale; + +import org.wildfly.common.bytes.ByteStringBuilder; +import org.wildfly.security.password.Password; +import org.wildfly.security.password.interfaces.OneTimePassword; +import org.wildfly.security.password.spec.OneTimePasswordAlgorithmSpec; +import org.wildfly.security.password.spec.OneTimePasswordSpec; + +import javax.security.sasl.SaslException; + +/** + * A {@code Password} implementation for {@link OneTimePassword}. + * + * @author Farah Juma + */ +class OneTimePasswordImpl extends AbstractPasswordImpl implements OneTimePassword { + + private static final long serialVersionUID = 5524179164918986449L; + + private final String algorithm; + private final byte[] hash; + private final String seed; + private final int sequenceNumber; + + OneTimePasswordImpl(final String algorithm, final byte[] hash, final String seed, final int sequenceNumber) { + this.algorithm = algorithm; + this.hash = hash; + this.seed = seed; + this.sequenceNumber = sequenceNumber; + } + + OneTimePasswordImpl(final OneTimePassword password) { + this(password.getAlgorithm(), password.getHash().clone(), password.getSeed(), password.getSequenceNumber()); + } + + OneTimePasswordImpl(final String algorithm, final OneTimePasswordSpec spec) { + this(algorithm, spec.getHash().clone(), spec.getSeed(), spec.getSequenceNumber()); + } + + OneTimePasswordImpl(final String algorithm, final char[] password, final OneTimePasswordAlgorithmSpec spec) throws SaslException { + this(algorithm, + generateOTP(algorithm, + getNormalizedPasswordBytes(password), + spec.getSeed().toLowerCase(Locale.ENGLISH), + spec.getSequenceNumber() + ), + spec.getSeed(), + spec.getSequenceNumber()); + } + + @Override + public String getAlgorithm() { + return this.algorithm; + } + + @Override + public byte[] getHash() { + return hash.clone(); + } + + @Override + public String getSeed() { + return seed; + } + + @Override + public int getSequenceNumber() { + return sequenceNumber; + } + + @Override + S getKeySpec(Class keySpecType) throws InvalidKeySpecException { + if (keySpecType.isAssignableFrom(OneTimePasswordSpec.class)) { + return keySpecType.cast(new OneTimePasswordSpec(hash.clone(), seed, sequenceNumber)); + } + throw new InvalidKeySpecException(); + } + + @Override + Password translate(final AlgorithmParameterSpec parameterSpec) throws InvalidKeyException, InvalidAlgorithmParameterException { + if (parameterSpec instanceof OneTimePasswordAlgorithmSpec) { + OneTimePasswordAlgorithmSpec updateSpec = (OneTimePasswordAlgorithmSpec) parameterSpec; + int updateSequence = updateSpec.getSequenceNumber(); + if (updateSequence < this.sequenceNumber) { + throw log.invalidSequenceNumberAlgorithmParameter(updateSequence); + } + if (updateSequence == this.sequenceNumber) { + return this; + } + byte[] hash = this.hash.clone(); + try { + hash = computeHash(hash, algorithm, getMessageDigest(algorithm), this.sequenceNumber, updateSequence); + } catch (NoSuchAlgorithmException e) { + throw log.invalidKeyNoSuchMessageDigestAlgorithm(algorithm); + } + return new OneTimePasswordImpl(algorithm, hash, seed, updateSequence); + } + throw log.invalidAlgorithmParameterSpecification(); + } + + @Override + boolean verify(char[] guess) throws InvalidKeyException { + // The OTP SASL mechanism handles this (this involves updating the stored password) + throw new InvalidKeyException(); + } + + /** + * Generate a 64-bit OTP as specified in RFC 2289. + * + * @param algorithm the OTP algorithm, must be either "otp-md5" or "otp-sha1" + * @param passPhrase the pass phrase, as a byte array + * @param seed the seed + * @param sequenceNumber the number of times the hash function will be applied + * @return the 64-bit OTP hash + * @throws SaslException if the given OTP algorithm is invalid + */ + private static byte[] generateOTP(String algorithm, byte[] passPhrase, String seed, int sequenceNumber) throws SaslException { + final MessageDigest messageDigest; + try { + messageDigest = getMessageDigest(algorithm); + } catch (NoSuchAlgorithmException e) { + throw log.mechInvalidOTPAlgorithm(algorithm); + } + + // Initial step + final ByteStringBuilder seedAndPassPhrase = new ByteStringBuilder(); + seedAndPassPhrase.append(seed); + seedAndPassPhrase.append(passPhrase); + byte[] hash = hashAndFold(algorithm, messageDigest, seedAndPassPhrase.toArray()); + + // Computation step + hash = computeHash(hash, algorithm, messageDigest, 0, sequenceNumber); + return hash; + } + + static byte[] computeHash(final byte[] hash, final String algorithm, final MessageDigest messageDigest, final int start, final int end) { + byte[] current = hash; + for (int i = start; i < end; i++) { + messageDigest.reset(); + current = hashAndFold(algorithm, messageDigest, current); + } + return current; + } + + private static MessageDigest getMessageDigest(String algorithm) throws NoSuchAlgorithmException { + switch (algorithm) { + case ALGORITHM_OTP_MD5: + return MessageDigest.getInstance("MD5"); + case ALGORITHM_OTP_SHA1: + return MessageDigest.getInstance("SHA-1"); + case ALGORITHM_OTP_SHA_256: + return MessageDigest.getInstance("SHA-256"); + case ALGORITHM_OTP_SHA_384: + return MessageDigest.getInstance("SHA-384"); + case ALGORITHM_OTP_SHA_512: + return MessageDigest.getInstance("SHA-512"); + default: + throw new NoSuchAlgorithmException(); + } + } + + /** + * Pass the given input through a hash function and fold the result to 64 bits. + * + * @param algorithm the OTP algorithm, must be either "otp-md5" or "otp-sha1" + * @param messageDigest the {@code MessageDigest} to use when generating the hash + * @param input the data to hash + * @return the folded hash + */ + private static byte[] hashAndFold(String algorithm, MessageDigest messageDigest, byte[] input) { + messageDigest.update(input); + byte[] result = messageDigest.digest(); + byte[] hash = new byte[OTP_HASH_SIZE]; + + // Fold the result (either 128 bits for MD5 or 160 bits for SHA-1) to 64 bits + for (int i = OTP_HASH_SIZE; i < result.length; i++) { + result[i % OTP_HASH_SIZE] ^= result[i]; + } + System.arraycopy(result, 0, hash, 0, OTP_HASH_SIZE); + + if (algorithm.equals(ALGORITHM_OTP_SHA1)) { + reverse(hash, 0, 4); + reverse(hash, 4, 4); + } + return hash; + } + + private static void reverse(byte[] bytes, int offset, int length) { + byte tmp; + int mid = (length / 2) + offset; + for (int i = offset, j = offset + length - 1; i < mid; i++, j--) { + tmp = bytes[i]; + bytes[i] = bytes[j]; + bytes[j] = tmp; + } + } + + @Override + boolean convertibleTo(Class keySpecType) { + return keySpecType.isAssignableFrom(OneTimePasswordSpec.class); + } + + public int hashCode() { + return multiHashOrdered(multiHashOrdered(multiHashOrdered(Arrays.hashCode(hash),seed.hashCode()), sequenceNumber), algorithm.hashCode()); + } + + public boolean equals(final Object obj) { + if (! (obj instanceof OneTimePasswordImpl)) { + return false; + } + OneTimePasswordImpl other = (OneTimePasswordImpl) obj; + return sequenceNumber == other.sequenceNumber && algorithm.equals(other.algorithm) && Arrays.equals(hash, other.hash) && seed.equals(other.seed); + } + + private void readObject(ObjectInputStream ignored) throws NotSerializableException { + throw new NotSerializableException(); + } + + Object writeReplace() { + return OneTimePassword.createRaw(algorithm, hash, seed, sequenceNumber); + } + + public OneTimePasswordImpl clone() { + return this; + } + +} Index: 3rdParty_sources/elytron/org/wildfly/security/password/impl/PasswordFactorySpiImpl.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/password/impl/PasswordFactorySpiImpl.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/password/impl/PasswordFactorySpiImpl.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,818 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2013 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.password.impl; + +import static org.wildfly.security.password.impl.ElytronMessages.log; +import static org.wildfly.security.password.interfaces.BCryptPassword.*; +import static org.wildfly.security.password.interfaces.BSDUnixDESCryptPassword.*; +import static org.wildfly.security.password.interfaces.ClearPassword.*; +import static org.wildfly.security.password.interfaces.DigestPassword.*; +import static org.wildfly.security.password.interfaces.OneTimePassword.*; +import static org.wildfly.security.password.interfaces.ScramDigestPassword.*; +import static org.wildfly.security.password.interfaces.SunUnixMD5CryptPassword.*; +import static org.wildfly.security.password.interfaces.SimpleDigestPassword.*; +import static org.wildfly.security.password.interfaces.SaltedSimpleDigestPassword.*; +import static org.wildfly.security.password.interfaces.UnixSHACryptPassword.*; +import static org.wildfly.security.password.interfaces.UnixMD5CryptPassword.*; +import static org.wildfly.security.password.interfaces.UnixDESCryptPassword.*; + +import java.nio.charset.Charset; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.InvalidParameterSpecException; +import java.security.spec.KeySpec; +import javax.security.sasl.SaslException; +import org.wildfly.security.password.Password; +import org.wildfly.security.password.PasswordFactorySpi; +import org.wildfly.security.password.interfaces.BCryptPassword; +import org.wildfly.security.password.interfaces.BSDUnixDESCryptPassword; +import org.wildfly.security.password.interfaces.ClearPassword; +import org.wildfly.security.password.interfaces.DigestPassword; +import org.wildfly.security.password.interfaces.MaskedPassword; +import org.wildfly.security.password.interfaces.OneTimePassword; +import org.wildfly.security.password.interfaces.SaltedSimpleDigestPassword; +import org.wildfly.security.password.interfaces.ScramDigestPassword; +import org.wildfly.security.password.interfaces.SimpleDigestPassword; +import org.wildfly.security.password.interfaces.SunUnixMD5CryptPassword; +import org.wildfly.security.password.interfaces.UnixDESCryptPassword; +import org.wildfly.security.password.interfaces.UnixMD5CryptPassword; +import org.wildfly.security.password.interfaces.UnixSHACryptPassword; +import org.wildfly.security.password.spec.ClearPasswordSpec; +import org.wildfly.security.password.spec.DigestPasswordSpec; +import org.wildfly.security.password.spec.EncryptablePasswordSpec; +import org.wildfly.security.password.spec.HashPasswordSpec; +import org.wildfly.security.password.spec.IteratedPasswordAlgorithmSpec; +import org.wildfly.security.password.spec.IteratedSaltedHashPasswordSpec; +import org.wildfly.security.password.spec.IteratedSaltedPasswordAlgorithmSpec; +import org.wildfly.security.password.spec.MaskedPasswordAlgorithmSpec; +import org.wildfly.security.password.spec.MaskedPasswordSpec; +import org.wildfly.security.password.spec.OneTimePasswordAlgorithmSpec; +import org.wildfly.security.password.spec.OneTimePasswordSpec; +import org.wildfly.security.password.spec.SaltedHashPasswordSpec; +import org.wildfly.security.password.spec.SaltedPasswordAlgorithmSpec; + +/** + * The Elytron-provided password factory SPI implementation, which supports all the provided password types. + */ +public final class PasswordFactorySpiImpl extends PasswordFactorySpi { + + @Override + protected Password engineGeneratePassword(final String algorithm, final KeySpec keySpec) throws InvalidKeySpecException { + /* + * When adding or removing an algorithm ensure that the registrations in 'WildFlyElytronProvider' are also + * updated. + */ + + switch (algorithm) { + case ALGORITHM_CLEAR: { + if (keySpec instanceof ClearPasswordSpec) { + return new ClearPasswordImpl(((ClearPasswordSpec) keySpec).getEncodedPassword().clone()); + } else if (keySpec instanceof EncryptablePasswordSpec) { + return new ClearPasswordImpl(((EncryptablePasswordSpec) keySpec).getPassword().clone()); + } else { + break; + } + } + case ALGORITHM_BCRYPT: { + if (keySpec instanceof IteratedSaltedHashPasswordSpec) { + try { + return new BCryptPasswordImpl((IteratedSaltedHashPasswordSpec) keySpec); + } catch (IllegalArgumentException | NullPointerException e) { + throw new InvalidKeySpecException(e); + } + } else if (keySpec instanceof SaltedHashPasswordSpec) { + try { + return new BCryptPasswordImpl((SaltedHashPasswordSpec) keySpec); + } catch (IllegalArgumentException | NullPointerException e) { + throw new InvalidKeySpecException(e); + } + } else if (keySpec instanceof ClearPasswordSpec) { + try { + return new BCryptPasswordImpl((ClearPasswordSpec) keySpec); + } catch (IllegalArgumentException | NullPointerException e) { + throw new InvalidKeySpecException(e); + } + } else if (keySpec instanceof EncryptablePasswordSpec) { + try { + final EncryptablePasswordSpec encryptableSpec = (EncryptablePasswordSpec) keySpec; + final AlgorithmParameterSpec parameterSpec = encryptableSpec.getAlgorithmParameterSpec(); + if (parameterSpec == null) { + return new BCryptPasswordImpl(encryptableSpec.getPassword(), encryptableSpec.getHashCharset()); + } else if (parameterSpec instanceof SaltedPasswordAlgorithmSpec) { + return new BCryptPasswordImpl(encryptableSpec.getPassword(), (SaltedPasswordAlgorithmSpec) parameterSpec, + encryptableSpec.getHashCharset()); + } else if (parameterSpec instanceof IteratedSaltedPasswordAlgorithmSpec) { + return new BCryptPasswordImpl(encryptableSpec.getPassword(), (IteratedSaltedPasswordAlgorithmSpec) parameterSpec, + encryptableSpec.getHashCharset()); + } else if (parameterSpec instanceof IteratedPasswordAlgorithmSpec) { + return new BCryptPasswordImpl(encryptableSpec.getPassword(), (IteratedPasswordAlgorithmSpec) parameterSpec, + encryptableSpec.getHashCharset()); + } else { + break; + } + } catch (IllegalArgumentException | NullPointerException e) { + throw new InvalidKeySpecException(e); + } + } else { + break; + } + } + case ALGORITHM_CRYPT_MD5: { + if (keySpec instanceof SaltedHashPasswordSpec) { + try { + return new UnixMD5CryptPasswordImpl((SaltedHashPasswordSpec) keySpec); + } catch (IllegalArgumentException | NullPointerException e) { + throw new InvalidKeySpecException(e); + } + } else if (keySpec instanceof ClearPasswordSpec) { + try { + return new UnixMD5CryptPasswordImpl((ClearPasswordSpec) keySpec); + } catch (IllegalArgumentException | NullPointerException | NoSuchAlgorithmException e) { + throw new InvalidKeySpecException(e); + } + } else if (keySpec instanceof EncryptablePasswordSpec) { + try { + final EncryptablePasswordSpec encryptableSpec = (EncryptablePasswordSpec) keySpec; + final AlgorithmParameterSpec parameterSpec = encryptableSpec.getAlgorithmParameterSpec(); + if (parameterSpec == null) { + return new UnixMD5CryptPasswordImpl(encryptableSpec.getPassword(), encryptableSpec.getHashCharset()); + } else if (parameterSpec instanceof SaltedPasswordAlgorithmSpec) { + return new UnixMD5CryptPasswordImpl(encryptableSpec.getPassword(), (SaltedPasswordAlgorithmSpec) parameterSpec, + encryptableSpec.getHashCharset()); + } else { + break; + } + } catch (IllegalArgumentException | NoSuchAlgorithmException e) { + throw new InvalidKeySpecException(e); + } + } else { + break; + } + } + case ALGORITHM_SUN_CRYPT_MD5: + case ALGORITHM_SUN_CRYPT_MD5_BARE_SALT: { + if (keySpec instanceof IteratedSaltedHashPasswordSpec) { + try { + return new SunUnixMD5CryptPasswordImpl(algorithm, (IteratedSaltedHashPasswordSpec) keySpec); + } catch (IllegalArgumentException | NullPointerException e) { + throw new InvalidKeySpecException(e); + } + } else if (keySpec instanceof SaltedHashPasswordSpec) { + try { + return new SunUnixMD5CryptPasswordImpl(algorithm, (SaltedHashPasswordSpec) keySpec); + } catch (IllegalArgumentException | NullPointerException e) { + throw new InvalidKeySpecException(e); + } + } else if (keySpec instanceof ClearPasswordSpec) { + try { + return new SunUnixMD5CryptPasswordImpl((ClearPasswordSpec) keySpec); + } catch (IllegalArgumentException | NullPointerException | NoSuchAlgorithmException e) { + throw new InvalidKeySpecException(e); + } + } else if (keySpec instanceof EncryptablePasswordSpec) { + try { + final EncryptablePasswordSpec encryptableSpec = (EncryptablePasswordSpec) keySpec; + final AlgorithmParameterSpec parameterSpec = encryptableSpec.getAlgorithmParameterSpec(); + if (parameterSpec == null) { + return new SunUnixMD5CryptPasswordImpl(algorithm, encryptableSpec.getPassword(), encryptableSpec.getHashCharset()); + } else if (parameterSpec instanceof SaltedPasswordAlgorithmSpec) { + return new SunUnixMD5CryptPasswordImpl(algorithm, encryptableSpec.getPassword(), (SaltedPasswordAlgorithmSpec) parameterSpec, + encryptableSpec.getHashCharset()); + } else if (parameterSpec instanceof IteratedSaltedPasswordAlgorithmSpec) { + return new SunUnixMD5CryptPasswordImpl(algorithm, encryptableSpec.getPassword(), (IteratedSaltedPasswordAlgorithmSpec) parameterSpec, + encryptableSpec.getHashCharset()); + } else if (parameterSpec instanceof IteratedPasswordAlgorithmSpec) { + return new SunUnixMD5CryptPasswordImpl(algorithm, encryptableSpec.getPassword(), (IteratedPasswordAlgorithmSpec) parameterSpec, + encryptableSpec.getHashCharset()); + } else { + break; + } + } catch (IllegalArgumentException | NullPointerException | NoSuchAlgorithmException e) { + throw new InvalidKeySpecException(e); + } + } else { + break; + } + } + case ALGORITHM_CRYPT_SHA_256: + case ALGORITHM_CRYPT_SHA_512: { + if (keySpec instanceof IteratedSaltedHashPasswordSpec) { + try { + return new UnixSHACryptPasswordImpl(algorithm, (IteratedSaltedHashPasswordSpec) keySpec); + } catch (IllegalArgumentException | NullPointerException e) { + throw new InvalidKeySpecException(e); + } + } else if (keySpec instanceof SaltedHashPasswordSpec) { + try { + return new UnixSHACryptPasswordImpl(algorithm, (SaltedHashPasswordSpec) keySpec); + } catch (IllegalArgumentException | NullPointerException e) { + throw new InvalidKeySpecException(e); + } + } else if (keySpec instanceof ClearPasswordSpec) { + try { + return new UnixSHACryptPasswordImpl(algorithm, (ClearPasswordSpec) keySpec); + } catch (IllegalArgumentException | NullPointerException | NoSuchAlgorithmException e) { + throw new InvalidKeySpecException(e); + } + } else if (keySpec instanceof EncryptablePasswordSpec) { + try { + final EncryptablePasswordSpec encryptableSpec = (EncryptablePasswordSpec) keySpec; + final AlgorithmParameterSpec parameterSpec = encryptableSpec.getAlgorithmParameterSpec(); + if (parameterSpec == null) { + return new UnixSHACryptPasswordImpl(algorithm, encryptableSpec.getPassword(), encryptableSpec.getHashCharset()); + } else if (parameterSpec instanceof IteratedPasswordAlgorithmSpec) { + return new UnixSHACryptPasswordImpl(algorithm, (IteratedPasswordAlgorithmSpec) parameterSpec, encryptableSpec.getPassword(), + encryptableSpec.getHashCharset()); + } else if (parameterSpec instanceof IteratedSaltedPasswordAlgorithmSpec) { + return new UnixSHACryptPasswordImpl(algorithm, (IteratedSaltedPasswordAlgorithmSpec) parameterSpec, encryptableSpec.getPassword(), + encryptableSpec.getHashCharset()); + } else if (parameterSpec instanceof SaltedPasswordAlgorithmSpec) { + return new UnixSHACryptPasswordImpl(algorithm, (SaltedPasswordAlgorithmSpec) parameterSpec, encryptableSpec.getPassword(), + encryptableSpec.getHashCharset()); + } else { + break; + } + } catch (IllegalArgumentException | NullPointerException | NoSuchAlgorithmException e) { + throw new InvalidKeySpecException(e); + } + } else { + break; + } + } + case ALGORITHM_DIGEST_MD5: + case ALGORITHM_DIGEST_SHA: + case ALGORITHM_DIGEST_SHA_256: + case ALGORITHM_DIGEST_SHA_384: + case ALGORITHM_DIGEST_SHA_512: + case ALGORITHM_DIGEST_SHA_512_256: + if (keySpec instanceof DigestPasswordSpec) { + return new DigestPasswordImpl(algorithm, (DigestPasswordSpec) keySpec); + } else if (keySpec instanceof EncryptablePasswordSpec) { + return new DigestPasswordImpl(algorithm, (EncryptablePasswordSpec) keySpec); + } + break; + case ALGORITHM_SIMPLE_DIGEST_MD2: + case ALGORITHM_SIMPLE_DIGEST_MD5: + case ALGORITHM_SIMPLE_DIGEST_SHA_1: + case ALGORITHM_SIMPLE_DIGEST_SHA_256: + case ALGORITHM_SIMPLE_DIGEST_SHA_384: + case ALGORITHM_SIMPLE_DIGEST_SHA_512: { + if (keySpec instanceof HashPasswordSpec) { + try { + return new SimpleDigestPasswordImpl(algorithm, (HashPasswordSpec) keySpec); + } catch (IllegalArgumentException | NullPointerException e) { + throw new InvalidKeySpecException(e); + } + } else if (keySpec instanceof ClearPasswordSpec) { + try { + return new SimpleDigestPasswordImpl(algorithm, (ClearPasswordSpec) keySpec); + } catch (IllegalArgumentException | NullPointerException e) { + throw new InvalidKeySpecException(e); + } + } else if (keySpec instanceof EncryptablePasswordSpec) { + try { + final EncryptablePasswordSpec encryptableSpec = (EncryptablePasswordSpec) keySpec; + final AlgorithmParameterSpec parameterSpec = encryptableSpec.getAlgorithmParameterSpec(); + if (parameterSpec == null) { + return new SimpleDigestPasswordImpl(algorithm, encryptableSpec.getPassword(), encryptableSpec.getHashCharset()); + } else { + break; + } + } catch (IllegalArgumentException | NullPointerException e) { + throw new InvalidKeySpecException(e); + } + } else { + break; + } + } + case ALGORITHM_PASSWORD_SALT_DIGEST_MD5: + case ALGORITHM_PASSWORD_SALT_DIGEST_SHA_1: + case ALGORITHM_PASSWORD_SALT_DIGEST_SHA_256: + case ALGORITHM_PASSWORD_SALT_DIGEST_SHA_384: + case ALGORITHM_PASSWORD_SALT_DIGEST_SHA_512: + case ALGORITHM_SALT_PASSWORD_DIGEST_MD5: + case ALGORITHM_SALT_PASSWORD_DIGEST_SHA_1: + case ALGORITHM_SALT_PASSWORD_DIGEST_SHA_256: + case ALGORITHM_SALT_PASSWORD_DIGEST_SHA_384: + case ALGORITHM_SALT_PASSWORD_DIGEST_SHA_512: + if (keySpec instanceof SaltedHashPasswordSpec) { + try { + return new SaltedSimpleDigestPasswordImpl(algorithm, (SaltedHashPasswordSpec) keySpec); + } catch (IllegalArgumentException | NullPointerException e) { + throw new InvalidKeySpecException(e); + } + } else if (keySpec instanceof ClearPasswordSpec) { + try { + return new SaltedSimpleDigestPasswordImpl(algorithm, (ClearPasswordSpec) keySpec); + } catch (IllegalArgumentException | NullPointerException e) { + throw new InvalidKeySpecException(e); + } + } else if (keySpec instanceof EncryptablePasswordSpec) { + try { + final EncryptablePasswordSpec encryptableSpec = (EncryptablePasswordSpec) keySpec; + final AlgorithmParameterSpec parameterSpec = encryptableSpec.getAlgorithmParameterSpec(); + if (parameterSpec == null) { + return new SaltedSimpleDigestPasswordImpl(algorithm, encryptableSpec.getPassword(), encryptableSpec.getHashCharset()); + } else if (parameterSpec instanceof SaltedPasswordAlgorithmSpec) { + return new SaltedSimpleDigestPasswordImpl(algorithm, encryptableSpec.getPassword(), (SaltedPasswordAlgorithmSpec) parameterSpec, + encryptableSpec.getHashCharset()); + } else { + break; + } + } catch (IllegalArgumentException | NullPointerException e) { + throw new InvalidKeySpecException(e); + } + } + break; + case ALGORITHM_CRYPT_DES: { + if (keySpec instanceof SaltedHashPasswordSpec) { + try { + return new UnixDESCryptPasswordImpl((SaltedHashPasswordSpec) keySpec); + } catch (IllegalArgumentException | InvalidParameterSpecException | InvalidKeyException e) { + throw new InvalidKeySpecException(e); + } + } else if (keySpec instanceof ClearPasswordSpec) { + try { + return new UnixDESCryptPasswordImpl((ClearPasswordSpec) keySpec); + } catch (IllegalArgumentException | InvalidKeyException e) { + throw new InvalidKeySpecException(e); + } + } else if (keySpec instanceof EncryptablePasswordSpec) { + try { + final EncryptablePasswordSpec encryptableSpec = (EncryptablePasswordSpec) keySpec; + final AlgorithmParameterSpec parameterSpec = encryptableSpec.getAlgorithmParameterSpec(); + if (parameterSpec == null) { + return new UnixDESCryptPasswordImpl(encryptableSpec.getPassword(), encryptableSpec.getHashCharset()); + } else if (parameterSpec instanceof SaltedPasswordAlgorithmSpec) { + return new UnixDESCryptPasswordImpl(encryptableSpec.getPassword(), (SaltedPasswordAlgorithmSpec) parameterSpec, + encryptableSpec.getHashCharset()); + } else { + break; + } + } catch (IllegalArgumentException | InvalidParameterSpecException | InvalidKeyException e) { + throw new InvalidKeySpecException(e); + } + } else { + break; + } + } + case ALGORITHM_BSD_CRYPT_DES: { + if (keySpec instanceof IteratedSaltedHashPasswordSpec) { + try { + return new BSDUnixDESCryptPasswordImpl((IteratedSaltedHashPasswordSpec) keySpec); + } catch (IllegalArgumentException | InvalidParameterSpecException e) { + throw new InvalidKeySpecException(e); + } + } else if (keySpec instanceof SaltedHashPasswordSpec) { + try { + return new BSDUnixDESCryptPasswordImpl((SaltedHashPasswordSpec) keySpec); + } catch (IllegalArgumentException | InvalidParameterSpecException e) { + throw new InvalidKeySpecException(e); + } + } else if (keySpec instanceof ClearPasswordSpec) { + try { + return new BSDUnixDESCryptPasswordImpl((ClearPasswordSpec) keySpec); + } catch (IllegalArgumentException e) { + throw new InvalidKeySpecException(e); + } + } else if (keySpec instanceof EncryptablePasswordSpec) { + try { + final EncryptablePasswordSpec encryptableSpec = (EncryptablePasswordSpec) keySpec; + final AlgorithmParameterSpec parameterSpec = encryptableSpec.getAlgorithmParameterSpec(); + if (parameterSpec == null) { + return new BSDUnixDESCryptPasswordImpl(encryptableSpec.getPassword(), encryptableSpec.getHashCharset()); + } else if (parameterSpec instanceof SaltedPasswordAlgorithmSpec) { + return new BSDUnixDESCryptPasswordImpl(encryptableSpec.getPassword(), (SaltedPasswordAlgorithmSpec) parameterSpec, + encryptableSpec.getHashCharset()); + } else if (parameterSpec instanceof IteratedSaltedPasswordAlgorithmSpec) { + return new BSDUnixDESCryptPasswordImpl(encryptableSpec.getPassword(), (IteratedSaltedPasswordAlgorithmSpec) parameterSpec, + encryptableSpec.getHashCharset()); + } else if (parameterSpec instanceof IteratedPasswordAlgorithmSpec) { + return new BSDUnixDESCryptPasswordImpl(encryptableSpec.getPassword(), (IteratedPasswordAlgorithmSpec) parameterSpec, + encryptableSpec.getHashCharset()); + } else { + break; + } + } catch (InvalidParameterSpecException | IllegalArgumentException | NullPointerException e) { + throw new InvalidKeySpecException(e); + } + } else { + break; + } + } + case ALGORITHM_SCRAM_SHA_1: + case ALGORITHM_SCRAM_SHA_256: + case ALGORITHM_SCRAM_SHA_384: + case ALGORITHM_SCRAM_SHA_512: { + if (keySpec instanceof IteratedSaltedHashPasswordSpec) { + try { + return new ScramDigestPasswordImpl(algorithm, (IteratedSaltedHashPasswordSpec) keySpec); + } catch (IllegalArgumentException | NullPointerException e) { + throw new InvalidKeySpecException(e); + } + } else if (keySpec instanceof SaltedHashPasswordSpec) { + try { + return new ScramDigestPasswordImpl(algorithm, (SaltedHashPasswordSpec) keySpec); + } catch (IllegalArgumentException | NullPointerException e) { + throw new InvalidKeySpecException(e); + } + } else if (keySpec instanceof ClearPasswordSpec) { + try { + return new ScramDigestPasswordImpl(algorithm, (ClearPasswordSpec) keySpec); + } catch (IllegalArgumentException | NullPointerException | InvalidKeyException | NoSuchAlgorithmException e) { + throw new InvalidKeySpecException(e); + } + } else if (keySpec instanceof EncryptablePasswordSpec) { + try { + final EncryptablePasswordSpec encryptableSpec = (EncryptablePasswordSpec) keySpec; + final AlgorithmParameterSpec parameterSpec = encryptableSpec.getAlgorithmParameterSpec(); + if (parameterSpec == null) { + return new ScramDigestPasswordImpl(algorithm, encryptableSpec.getPassword(), encryptableSpec.getHashCharset()); + } else if (parameterSpec instanceof SaltedPasswordAlgorithmSpec) { + return new ScramDigestPasswordImpl(algorithm, encryptableSpec.getPassword(), (SaltedPasswordAlgorithmSpec) parameterSpec, + encryptableSpec.getHashCharset()); + } else if (parameterSpec instanceof IteratedSaltedPasswordAlgorithmSpec) { + return new ScramDigestPasswordImpl(algorithm, encryptableSpec.getPassword(), (IteratedSaltedPasswordAlgorithmSpec) parameterSpec, + encryptableSpec.getHashCharset()); + } else if (parameterSpec instanceof IteratedPasswordAlgorithmSpec) { + return new ScramDigestPasswordImpl(algorithm, encryptableSpec.getPassword(), (IteratedPasswordAlgorithmSpec) parameterSpec, + encryptableSpec.getHashCharset()); + } else { + break; + } + } catch (IllegalArgumentException | NullPointerException | InvalidKeyException | NoSuchAlgorithmException e) { + throw new InvalidKeySpecException(e); + } + } + else { + break; + } + } + case ALGORITHM_OTP_MD5: + case ALGORITHM_OTP_SHA1: + case ALGORITHM_OTP_SHA_256: + case ALGORITHM_OTP_SHA_384: + case ALGORITHM_OTP_SHA_512: { + if (keySpec instanceof OneTimePasswordSpec) { + return new OneTimePasswordImpl(algorithm, (OneTimePasswordSpec) keySpec); + } else if (keySpec instanceof EncryptablePasswordSpec) { + final EncryptablePasswordSpec encryptableSpec = (EncryptablePasswordSpec) keySpec; + final AlgorithmParameterSpec parameterSpec = encryptableSpec.getAlgorithmParameterSpec(); + try { + if ( parameterSpec instanceof OneTimePasswordAlgorithmSpec){ + return new OneTimePasswordImpl(algorithm, encryptableSpec.getPassword(), (OneTimePasswordAlgorithmSpec) parameterSpec); + } else { + break; + } + } catch (SaslException e) { + throw new InvalidKeySpecException(e); + } + } + break; + } + default: { + if (MaskedPassword.isMaskedAlgorithm(algorithm)) { + if (keySpec instanceof MaskedPasswordSpec) { + return new MaskedPasswordImpl(algorithm, (MaskedPasswordSpec) keySpec); + } else if (keySpec instanceof EncryptablePasswordSpec) { + final EncryptablePasswordSpec encryptableSpec = (EncryptablePasswordSpec) keySpec; + final AlgorithmParameterSpec parameterSpec = encryptableSpec.getAlgorithmParameterSpec(); + if (parameterSpec == null) { + return new MaskedPasswordImpl(algorithm, encryptableSpec.getPassword()); + } else if (parameterSpec instanceof MaskedPasswordAlgorithmSpec) { + return new MaskedPasswordImpl(algorithm, encryptableSpec.getPassword(), (MaskedPasswordAlgorithmSpec) parameterSpec); + } else if (parameterSpec instanceof IteratedSaltedPasswordAlgorithmSpec) { + return new MaskedPasswordImpl(algorithm, encryptableSpec.getPassword(), (IteratedSaltedPasswordAlgorithmSpec) parameterSpec); + } else if (parameterSpec instanceof SaltedPasswordAlgorithmSpec) { + return new MaskedPasswordImpl(algorithm, encryptableSpec.getPassword(), (SaltedPasswordAlgorithmSpec) parameterSpec); + } else if (parameterSpec instanceof IteratedPasswordAlgorithmSpec) { + return new MaskedPasswordImpl(algorithm, encryptableSpec.getPassword(), (IteratedPasswordAlgorithmSpec) parameterSpec); + } + } else if (keySpec instanceof ClearPasswordSpec) { + return new MaskedPasswordImpl(algorithm, (ClearPasswordSpec) keySpec); + } + break; + } + break; + } + } + throw log.invalidKeySpecUnknownAlgorithmOrIncompatiblePasswordSpec(algorithm, keySpec == null ? null : keySpec.getClass().getSimpleName()); + } + + @Override + protected S engineGetKeySpec(final String algorithm, final Password password, final Class keySpecType) throws InvalidKeySpecException { + if (password instanceof AbstractPasswordImpl) { + final AbstractPasswordImpl abstractPassword = (AbstractPasswordImpl) password; + if (algorithm.equals(abstractPassword.getAlgorithm())) { + return abstractPassword.getKeySpec(keySpecType); + } + } + throw new InvalidKeySpecException(); + } + + @Override + protected boolean engineIsTranslatablePassword(final String algorithm, final Password password) { + if (password instanceof AbstractPasswordImpl) { + final AbstractPasswordImpl abstractPassword = (AbstractPasswordImpl) password; + if (algorithm.equals(abstractPassword.getAlgorithm())) { + return true; + } + } + + /* + * When adding or removing an algorithm ensure that the registrations in 'WildFlyElytronProvider' are also + * updated. + */ + + switch (algorithm) { + case ALGORITHM_CLEAR: { + return (password instanceof ClearPassword); + } + case ALGORITHM_BCRYPT: { + return (password instanceof BCryptPassword); + } + case ALGORITHM_CRYPT_MD5: { + return (password instanceof UnixMD5CryptPassword); + } + case ALGORITHM_SUN_CRYPT_MD5: + case ALGORITHM_SUN_CRYPT_MD5_BARE_SALT: { + return (password instanceof SunUnixMD5CryptPassword && algorithm.equals(password.getAlgorithm())); + } + case ALGORITHM_CRYPT_SHA_256: + case ALGORITHM_CRYPT_SHA_512: { + return (password instanceof UnixSHACryptPassword && algorithm.equals(password.getAlgorithm())); + } + case ALGORITHM_DIGEST_MD5: + case ALGORITHM_DIGEST_SHA: + case ALGORITHM_DIGEST_SHA_256: + case ALGORITHM_DIGEST_SHA_384: + case ALGORITHM_DIGEST_SHA_512:{ + return (password instanceof DigestPassword && algorithm.equals(password.getAlgorithm())); + } + case ALGORITHM_SIMPLE_DIGEST_MD2: + case ALGORITHM_SIMPLE_DIGEST_MD5: + case ALGORITHM_SIMPLE_DIGEST_SHA_1: + case ALGORITHM_SIMPLE_DIGEST_SHA_256: + case ALGORITHM_SIMPLE_DIGEST_SHA_384: + case ALGORITHM_SIMPLE_DIGEST_SHA_512: { + return (password instanceof SimpleDigestPassword && algorithm.equals(password.getAlgorithm())); + } + case ALGORITHM_PASSWORD_SALT_DIGEST_MD5: + case ALGORITHM_PASSWORD_SALT_DIGEST_SHA_1: + case ALGORITHM_PASSWORD_SALT_DIGEST_SHA_256: + case ALGORITHM_PASSWORD_SALT_DIGEST_SHA_384: + case ALGORITHM_PASSWORD_SALT_DIGEST_SHA_512: + case ALGORITHM_SALT_PASSWORD_DIGEST_MD5: + case ALGORITHM_SALT_PASSWORD_DIGEST_SHA_1: + case ALGORITHM_SALT_PASSWORD_DIGEST_SHA_256: + case ALGORITHM_SALT_PASSWORD_DIGEST_SHA_384: + case ALGORITHM_SALT_PASSWORD_DIGEST_SHA_512: { + return (password instanceof SaltedSimpleDigestPassword && algorithm.equals(password.getAlgorithm())); + } + case ALGORITHM_CRYPT_DES: { + return (password instanceof UnixDESCryptPassword); + } + case ALGORITHM_BSD_CRYPT_DES: { + return (password instanceof BSDUnixDESCryptPassword); + } + case ALGORITHM_SCRAM_SHA_1: + case ALGORITHM_SCRAM_SHA_256: + case ALGORITHM_SCRAM_SHA_384: + case ALGORITHM_SCRAM_SHA_512: { + return (password instanceof ScramDigestPassword && algorithm.equals(password.getAlgorithm())); + } + case ALGORITHM_OTP_MD5: + case ALGORITHM_OTP_SHA1: + case ALGORITHM_OTP_SHA_256: + case ALGORITHM_OTP_SHA_384: + case ALGORITHM_OTP_SHA_512: { + return (password instanceof OneTimePassword && algorithm.equals(password.getAlgorithm())); + } + default: { + return MaskedPassword.isMaskedAlgorithm(algorithm) && password instanceof MaskedPassword && algorithm.equals(password.getAlgorithm()); + } + } + } + + @Override + protected Password engineTranslatePassword(final String algorithm, final Password password) throws InvalidKeyException { + if (password instanceof AbstractPasswordImpl) { + final AbstractPasswordImpl abstractPassword = (AbstractPasswordImpl) password; + if (algorithm.equals(abstractPassword.getAlgorithm())) { + return abstractPassword; + } + } + + /* + * When adding or removing an algorithm ensure that the registrations in 'WildFlyElytronProvider' are also + * updated. + */ + + switch (algorithm) { + case ALGORITHM_CLEAR: { + if (password instanceof ClearPasswordImpl) { + return password; + } else if (password instanceof ClearPassword) { + return new ClearPasswordImpl((ClearPassword) password); + } + break; + } + case ALGORITHM_BCRYPT: { + if (password instanceof BCryptPasswordImpl) { + return password; + } else if (password instanceof BCryptPassword) { + return new BCryptPasswordImpl((BCryptPassword) password); + } + break; + } + case ALGORITHM_CRYPT_MD5: { + if (password instanceof UnixMD5CryptPasswordImpl) { + return password; + } else if (password instanceof UnixMD5CryptPassword) { + return new UnixMD5CryptPasswordImpl((UnixMD5CryptPassword) password); + } else { + break; + } + } + case ALGORITHM_SUN_CRYPT_MD5: + case ALGORITHM_SUN_CRYPT_MD5_BARE_SALT: { + if (password instanceof SunUnixMD5CryptPasswordImpl && algorithm.equals(password.getAlgorithm())) { + return password; + } else if (password instanceof SunUnixMD5CryptPassword && algorithm.equals(password.getAlgorithm())) { + return new SunUnixMD5CryptPasswordImpl((SunUnixMD5CryptPassword) password); + } else { + break; + } + } + case ALGORITHM_CRYPT_SHA_256: + case ALGORITHM_CRYPT_SHA_512: { + if (password instanceof UnixSHACryptPasswordImpl && algorithm.equals(password.getAlgorithm())) { + return password; + } else if (password instanceof UnixSHACryptPassword && algorithm.equals(password.getAlgorithm())) { + return new UnixSHACryptPasswordImpl((UnixSHACryptPassword) password); + } + break; + } + case ALGORITHM_DIGEST_MD5: + case ALGORITHM_DIGEST_SHA: + case ALGORITHM_DIGEST_SHA_256: + case ALGORITHM_DIGEST_SHA_384: + case ALGORITHM_DIGEST_SHA_512: + case ALGORITHM_DIGEST_SHA_512_256: { + if (password instanceof DigestPasswordImpl && algorithm.equals(password.getAlgorithm())) { + return password; + } else if (password instanceof DigestPassword && algorithm.equals(password.getAlgorithm())) { + return new SimpleDigestPasswordImpl((SimpleDigestPassword) password); + } + break; + } + case ALGORITHM_SIMPLE_DIGEST_MD2: + case ALGORITHM_SIMPLE_DIGEST_MD5: + case ALGORITHM_SIMPLE_DIGEST_SHA_1: + case ALGORITHM_SIMPLE_DIGEST_SHA_256: + case ALGORITHM_SIMPLE_DIGEST_SHA_384: + case ALGORITHM_SIMPLE_DIGEST_SHA_512: { + if (password instanceof SimpleDigestPasswordImpl && algorithm.equals(password.getAlgorithm())) { + return password; + } else if (password instanceof SimpleDigestPassword && algorithm.equals(password.getAlgorithm())) { + return new SimpleDigestPasswordImpl((SimpleDigestPassword) password); + } + break; + } + case ALGORITHM_PASSWORD_SALT_DIGEST_MD5: + case ALGORITHM_PASSWORD_SALT_DIGEST_SHA_1: + case ALGORITHM_PASSWORD_SALT_DIGEST_SHA_256: + case ALGORITHM_PASSWORD_SALT_DIGEST_SHA_384: + case ALGORITHM_PASSWORD_SALT_DIGEST_SHA_512: + case ALGORITHM_SALT_PASSWORD_DIGEST_MD5: + case ALGORITHM_SALT_PASSWORD_DIGEST_SHA_1: + case ALGORITHM_SALT_PASSWORD_DIGEST_SHA_256: + case ALGORITHM_SALT_PASSWORD_DIGEST_SHA_384: + case ALGORITHM_SALT_PASSWORD_DIGEST_SHA_512: { + if (password instanceof SaltedSimpleDigestPasswordImpl && algorithm.equals(password.getAlgorithm())) { + return password; + } else if (password instanceof SaltedSimpleDigestPassword && algorithm.equals(password.getAlgorithm())) { + return new SaltedSimpleDigestPasswordImpl((SaltedSimpleDigestPassword) password); + } + break; + } + case ALGORITHM_CRYPT_DES: { + if (password instanceof UnixDESCryptPasswordImpl) { + return password; + } else if (password instanceof UnixDESCryptPassword) { + return new UnixDESCryptPasswordImpl((UnixDESCryptPassword) password); + } + break; + } + case ALGORITHM_BSD_CRYPT_DES: { + if (password instanceof BSDUnixDESCryptPasswordImpl) { + return password; + } else if (password instanceof BSDUnixDESCryptPassword) { + return new BSDUnixDESCryptPasswordImpl((BSDUnixDESCryptPassword) password); + } + break; + } + case ALGORITHM_SCRAM_SHA_1: + case ALGORITHM_SCRAM_SHA_256: + case ALGORITHM_SCRAM_SHA_384: + case ALGORITHM_SCRAM_SHA_512: { + if (password instanceof ScramDigestPasswordImpl && algorithm.equals(password.getAlgorithm())) { + return password; + } else if (password instanceof ScramDigestPassword && algorithm.equals(password.getAlgorithm())) { + return new ScramDigestPasswordImpl((ScramDigestPassword) password); + } + break; + } + case ALGORITHM_OTP_MD5: + case ALGORITHM_OTP_SHA1: + case ALGORITHM_OTP_SHA_256: + case ALGORITHM_OTP_SHA_384: + case ALGORITHM_OTP_SHA_512: { + if (password instanceof OneTimePasswordImpl && algorithm.equals(password.getAlgorithm())) { + return password; + } else if (password instanceof OneTimePassword && algorithm.equals(password.getAlgorithm())) { + return new OneTimePasswordImpl((OneTimePassword) password); + } + break; + } + default: { + if (MaskedPassword.isMaskedAlgorithm(algorithm)) { + if (password instanceof MaskedPasswordImpl && algorithm.equals(password.getAlgorithm())) { + return password; + } else if (password instanceof MaskedPassword && algorithm.equals(password.getAlgorithm())) { + try { + return new MaskedPasswordImpl((MaskedPassword) password); + } catch (InvalidKeySpecException e) { + throw new InvalidKeyException(e); + } + } + } + break; + } + } + throw log.invalidKeyUnknownUnknownPasswordTypeOrAlgorithm(); + } + + @Override + protected boolean engineVerify(final String algorithm, final Password password, final char[] guess) throws InvalidKeyException { + if (password instanceof AbstractPasswordImpl) { + final AbstractPasswordImpl abstractPassword = (AbstractPasswordImpl) password; + if (algorithm.equals(abstractPassword.getAlgorithm())) { + return abstractPassword.verify(guess); + } + } + throw new InvalidKeyException(); + } + + @Override + protected boolean engineVerify(final String algorithm, final Password password, final char[] guess, Charset hashCharset) throws InvalidKeyException { + if (password instanceof AbstractPasswordImpl) { + final AbstractPasswordImpl abstractPassword = (AbstractPasswordImpl) password; + if (algorithm.equals(abstractPassword.getAlgorithm())) { + return abstractPassword.verify(guess, hashCharset); + } + } + throw new InvalidKeyException(); + } + + @Override + protected boolean engineConvertibleToKeySpec(final String algorithm, final Password password, final Class keySpecType) { + if (password instanceof AbstractPasswordImpl) { + final AbstractPasswordImpl abstractPassword = (AbstractPasswordImpl) password; + if (algorithm.equals(abstractPassword.getAlgorithm())) { + return abstractPassword.convertibleTo(keySpecType); + } + } + return false; + } + + @Override + protected Password engineTransform(final String algorithm, final Password password, final AlgorithmParameterSpec parameterSpec) throws InvalidKeyException, InvalidAlgorithmParameterException { + if (password instanceof AbstractPasswordImpl) { + final AbstractPasswordImpl abstractPassword = (AbstractPasswordImpl) password; + if (algorithm.equals(abstractPassword.getAlgorithm())) { + return abstractPassword.translate(parameterSpec); + } + } + throw new InvalidKeyException(); + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/password/impl/PasswordUtil.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/password/impl/PasswordUtil.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/password/impl/PasswordUtil.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,41 @@ +/* + * JBoss, Home of Professional Open Source + * + * Copyright 2015 Red Hat, Inc. and/or its affiliates. + * + * 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.password.impl; + +import java.util.concurrent.ThreadLocalRandom; + +/** + * Helper utility methods for operations on passwords. + * + * @author Juraci Paixão Kröhling + * @author David M. Lloyd + */ +final class PasswordUtil { + + /** + * Generate a random salt as byte array. + * + * @param saltSize the size of the salt + * @return a byte array representing the random salt + */ + public static byte[] generateRandomSalt(int saltSize) { + byte[] randomSalt = new byte[saltSize]; + ThreadLocalRandom.current().nextBytes(randomSalt); + return randomSalt; + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/password/impl/SaltedPasswordAlgorithmParametersSpiImpl.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/password/impl/SaltedPasswordAlgorithmParametersSpiImpl.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/password/impl/SaltedPasswordAlgorithmParametersSpiImpl.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,54 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2016 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.password.impl; + +import java.security.AlgorithmParametersSpi; + +import org.wildfly.security.asn1.ASN1Decoder; +import org.wildfly.security.asn1.ASN1Encoder; +import org.wildfly.security.password.spec.SaltedPasswordAlgorithmSpec; +import org.wildfly.security.util.AbstractAlgorithmParametersSpiImpl; + +/** + * An implementation of the {@link AlgorithmParametersSpi} SPI, in order to support encoding and decoding of + * password algorithm parameters. + * + * @author David M. Lloyd + */ +public final class SaltedPasswordAlgorithmParametersSpiImpl extends AbstractAlgorithmParametersSpiImpl { + + /** + * Construct a new instance. + */ + public SaltedPasswordAlgorithmParametersSpiImpl() { + } + + protected Class getParameterType() { + return SaltedPasswordAlgorithmSpec.class; + } + + protected void engineEncode(final ASN1Encoder encoder, final SaltedPasswordAlgorithmSpec parameterSpec) { + encoder.encodeOctetString(parameterSpec.getSalt()); + } + + protected SaltedPasswordAlgorithmSpec engineDecode(final ASN1Decoder decoder) { + final byte[] salt = decoder.decodeOctetString(); + return new SaltedPasswordAlgorithmSpec(salt); + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/password/impl/SaltedSimpleDigestPasswordImpl.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/password/impl/SaltedSimpleDigestPasswordImpl.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/password/impl/SaltedSimpleDigestPasswordImpl.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,219 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2014 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.password.impl; + +import static org.wildfly.common.math.HashMath.multiHashOrdered; +import static org.wildfly.security.password.impl.ElytronMessages.log; + +import java.io.NotSerializableException; +import java.io.ObjectInputStream; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.security.InvalidKeyException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.KeySpec; +import java.util.Arrays; + +import org.wildfly.security.password.interfaces.SaltedSimpleDigestPassword; +import org.wildfly.security.password.spec.ClearPasswordSpec; +import org.wildfly.security.password.spec.SaltedHashPasswordSpec; +import org.wildfly.security.password.spec.SaltedPasswordAlgorithmSpec; + +/** + * A {@code Password} implementation for {@link SaltedSimpleDigestPassword}. + * + * @author Darran Lofthouse + */ +class SaltedSimpleDigestPasswordImpl extends AbstractPasswordImpl implements SaltedSimpleDigestPassword { + + private static final long serialVersionUID = -6754143875392946386L; + + private final String algorithm; + private final byte[] digest; + private final byte[] salt; + + SaltedSimpleDigestPasswordImpl(final String algorithm, final byte[] salt, final byte[] digest) { + this.algorithm = algorithm; + this.digest = digest; + this.salt = salt; + } + + + SaltedSimpleDigestPasswordImpl(final String algorithm, final SaltedHashPasswordSpec spec) { + this(algorithm, spec.getSalt().clone(), spec.getHash().clone()); + } + + SaltedSimpleDigestPasswordImpl(final SaltedSimpleDigestPassword password) { + this(password.getAlgorithm(), password.getSalt().clone(), password.getDigest().clone()); + } + + SaltedSimpleDigestPasswordImpl(final String algorithm, final ClearPasswordSpec spec) throws InvalidKeySpecException { + this.algorithm = algorithm; + this.salt = PasswordUtil.generateRandomSalt(DEFAULT_SALT_SIZE); + try { + this.digest = digestOf(algorithm, salt, spec.getEncodedPassword(), StandardCharsets.UTF_8); + } catch (NoSuchAlgorithmException e) { + throw log.invalidKeySpecNoSuchMessageDigestAlgorithm(algorithm); + } + } + + SaltedSimpleDigestPasswordImpl(final String algorithm, final char[] password, final SaltedPasswordAlgorithmSpec spec, final Charset hashCharset) throws InvalidKeySpecException { + this(algorithm, spec.getSalt().clone(), password, hashCharset); + } + + SaltedSimpleDigestPasswordImpl(final String algorithm, final char[] password, final Charset hashCharset) throws InvalidKeySpecException { + this(algorithm, PasswordUtil.generateRandomSalt(DEFAULT_SALT_SIZE), password, hashCharset); + } + + private SaltedSimpleDigestPasswordImpl(final String algorithm, final byte[] salt, final char[] password, final Charset hashCharset) + throws InvalidKeySpecException { + this.algorithm = algorithm; + this.salt = salt; + try { + this.digest = digestOf(algorithm, salt, password, hashCharset); + } catch (NoSuchAlgorithmException e) { + throw log.invalidKeySpecNoSuchMessageDigestAlgorithm(algorithm); + } + } + + @Override + public String getAlgorithm() { + return algorithm; + } + + @Override + public byte[] getDigest() { + return digest.clone(); + } + + @Override + public byte[] getSalt() { + return salt.clone(); + } + + @Override + S getKeySpec(Class keySpecType) throws InvalidKeySpecException { + if (keySpecType.isAssignableFrom(SaltedHashPasswordSpec.class)) { + return keySpecType.cast(new SaltedHashPasswordSpec(digest.clone(), salt.clone())); + } + throw new InvalidKeySpecException(); + } + + @Override + boolean verify(char[] guess) throws InvalidKeyException { + return verify(guess, StandardCharsets.UTF_8); + } + + @Override + boolean verify(char[] guess, Charset hashCharset) throws InvalidKeyException { + try { + return MessageDigest.isEqual(digest, digestOf(algorithm, salt, guess, hashCharset)); + } catch (NoSuchAlgorithmException e) { + throw log.invalidKeyNoSuchMessageDigestAlgorithm(algorithm); + } + } + + @Override + boolean convertibleTo(Class keySpecType) { + return keySpecType.isAssignableFrom(SaltedHashPasswordSpec.class); + } + + private static byte[] digestOf(final String algorithm, final byte[] salt, final char[] password, final Charset hashCharset) + throws NoSuchAlgorithmException { + boolean saltFirst = isSaltFirst(algorithm); + MessageDigest md = getMessageDigest(algorithm); + byte[] passwordBytes = new String(password).getBytes(hashCharset); + if (saltFirst) { + md.update(salt); + md.update(passwordBytes); + } else { + md.update(passwordBytes); + md.update(salt); + } + return md.digest(); + } + + private static MessageDigest getMessageDigest(final String algorithm) throws NoSuchAlgorithmException { + switch (algorithm) { + case ALGORITHM_PASSWORD_SALT_DIGEST_MD5: + case ALGORITHM_SALT_PASSWORD_DIGEST_MD5: + return MessageDigest.getInstance("MD5"); + case ALGORITHM_PASSWORD_SALT_DIGEST_SHA_1: + case ALGORITHM_SALT_PASSWORD_DIGEST_SHA_1: + return MessageDigest.getInstance("SHA-1"); + case ALGORITHM_PASSWORD_SALT_DIGEST_SHA_256: + case ALGORITHM_SALT_PASSWORD_DIGEST_SHA_256: + return MessageDigest.getInstance("SHA-256"); + case ALGORITHM_PASSWORD_SALT_DIGEST_SHA_384: + case ALGORITHM_SALT_PASSWORD_DIGEST_SHA_384: + return MessageDigest.getInstance("SHA-384"); + case ALGORITHM_PASSWORD_SALT_DIGEST_SHA_512: + case ALGORITHM_SALT_PASSWORD_DIGEST_SHA_512: + return MessageDigest.getInstance("SHA-512"); + default: + throw log.noSuchAlgorithmInvalidAlgorithm(algorithm); + } + } + + private static boolean isSaltFirst(final String algorithm) throws NoSuchAlgorithmException { + switch (algorithm) { + case ALGORITHM_PASSWORD_SALT_DIGEST_MD5: + case ALGORITHM_PASSWORD_SALT_DIGEST_SHA_1: + case ALGORITHM_PASSWORD_SALT_DIGEST_SHA_256: + case ALGORITHM_PASSWORD_SALT_DIGEST_SHA_384: + case ALGORITHM_PASSWORD_SALT_DIGEST_SHA_512: + return false; + case ALGORITHM_SALT_PASSWORD_DIGEST_MD5: + case ALGORITHM_SALT_PASSWORD_DIGEST_SHA_1: + case ALGORITHM_SALT_PASSWORD_DIGEST_SHA_256: + case ALGORITHM_SALT_PASSWORD_DIGEST_SHA_384: + case ALGORITHM_SALT_PASSWORD_DIGEST_SHA_512: + return true; + default: + throw log.noSuchAlgorithmInvalidAlgorithm(algorithm); + } + } + + public int hashCode() { + return multiHashOrdered(multiHashOrdered(Arrays.hashCode(digest), Arrays.hashCode(salt)), algorithm.hashCode()); + } + + public boolean equals(final Object obj) { + if (! (obj instanceof SaltedSimpleDigestPasswordImpl)) { + return false; + } + SaltedSimpleDigestPasswordImpl other = (SaltedSimpleDigestPasswordImpl) obj; + return algorithm.equals(other.algorithm) && MessageDigest.isEqual(digest, other.digest) && Arrays.equals(salt, other.salt); + } + + private void readObject(ObjectInputStream ignored) throws NotSerializableException { + throw new NotSerializableException(); + } + + Object writeReplace() { + return SaltedSimpleDigestPassword.createRaw(algorithm, digest, salt); + } + + public SaltedSimpleDigestPasswordImpl clone() { + return this; + } + +} Index: 3rdParty_sources/elytron/org/wildfly/security/password/impl/ScramDigestPasswordImpl.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/password/impl/ScramDigestPasswordImpl.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/password/impl/ScramDigestPasswordImpl.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,380 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2014 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.password.impl; + +import static org.wildfly.common.math.HashMath.multiHashOrdered; +import static org.wildfly.security.password.impl.ElytronMessages.log; + +import java.io.NotSerializableException; +import java.io.ObjectInputStream; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.security.InvalidAlgorithmParameterException; +import java.security.InvalidKeyException; +import java.security.Key; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.spec.AlgorithmParameterSpec; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.KeySpec; +import java.util.Arrays; + +import javax.crypto.Mac; +import javax.crypto.spec.SecretKeySpec; + +import org.wildfly.security.password.Password; +import org.wildfly.security.password.spec.IteratedPasswordAlgorithmSpec; +import org.wildfly.security.password.spec.SaltedPasswordAlgorithmSpec; +import org.wildfly.security.password.interfaces.ScramDigestPassword; +import org.wildfly.security.password.spec.ClearPasswordSpec; +import org.wildfly.security.password.spec.IteratedSaltedPasswordAlgorithmSpec; +import org.wildfly.security.password.spec.IteratedSaltedHashPasswordSpec; +import org.wildfly.security.password.spec.SaltedHashPasswordSpec; + +/** + * A {@link org.wildfly.security.password.Password} implementation for {@link org.wildfly.security.password.interfaces.ScramDigestPassword}. + * + * @author Stefan Guilhen + */ +class ScramDigestPasswordImpl extends AbstractPasswordImpl implements ScramDigestPassword { + + private static final long serialVersionUID = 5831469808883867480L; + + private static final String HMAC_SHA1_ALGORITHM = "HmacSHA1"; + private static final String HMAC_SHA256_ALGORITHM = "HmacSHA256"; + private static final String HMAC_SHA384_ALGORITHM = "HmacSHA384"; + private static final String HMAC_SHA512_ALGORITHM = "HmacSHA512"; + + private final String algorithm; + private final byte[] digest; + private final byte[] salt; + private final int iterationCount; + + ScramDigestPasswordImpl(final String algorithm, final byte[] digest, final byte[] salt, final int iterationCount) { + this.algorithm = algorithm; + this.digest = digest; + this.salt = salt; + this.iterationCount = iterationCount; + } + + ScramDigestPasswordImpl(final ScramDigestPassword password) { + this(password.getAlgorithm(), password.getDigest().clone(), password.getSalt().clone(), password.getIterationCount()); + } + + ScramDigestPasswordImpl(final String algorithm, final IteratedSaltedHashPasswordSpec spec) { + this(algorithm, spec.getHash().clone(), spec.getSalt().clone(), spec.getIterationCount()); + } + + ScramDigestPasswordImpl(final String algorithm, final SaltedHashPasswordSpec spec) { + this(algorithm, spec.getHash().clone(), spec.getSalt().clone(), DEFAULT_ITERATION_COUNT); + } + + ScramDigestPasswordImpl(final String algorithm, final ClearPasswordSpec spec) throws InvalidKeySpecException, NoSuchAlgorithmException, InvalidKeyException { + this(algorithm, spec.getEncodedPassword(), PasswordUtil.generateRandomSalt(DEFAULT_SALT_SIZE), DEFAULT_ITERATION_COUNT); + } + + ScramDigestPasswordImpl(final String algorithm, final char[] password, final Charset hashCharset) throws InvalidKeySpecException, NoSuchAlgorithmException, InvalidKeyException { + this(algorithm, password, PasswordUtil.generateRandomSalt(DEFAULT_SALT_SIZE), DEFAULT_ITERATION_COUNT, hashCharset); + } + + ScramDigestPasswordImpl(final String algorithm, final char[] password, final IteratedSaltedPasswordAlgorithmSpec spec, final Charset hashCharset) throws InvalidKeySpecException, NoSuchAlgorithmException, InvalidKeyException { + this(algorithm, password, spec.getSalt(), spec.getIterationCount(), hashCharset); + } + + ScramDigestPasswordImpl(final String algorithm, final char[] password, final SaltedPasswordAlgorithmSpec spec, final Charset hashCharset) throws InvalidKeySpecException, NoSuchAlgorithmException, InvalidKeyException { + this(algorithm, password, spec.getSalt(), DEFAULT_ITERATION_COUNT, hashCharset); + } + + ScramDigestPasswordImpl(final String algorithm, final char[] password, final IteratedPasswordAlgorithmSpec spec, final Charset hashCharset) throws InvalidKeySpecException, NoSuchAlgorithmException, InvalidKeyException { + this(algorithm, password, PasswordUtil.generateRandomSalt(DEFAULT_SALT_SIZE), spec.getIterationCount(), hashCharset); + } + + ScramDigestPasswordImpl(final String algorithm, final char[] password, final byte[] salt, final int iterationCount) throws InvalidKeyException, NoSuchAlgorithmException { + this(algorithm, scramDigest(algorithm, getNormalizedPasswordBytes(password), salt, iterationCount), salt, iterationCount); + } + + ScramDigestPasswordImpl(final String algorithm, final char[] password, final byte[] salt, final int iterationCount, final Charset hashCharset) throws InvalidKeyException, NoSuchAlgorithmException { + this.algorithm = algorithm; + this.digest = scramDigest(algorithm, getNormalizedPasswordBytes(password, hashCharset), salt, iterationCount, hashCharset); + this.salt = salt; + this.iterationCount = iterationCount; + } + + @Override + public String getAlgorithm() { + return this.algorithm; + } + + @Override + public byte[] getDigest() { + try { + return this.digest.clone(); + } catch (NullPointerException npe) { + throw new IllegalStateException(); + } + } + + @Override + public byte[] getSalt() { + try { + return this.salt.clone(); + } catch (NullPointerException npe) { + throw new IllegalStateException(); + } + } + + @Override + public int getIterationCount() { + return this.iterationCount; + } + + @Override + boolean convertibleTo(Class keySpecType) { + return keySpecType.isAssignableFrom(IteratedSaltedHashPasswordSpec.class); + } + + @Override + Password translate(final AlgorithmParameterSpec parameterSpec) throws InvalidKeyException, InvalidAlgorithmParameterException { + if (parameterSpec instanceof IteratedSaltedPasswordAlgorithmSpec) { + IteratedSaltedPasswordAlgorithmSpec updateSpec = (IteratedSaltedPasswordAlgorithmSpec) parameterSpec; + byte[] updateSalt = updateSpec.getSalt(); + if (updateSalt != null && ! Arrays.equals(updateSalt, salt)) { + throw new InvalidAlgorithmParameterException(); + } + int updateIterationCount = updateSpec.getIterationCount(); + if (updateIterationCount < this.iterationCount) { + throw new InvalidAlgorithmParameterException(); + } + if (updateIterationCount == this.iterationCount) { + return this; + } + byte[] digest = this.digest.clone(); + try { + addIterations(digest, getMacInstance(algorithm, digest), this.iterationCount, updateIterationCount); + } catch (NoSuchAlgorithmException | InvalidKeyException e) { + throw new InvalidKeyException(e); + } + return new ScramDigestPasswordImpl(algorithm, digest, updateSalt, updateIterationCount); + } else if (parameterSpec instanceof IteratedPasswordAlgorithmSpec) { + final IteratedPasswordAlgorithmSpec updateSpec = (IteratedPasswordAlgorithmSpec) parameterSpec; + int updateIterationCount = updateSpec.getIterationCount(); + if (updateIterationCount < this.iterationCount) { + throw new InvalidAlgorithmParameterException(); + } + if (updateIterationCount == this.iterationCount) { + return this; + } + try { + addIterations(digest, getMacInstance(algorithm, digest), this.iterationCount, updateIterationCount); + } catch (NoSuchAlgorithmException | InvalidKeyException e) { + throw new InvalidKeyException(e); + } + return new ScramDigestPasswordImpl(algorithm, digest, salt, updateIterationCount); + } else if (parameterSpec instanceof SaltedPasswordAlgorithmSpec) { + SaltedPasswordAlgorithmSpec updateSpec = (SaltedPasswordAlgorithmSpec) parameterSpec; + byte[] updateSalt = updateSpec.getSalt(); + if (updateSalt != null && ! Arrays.equals(updateSalt, salt)) { + throw new InvalidAlgorithmParameterException(); + } + return this; + } + throw new InvalidAlgorithmParameterException(); + } + + @Override + boolean verify(char[] guess) throws InvalidKeyException { + return verify(guess, StandardCharsets.UTF_8); + } + + @Override + boolean verify(char[] guess, Charset hashCharset) throws InvalidKeyException { + if (guess.length == 0) return false; + try { + byte[] output = scramDigest(this.getAlgorithm(), getNormalizedPasswordBytes(guess, hashCharset), this.getSalt(), this.getIterationCount()); + return MessageDigest.isEqual(this.digest, output); + } catch (NoSuchAlgorithmException nsae) { + throw new InvalidKeyException(nsae); + } + } + + @Override + S getKeySpec(Class keySpecType) throws InvalidKeySpecException { + if (keySpecType.isAssignableFrom(IteratedSaltedHashPasswordSpec.class)) { + return keySpecType.cast(new IteratedSaltedHashPasswordSpec(this.getDigest(), this.getSalt(), this.getIterationCount())); + } + throw new InvalidKeySpecException(); + } + + /** + *

+ * This method implements the SCRAM {@code Hi} function as specified by + * RFC 5802. The function is defined as follows: + * + *

+     *     Hi(str, salt, i)
+     *         U1 <- HMAC(str, salt + INT(1))
+     *         U2 <- HMAC(str, U1)
+     *         ...
+     *         Ui-1 <- HMAC(str, Ui-2)
+     *         Ui <- HMAC(str, Ui-1)
+     *         Hi <- U1 XOR U2 XOR ... XOR Ui
+     *         return Hi
+     * 
+ * + * where {@code i} is the iteration count, {@code +} is the string concatenation operator, and {@code INT(g)} is a + * 4-octet encoding of the integer {@code g}, most significant octet first. + *

+ * + * @param algorithm the algorithm that should be used to hash the password. + * @param password the password to be hashed. + * @param salt the salt used to hash the password. + * @param iterationCount the iteration count used to hash the password. + * + * @return a byte[] containing the hashed password. + */ + static byte[] scramDigest(final String algorithm, final byte[] password, final byte[] salt, final int iterationCount) + throws NoSuchAlgorithmException, InvalidKeyException { + + return scramDigest(algorithm, password, salt, iterationCount, StandardCharsets.UTF_8); + } + + static byte[] scramDigest(final String algorithm, final byte[] password, final byte[] salt, final int iterationCount, final Charset hashCharset) + throws NoSuchAlgorithmException, InvalidKeyException { + + Mac hmac = getMacInstance(algorithm, password); + + // compute U1 (see Hi function description in the javadoc). + hmac.update(salt); + hmac.update("\00\00\00\01".getBytes(hashCharset)); + byte[] hi = hmac.doFinal(); + addIterations(hi, hmac, 1, iterationCount); + return hi; + } + + static void addIterations(final byte[] hi, final Mac hmac, final int currentIterationCount, final int newIterationCount) { + // compute U2 ... Ui, performing the xor with the previous result as we iterate. + byte[] current = hi; + for (int i = currentIterationCount; i < newIterationCount; i++) { + hmac.update(current); + current = hmac.doFinal(); + for (int j = 0; j < hi.length; j++) { + hi[j] ^= current[j]; + } + } + } + + /** + *

+ * Builds a {@link Mac} instance using the specified algorithm and password. + *

+ * + * @param algorithm the algorithm that should be used to hash the password. + * @param password the password to be hashed. It will be padded according to the HMAC block size. + * @return the constructed {@link Mac} instance. + */ + private static Mac getMacInstance(final String algorithm, final byte[] password) throws NoSuchAlgorithmException, InvalidKeyException { + byte[] padded = hmacPad(algorithm, password); + //byte[] padded = password; + switch (algorithm) { + case ALGORITHM_SCRAM_SHA_1: { + Mac hmac = Mac.getInstance(HMAC_SHA1_ALGORITHM); + Key key = new SecretKeySpec(padded, HMAC_SHA1_ALGORITHM); + hmac.init(key); + return hmac; + } + case ALGORITHM_SCRAM_SHA_256: { + Mac hmac = Mac.getInstance(HMAC_SHA256_ALGORITHM); + Key key = new SecretKeySpec(padded, HMAC_SHA256_ALGORITHM); + hmac.init(key); + return hmac; + } + case ALGORITHM_SCRAM_SHA_384: { + Mac hmac = Mac.getInstance(HMAC_SHA384_ALGORITHM); + Key key = new SecretKeySpec(padded, HMAC_SHA384_ALGORITHM); + hmac.init(key); + return hmac; + } + case ALGORITHM_SCRAM_SHA_512: { + Mac hmac = Mac.getInstance(HMAC_SHA512_ALGORITHM); + Key key = new SecretKeySpec(padded, HMAC_SHA512_ALGORITHM); + hmac.init(key); + return hmac; + } + default: + throw log.noSuchAlgorithmInvalidAlgorithm(algorithm); + } + } + + private static byte[] hmacPad(final String algorithm, byte[] password) throws NoSuchAlgorithmException { + int blockSize; + String digestAlgo; + switch (algorithm) { + case ScramDigestPassword.ALGORITHM_SCRAM_SHA_1: + blockSize = 64; + digestAlgo = "SHA-1"; + break; + case ScramDigestPassword.ALGORITHM_SCRAM_SHA_256: + blockSize = 64; + digestAlgo = "SHA-256"; + break; + case ScramDigestPassword.ALGORITHM_SCRAM_SHA_384: + blockSize = 128; + digestAlgo = "SHA-384"; + break; + case ScramDigestPassword.ALGORITHM_SCRAM_SHA_512: + blockSize = 128; + digestAlgo = "SHA-512"; + break; + default: + throw log.noSuchAlgorithmInvalidAlgorithm(algorithm); + } + byte[] bytes = new byte[blockSize]; + if (password.length > blockSize) { + MessageDigest md = MessageDigest.getInstance(digestAlgo); + password = md.digest(password); + } + System.arraycopy(password, 0, bytes, 0, Math.min(password.length, blockSize)); + return bytes; + } + + public int hashCode() { + return multiHashOrdered(multiHashOrdered(multiHashOrdered(Arrays.hashCode(digest), Arrays.hashCode(salt)), iterationCount), algorithm.hashCode()); + } + + public boolean equals(final Object obj) { + if (! (obj instanceof ScramDigestPasswordImpl)) { + return false; + } + ScramDigestPasswordImpl other = (ScramDigestPasswordImpl) obj; + return iterationCount == other.iterationCount && algorithm.equals(other.algorithm) && MessageDigest.isEqual(digest, other.digest) && Arrays.equals(salt, other.salt); + } + + private void readObject(ObjectInputStream ignored) throws NotSerializableException { + throw new NotSerializableException(); + } + + Object writeReplace() { + return ScramDigestPassword.createRaw(algorithm, digest, salt, iterationCount); + } + + public ScramDigestPasswordImpl clone() { + return this; + } + +} Index: 3rdParty_sources/elytron/org/wildfly/security/password/impl/SimpleDigestPasswordImpl.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/password/impl/SimpleDigestPasswordImpl.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/password/impl/SimpleDigestPasswordImpl.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,152 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2014 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.password.impl; + +import static org.wildfly.common.math.HashMath.multiHashOrdered; +import static org.wildfly.security.password.impl.ElytronMessages.log; + +import java.io.NotSerializableException; +import java.io.ObjectInputStream; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.security.InvalidKeyException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.KeySpec; +import java.util.Arrays; + +import org.wildfly.security.password.interfaces.SimpleDigestPassword; +import org.wildfly.security.password.spec.ClearPasswordSpec; +import org.wildfly.security.password.spec.HashPasswordSpec; + +/** + * @author David M. Lloyd + */ +class SimpleDigestPasswordImpl extends AbstractPasswordImpl implements SimpleDigestPassword { + + private static final long serialVersionUID = -5673285507422174313L; + + private final String algorithm; + private final byte[] digest; + + SimpleDigestPasswordImpl(final String algorithm, final byte[] digest) { + this.algorithm = algorithm; + this.digest = digest; + } + + SimpleDigestPasswordImpl(final String algorithm, final HashPasswordSpec spec) { + this(algorithm, spec.getDigest().clone()); + } + + SimpleDigestPasswordImpl(final SimpleDigestPassword password) { + this(password.getAlgorithm(), password.getDigest().clone()); + } + + SimpleDigestPasswordImpl(final String algorithm, final ClearPasswordSpec spec) throws InvalidKeySpecException { + this(algorithm, spec.getEncodedPassword(), StandardCharsets.UTF_8); + } + + SimpleDigestPasswordImpl(final String algorithm, final char[] chars, final Charset hashCharset) throws InvalidKeySpecException { + this.algorithm = algorithm; + this.digest = getDigestOfKS(algorithm, chars, hashCharset); + } + + S getKeySpec(final Class keySpecType) throws InvalidKeySpecException { + if (keySpecType.isAssignableFrom(HashPasswordSpec.class)) { + return keySpecType.cast(new HashPasswordSpec(digest.clone())); + } + throw new InvalidKeySpecException(); + } + + static byte[] getDigestOfKS(String algorithm, char[] chars, Charset hashCharset) throws InvalidKeySpecException { + try { + return getDigestOf(algorithm, chars, hashCharset); + } catch (NoSuchAlgorithmException e) { + throw log.invalidKeySpecNoSuchMessageDigestAlgorithm(algorithm); + } + } + + static byte[] getDigestOf(String algorithm, char[] chars, Charset hashCharset) throws NoSuchAlgorithmException { + final MessageDigest md = getMessageDigest(algorithm); + md.update(new String(chars).getBytes(hashCharset)); + return md.digest(); + } + + static MessageDigest getMessageDigest(String algorithm) throws NoSuchAlgorithmException { + switch (algorithm) { + case ALGORITHM_SIMPLE_DIGEST_MD2: return MessageDigest.getInstance("MD2"); + case ALGORITHM_SIMPLE_DIGEST_MD5: return MessageDigest.getInstance("MD5"); + case ALGORITHM_SIMPLE_DIGEST_SHA_1: return MessageDigest.getInstance("SHA-1"); + case ALGORITHM_SIMPLE_DIGEST_SHA_256: return MessageDigest.getInstance("SHA-256"); + case ALGORITHM_SIMPLE_DIGEST_SHA_384: return MessageDigest.getInstance("SHA-384"); + case ALGORITHM_SIMPLE_DIGEST_SHA_512: return MessageDigest.getInstance("SHA-512"); + default: throw log.noSuchAlgorithmInvalidAlgorithm(algorithm); + } + } + + boolean verify(final char[] guess) throws InvalidKeyException { + return verify(guess, StandardCharsets.UTF_8); + } + + @Override + boolean verify(char[] guess, Charset hashCharset) throws InvalidKeyException { + try { + return MessageDigest.isEqual(digest, getDigestOf(algorithm, guess, hashCharset)); + } catch (NoSuchAlgorithmException e) { + throw log.invalidKeyNoSuchMessageDigestAlgorithm(algorithm); + } + } + + boolean convertibleTo(final Class keySpecType) { + return keySpecType.isAssignableFrom(HashPasswordSpec.class); + } + + public String getAlgorithm() { + return algorithm; + } + + public byte[] getDigest() { + return digest.clone(); + } + + public int hashCode() { + return multiHashOrdered(Arrays.hashCode(digest), algorithm.hashCode()); + } + + public boolean equals(final Object obj) { + if (! (obj instanceof SimpleDigestPasswordImpl)) { + return false; + } + SimpleDigestPasswordImpl other = (SimpleDigestPasswordImpl) obj; + return algorithm.equals(other.algorithm) && MessageDigest.isEqual(digest, other.digest); + } + + private void readObject(ObjectInputStream ignored) throws NotSerializableException { + throw new NotSerializableException(); + } + + Object writeReplace() { + return SimpleDigestPassword.createRaw(algorithm, digest); + } + + public SimpleDigestPasswordImpl clone() { + return this; + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/password/impl/SunUnixMD5CryptPasswordImpl.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/password/impl/SunUnixMD5CryptPasswordImpl.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/password/impl/SunUnixMD5CryptPasswordImpl.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,315 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2014 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.password.impl; + +import static org.wildfly.common.math.HashMath.multiHashOrdered; +import static org.wildfly.security.password.impl.ElytronMessages.log; + +import java.io.NotSerializableException; +import java.io.ObjectInputStream; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.security.InvalidKeyException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.KeySpec; +import java.util.Arrays; + +import org.wildfly.common.Assert; +import org.wildfly.security.password.spec.IteratedPasswordAlgorithmSpec; +import org.wildfly.security.password.spec.SaltedPasswordAlgorithmSpec; +import org.wildfly.security.password.interfaces.SunUnixMD5CryptPassword; +import org.wildfly.security.password.spec.ClearPasswordSpec; +import org.wildfly.security.password.spec.IteratedSaltedPasswordAlgorithmSpec; +import org.wildfly.security.password.spec.IteratedSaltedHashPasswordSpec; +import org.wildfly.security.password.spec.SaltedHashPasswordSpec; + +/** + * Implementation of the Sun variant of the Unix MD5 Crypt password. + * + * @author Farah Juma + */ +final class SunUnixMD5CryptPasswordImpl extends AbstractPasswordImpl implements SunUnixMD5CryptPassword { + + private static final long serialVersionUID = 2894797156094167807L; + + static final String MD5 = "MD5"; + static final byte[] MAGIC_BYTES = "$md5$".getBytes(StandardCharsets.UTF_8); + static final byte[] MAGIC_BYTES_WITH_ROUNDS = "$md5,rounds=".getBytes(StandardCharsets.UTF_8); + static final byte[] SEPARATOR_BYTES = "$".getBytes(StandardCharsets.UTF_8); + static final int BASIC_ROUND_COUNT = 4096; + + private final String algorithm; + private final byte[] hash; + private final byte[] salt; + private final int iterationCount; + + // Excerpt from Hamlet III.ii that's used by the Muffet Coin Toss algorithm + // (the excerpt was taken from Project Gutenberg: ftp://metalab.unc.edu/pub/docs/books/gutenberg/etext98/2ws2610.txt) + private static final String HAMLET_EXCERPT = "To be, or not to be,--that is the question:--\n" + + "Whether 'tis nobler in the mind to suffer\n" + + "The slings and arrows of outrageous fortune\n" + + "Or to take arms against a sea of troubles,\n" + + "And by opposing end them?--To die,--to sleep,--\n" + + "No more; and by a sleep to say we end\n" + + "The heartache, and the thousand natural shocks\n" + + "That flesh is heir to,--'tis a consummation\n" + + "Devoutly to be wish'd. To die,--to sleep;--\n" + + "To sleep! perchance to dream:--ay, there's the rub;\n" + + "For in that sleep of death what dreams may come,\n" + + "When we have shuffled off this mortal coil,\n" + + "Must give us pause: there's the respect\n" + + "That makes calamity of so long life;\n" + + "For who would bear the whips and scorns of time,\n" + + "The oppressor's wrong, the proud man's contumely,\n" + + "The pangs of despis'd love, the law's delay,\n" + + "The insolence of office, and the spurns\n" + + "That patient merit of the unworthy takes,\n" + + "When he himself might his quietus make\n" + + "With a bare bodkin? who would these fardels bear,\n" + + "To grunt and sweat under a weary life,\n" + + "But that the dread of something after death,--\n" + + "The undiscover'd country, from whose bourn\n" + + "No traveller returns,--puzzles the will,\n" + + "And makes us rather bear those ills we have\n" + + "Than fly to others that we know not of?\n" + + "Thus conscience does make cowards of us all;\n" + + "And thus the native hue of resolution\n" + + "Is sicklied o'er with the pale cast of thought;\n" + + "And enterprises of great pith and moment,\n" + + "With this regard, their currents turn awry,\n" + + "And lose the name of action.--Soft you now!\n" + + "The fair Ophelia!--Nymph, in thy orisons\n" + + "Be all my sins remember'd.\n\0"; // trailing null character is needed + + SunUnixMD5CryptPasswordImpl(final String algorithm, final byte[] clonedHash, final byte[] clonedSalt, final int iterationCount) { + Assert.checkNotNullParam("algorithm", algorithm); + if (!algorithm.equals(ALGORITHM_SUN_CRYPT_MD5) && !algorithm.equals(ALGORITHM_SUN_CRYPT_MD5_BARE_SALT)) { + throw log.unrecognizedAlgorithm(algorithm); + } + + this.algorithm = algorithm; + this.hash = clonedHash; + this.salt = clonedSalt; + this.iterationCount = iterationCount; + } + + SunUnixMD5CryptPasswordImpl(SunUnixMD5CryptPassword password) { + this(password.getAlgorithm(), password.getHash().clone(), password.getSalt().clone(), password.getIterationCount()); + } + + SunUnixMD5CryptPasswordImpl(final String algorithm, final IteratedSaltedHashPasswordSpec spec) { + this(algorithm, spec.getHash().clone(), spec.getSalt().clone(), spec.getIterationCount()); + } + + SunUnixMD5CryptPasswordImpl(final String algorithm, final SaltedHashPasswordSpec spec) { + this(algorithm, spec.getHash().clone(), spec.getSalt().clone(), DEFAULT_ITERATION_COUNT); + } + + SunUnixMD5CryptPasswordImpl(final ClearPasswordSpec spec) throws NoSuchAlgorithmException { + this.algorithm = ALGORITHM_SUN_CRYPT_MD5; + this.salt = PasswordUtil.generateRandomSalt(DEFAULT_SALT_SIZE); + this.iterationCount = DEFAULT_ITERATION_COUNT; + this.hash = sunMD5Crypt(algorithm, getNormalizedPasswordBytes(spec.getEncodedPassword()), salt, iterationCount); + } + + SunUnixMD5CryptPasswordImpl(final String algorithm, final char[] password, final Charset hashCharset) throws NoSuchAlgorithmException { + this(algorithm, password, PasswordUtil.generateRandomSalt(DEFAULT_SALT_SIZE), DEFAULT_ITERATION_COUNT, hashCharset); + } + + SunUnixMD5CryptPasswordImpl(final String algorithm, final char[] password, final IteratedSaltedPasswordAlgorithmSpec spec, final Charset hashCharset) throws NoSuchAlgorithmException { + this(algorithm, password, spec.getSalt().clone(), spec.getIterationCount(), hashCharset); + } + + SunUnixMD5CryptPasswordImpl(final String algorithm, final char[] password, final SaltedPasswordAlgorithmSpec spec, final Charset hashCharset) throws NoSuchAlgorithmException { + this(algorithm, password, spec.getSalt().clone(), DEFAULT_ITERATION_COUNT, hashCharset); + } + + SunUnixMD5CryptPasswordImpl(final String algorithm, final char[] password, final IteratedPasswordAlgorithmSpec spec, final Charset hashCharset) throws NoSuchAlgorithmException { + this(algorithm, password, PasswordUtil.generateRandomSalt(DEFAULT_SALT_SIZE), spec.getIterationCount(), hashCharset); + } + + private SunUnixMD5CryptPasswordImpl(final String algorithm, final char[] password, final byte[] clonedSalt, final int iterationCount, final Charset hashCharset) + throws NoSuchAlgorithmException { + this(algorithm, sunMD5Crypt(algorithm, getNormalizedPasswordBytes(password, hashCharset), clonedSalt, iterationCount), clonedSalt, iterationCount); + } + + @Override + public String getAlgorithm() { + return algorithm; + } + + @Override + public byte[] getHash() { + return hash.clone(); + } + + @Override + public byte[] getSalt() { + return salt.clone(); + } + + @Override + public int getIterationCount() { + return iterationCount; + } + + @Override + S getKeySpec(final Class keySpecType) throws InvalidKeySpecException { + if (keySpecType.isAssignableFrom(IteratedSaltedHashPasswordSpec.class)) { + return keySpecType.cast(new IteratedSaltedHashPasswordSpec(getHash(), getSalt(), getIterationCount())); + } + throw new InvalidKeySpecException(); + } + + @Override + boolean verify(final char[] guess) throws InvalidKeyException { + return verify(guess, StandardCharsets.UTF_8); + } + + @Override + boolean verify(char[] guess, Charset hashCharset) throws InvalidKeyException { + byte[] test; + try { + test = sunMD5Crypt(getAlgorithm(), getNormalizedPasswordBytes(guess, hashCharset), getSalt(), getIterationCount()); + } catch (NoSuchAlgorithmException e) { + throw log.invalidKeyCannotVerifyPassword(e); + } + return MessageDigest.isEqual(getHash(), test); + } + + @Override + boolean convertibleTo(final Class keySpecType) { + return keySpecType.isAssignableFrom(IteratedSaltedHashPasswordSpec.class); + } + + /** + * Hashes the given password using the Sun variant of the MD5 Crypt algorithm. + * + * @param algorithm the algorithm to be used. Possible values are available as constants on {link}SunUnixMD5CryptPassword{link} + * @param password the password to be hashed + * @param salt the salt + * @param iterationCount the number of additional iterations to use + * @return a {@code byte[]} containing the hashed password + * @throws NoSuchAlgorithmException if a {@code MessageDigest} object that implements MD5 cannot be retrieved + */ + static byte[] sunMD5Crypt(final String algorithm, final byte[] password, final byte[] salt, final int iterationCount) throws NoSuchAlgorithmException { + // Add the password to the digest first + MessageDigest digest = getMD5MessageDigest(); + digest.update(password); + + // Now add the magic bytes, followed by the number of rounds (if specified), followed by the salt + if (iterationCount == 0) { + digest.update(MAGIC_BYTES); + } else { + digest.update(MAGIC_BYTES_WITH_ROUNDS); + digest.update(Integer.toString(iterationCount).getBytes(StandardCharsets.UTF_8)); + digest.update(SEPARATOR_BYTES); + } + digest.update(salt); + + if (algorithm.equals(ALGORITHM_SUN_CRYPT_MD5)) { + // Include the trailing "$" after the salt + digest.update(SEPARATOR_BYTES); + } + + byte[] result = digest.digest(); + + int actualIterationCount = BASIC_ROUND_COUNT + iterationCount; + int a, b, v, x, y; + int[] unsignedResult = new int[16]; + for (int round = 0; round < actualIterationCount; round++) { + digest.reset(); + + // Add the previous digest + digest.update(result, 0, 16); + + for(int i = 0; i < 16; i++) { + unsignedResult[i] = result[i] & 0xff; + } + + x = 0; + y = 0; + for (int i = 0; i < 8; i++) { + // Build up x (an 8-bit integer) + a = unsignedResult[i]; + b = unsignedResult[i+3]; + v = unsignedResult[(a >> (b % 5)) & 0x0f] >> ((b >> (a & 0x07)) & 0x01); + x |= (getDigestBit(unsignedResult, v) << i); + + // Build up y (an 8-bit integer) + a = unsignedResult[i+8]; + b = unsignedResult[(i+11) & 0x0f]; + v = unsignedResult[(a >> (b % 5)) & 0x0f] >> ((b >> (a & 0x07)) & 0x01); + y |= (getDigestBit(unsignedResult, v) << i); + } + + // Only the top 7 or bottom 7 bits will be used + x = (x >> getDigestBit(unsignedResult, round)) & 0x7f; + y = (y >> getDigestBit(unsignedResult, round + 64)) & 0x7f; + + // If the coin toss results in a 1, add a constant phrase to the digest + int muffetCoinToss = getDigestBit(unsignedResult, x) ^ getDigestBit(unsignedResult, y); + if (muffetCoinToss == 1) { + digest.update(HAMLET_EXCERPT.getBytes(StandardCharsets.UTF_8)); + } + + // Add the ASCII representation of the current round to the digest + digest.update(Integer.toString(round).getBytes(StandardCharsets.US_ASCII)); + result = digest.digest(); + } + + Arrays.fill(unsignedResult, 0); + return result; + } + + private static int getDigestBit(int[] unsignedResult, int bitPosition) { + return (unsignedResult[(bitPosition >> 3) & 0x0f] >> (bitPosition & 0x07)) & 0x01; + } + + private static MessageDigest getMD5MessageDigest() throws NoSuchAlgorithmException { + return MessageDigest.getInstance(MD5); + } + + public int hashCode() { + return multiHashOrdered(multiHashOrdered(multiHashOrdered(Arrays.hashCode(hash), Arrays.hashCode(salt)), iterationCount), algorithm.hashCode()); + } + + public boolean equals(final Object obj) { + if (! (obj instanceof SunUnixMD5CryptPasswordImpl)) { + return false; + } + SunUnixMD5CryptPasswordImpl other = (SunUnixMD5CryptPasswordImpl) obj; + return iterationCount == other.iterationCount && algorithm.equals(other.algorithm) && MessageDigest.isEqual(hash, other.hash) && Arrays.equals(salt, other.salt); + } + + private void readObject(ObjectInputStream ignored) throws NotSerializableException { + throw new NotSerializableException(); + } + + Object writeReplace() { + return SunUnixMD5CryptPassword.createRaw(algorithm, salt, hash, iterationCount); + } + + public SunUnixMD5CryptPasswordImpl clone() { + return this; + } + +} Index: 3rdParty_sources/elytron/org/wildfly/security/password/impl/UnixDESCryptPasswordImpl.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/password/impl/UnixDESCryptPasswordImpl.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/password/impl/UnixDESCryptPasswordImpl.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,430 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2014 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.password.impl; + +import static org.wildfly.common.math.HashMath.multiHashOrdered; +import static org.wildfly.security.password.impl.ElytronMessages.log; + +import java.io.NotSerializableException; +import java.io.ObjectInputStream; +import java.nio.ByteBuffer; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.security.InvalidKeyException; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.InvalidParameterSpecException; +import java.security.spec.KeySpec; +import java.util.Arrays; +import java.util.concurrent.ThreadLocalRandom; + +import org.wildfly.security.password.interfaces.UnixDESCryptPassword; +import org.wildfly.security.password.spec.ClearPasswordSpec; +import org.wildfly.security.password.spec.SaltedHashPasswordSpec; +import org.wildfly.security.password.spec.SaltedPasswordAlgorithmSpec; + +/** + * @author David M. Lloyd + */ +class UnixDESCryptPasswordImpl extends AbstractPasswordImpl implements UnixDESCryptPassword { + + private static final long serialVersionUID = -1531925386776476431L; + private final short salt; + private final byte[] hash; + + UnixDESCryptPasswordImpl(final short salt, final byte[] hash) throws InvalidKeyException { + this.salt = salt; + if (hash == null || hash.length != 8) { + throw log.invalidKeyDesCryptPasswordHashMustBeBytes(8); + } + this.hash = hash; + } + + UnixDESCryptPasswordImpl(final byte[] saltBytes, final byte[] hash) throws InvalidParameterSpecException, InvalidKeyException { + this(saltFromBytes(saltBytes), hash); + } + + UnixDESCryptPasswordImpl(final byte[] saltBytes, final char[] passwordChars, final Charset hashCharset) throws InvalidParameterSpecException, InvalidKeyException { + this(saltFromBytes(saltBytes), passwordChars, hashCharset); + } + + UnixDESCryptPasswordImpl(final SaltedHashPasswordSpec spec) throws InvalidKeySpecException, InvalidParameterSpecException, InvalidKeyException { + this(spec.getSalt(), spec.getHash().clone()); + } + + UnixDESCryptPasswordImpl(final ClearPasswordSpec spec) throws InvalidKeySpecException, InvalidKeyException { + this((short) (ThreadLocalRandom.current().nextInt() & 0xfff), spec.getEncodedPassword()); + } + + UnixDESCryptPasswordImpl(final char[] passwordChars, final Charset hashCharset) throws InvalidKeyException { + this((short) (ThreadLocalRandom.current().nextInt() & 0xfff), passwordChars, hashCharset); + } + + UnixDESCryptPasswordImpl(final char[] passwordChars, SaltedPasswordAlgorithmSpec algorithmSpec, final Charset hashCharset) throws InvalidParameterSpecException, InvalidKeyException { + this(algorithmSpec.getSalt(), passwordChars, hashCharset); + } + + UnixDESCryptPasswordImpl(final UnixDESCryptPassword password) throws InvalidKeyException { + this(password.getSalt(), password.getHash()); + } + + UnixDESCryptPasswordImpl(final short salt, final char[] passwordChars) throws InvalidKeyException { + this(salt, generateHash(salt, passwordChars)); + } + + UnixDESCryptPasswordImpl(final short salt, final char[] passwordChars, final Charset hashCharset) throws InvalidKeyException { + this(salt, generateHash(salt, passwordChars, hashCharset)); + } + + private static short saltFromBytes(final byte[] saltBytes) throws InvalidParameterSpecException { + if (saltBytes.length != 2) { + throw log.invalidParameterSpecSaltMustBeBytesBits(2, 12); + } + return (short) ((saltBytes[0] & 0x0f) << 8 | saltBytes[1] & 0xff); + } + + S getKeySpec(final Class keySpecType) throws InvalidKeySpecException { + if (keySpecType.isAssignableFrom(SaltedHashPasswordSpec.class)) { + return keySpecType.cast(new SaltedHashPasswordSpec(hash.clone(), ByteBuffer.allocate(2).putShort(this.salt).array())); + } + throw new InvalidKeySpecException(); + } + + boolean verify(final char[] guess) throws InvalidKeyException { + return verify(guess, StandardCharsets.UTF_8); + } + + @Override + boolean verify(char[] guess, Charset hashCharset) throws InvalidKeyException { + return Arrays.equals(hash, generateHash(salt, guess, hashCharset)); + } + + boolean convertibleTo(final Class keySpecType) { + return keySpecType.isAssignableFrom(SaltedHashPasswordSpec.class); + } + + public String getAlgorithm() { + return UnixDESCryptPassword.ALGORITHM_CRYPT_DES; + } + + public short getSalt() { + return salt; + } + + public byte[] getHash() { + return hash.clone(); + } + + private static byte[] generateHash(final short salt, final char[] password) { + return generateHash(salt, password, StandardCharsets.UTF_8); + } + + private static byte[] generateHash(final short salt, final char[] password, final Charset hashCharset) { + // get bytes + final byte[] bytes1 = getNormalizedPasswordBytes(password, hashCharset); + return crypt(bytes1, salt); + } + + // Much of the following code was copied from Apache commons-codec; at some point we may consider just adding + // a dependency? + + private static final boolean[] SHIFT2 = { false, false, true, true, true, true, true, true, false, true, true, + true, true, true, true, false }; + + private static final int[][] SKB = { + { 0, 16, 0x20000000, 0x20000010, 0x10000, 0x10010, 0x20010000, 0x20010010, 2048, 2064, 0x20000800, + 0x20000810, 0x10800, 0x10810, 0x20010800, 0x20010810, 32, 48, 0x20000020, 0x20000030, 0x10020, + 0x10030, 0x20010020, 0x20010030, 2080, 2096, 0x20000820, 0x20000830, 0x10820, 0x10830, 0x20010820, + 0x20010830, 0x80000, 0x80010, 0x20080000, 0x20080010, 0x90000, 0x90010, 0x20090000, 0x20090010, + 0x80800, 0x80810, 0x20080800, 0x20080810, 0x90800, 0x90810, 0x20090800, 0x20090810, 0x80020, + 0x80030, 0x20080020, 0x20080030, 0x90020, 0x90030, 0x20090020, 0x20090030, 0x80820, 0x80830, + 0x20080820, 0x20080830, 0x90820, 0x90830, 0x20090820, 0x20090830 }, + { 0, 0x2000000, 8192, 0x2002000, 0x200000, 0x2200000, 0x202000, 0x2202000, 4, 0x2000004, 8196, 0x2002004, + 0x200004, 0x2200004, 0x202004, 0x2202004, 1024, 0x2000400, 9216, 0x2002400, 0x200400, 0x2200400, + 0x202400, 0x2202400, 1028, 0x2000404, 9220, 0x2002404, 0x200404, 0x2200404, 0x202404, 0x2202404, + 0x10000000, 0x12000000, 0x10002000, 0x12002000, 0x10200000, 0x12200000, 0x10202000, 0x12202000, + 0x10000004, 0x12000004, 0x10002004, 0x12002004, 0x10200004, 0x12200004, 0x10202004, 0x12202004, + 0x10000400, 0x12000400, 0x10002400, 0x12002400, 0x10200400, 0x12200400, 0x10202400, 0x12202400, + 0x10000404, 0x12000404, 0x10002404, 0x12002404, 0x10200404, 0x12200404, 0x10202404, 0x12202404 }, + { 0, 1, 0x40000, 0x40001, 0x1000000, 0x1000001, 0x1040000, 0x1040001, 2, 3, 0x40002, 0x40003, 0x1000002, + 0x1000003, 0x1040002, 0x1040003, 512, 513, 0x40200, 0x40201, 0x1000200, 0x1000201, 0x1040200, + 0x1040201, 514, 515, 0x40202, 0x40203, 0x1000202, 0x1000203, 0x1040202, 0x1040203, 0x8000000, + 0x8000001, 0x8040000, 0x8040001, 0x9000000, 0x9000001, 0x9040000, 0x9040001, 0x8000002, 0x8000003, + 0x8040002, 0x8040003, 0x9000002, 0x9000003, 0x9040002, 0x9040003, 0x8000200, 0x8000201, 0x8040200, + 0x8040201, 0x9000200, 0x9000201, 0x9040200, 0x9040201, 0x8000202, 0x8000203, 0x8040202, 0x8040203, + 0x9000202, 0x9000203, 0x9040202, 0x9040203 }, + { 0, 0x100000, 256, 0x100100, 8, 0x100008, 264, 0x100108, 4096, 0x101000, 4352, 0x101100, 4104, 0x101008, + 4360, 0x101108, 0x4000000, 0x4100000, 0x4000100, 0x4100100, 0x4000008, 0x4100008, 0x4000108, + 0x4100108, 0x4001000, 0x4101000, 0x4001100, 0x4101100, 0x4001008, 0x4101008, 0x4001108, 0x4101108, + 0x20000, 0x120000, 0x20100, 0x120100, 0x20008, 0x120008, 0x20108, 0x120108, 0x21000, 0x121000, + 0x21100, 0x121100, 0x21008, 0x121008, 0x21108, 0x121108, 0x4020000, 0x4120000, 0x4020100, + 0x4120100, 0x4020008, 0x4120008, 0x4020108, 0x4120108, 0x4021000, 0x4121000, 0x4021100, 0x4121100, + 0x4021008, 0x4121008, 0x4021108, 0x4121108 }, + { 0, 0x10000000, 0x10000, 0x10010000, 4, 0x10000004, 0x10004, 0x10010004, 0x20000000, 0x30000000, + 0x20010000, 0x30010000, 0x20000004, 0x30000004, 0x20010004, 0x30010004, 0x100000, 0x10100000, + 0x110000, 0x10110000, 0x100004, 0x10100004, 0x110004, 0x10110004, 0x20100000, 0x30100000, + 0x20110000, 0x30110000, 0x20100004, 0x30100004, 0x20110004, 0x30110004, 4096, 0x10001000, 0x11000, + 0x10011000, 4100, 0x10001004, 0x11004, 0x10011004, 0x20001000, 0x30001000, 0x20011000, 0x30011000, + 0x20001004, 0x30001004, 0x20011004, 0x30011004, 0x101000, 0x10101000, 0x111000, 0x10111000, + 0x101004, 0x10101004, 0x111004, 0x10111004, 0x20101000, 0x30101000, 0x20111000, 0x30111000, + 0x20101004, 0x30101004, 0x20111004, 0x30111004 }, + { 0, 0x8000000, 8, 0x8000008, 1024, 0x8000400, 1032, 0x8000408, 0x20000, 0x8020000, 0x20008, 0x8020008, + 0x20400, 0x8020400, 0x20408, 0x8020408, 1, 0x8000001, 9, 0x8000009, 1025, 0x8000401, 1033, + 0x8000409, 0x20001, 0x8020001, 0x20009, 0x8020009, 0x20401, 0x8020401, 0x20409, 0x8020409, + 0x2000000, 0xa000000, 0x2000008, 0xa000008, 0x2000400, 0xa000400, 0x2000408, 0xa000408, 0x2020000, + 0xa020000, 0x2020008, 0xa020008, 0x2020400, 0xa020400, 0x2020408, 0xa020408, 0x2000001, 0xa000001, + 0x2000009, 0xa000009, 0x2000401, 0xa000401, 0x2000409, 0xa000409, 0x2020001, 0xa020001, 0x2020009, + 0xa020009, 0x2020401, 0xa020401, 0x2020409, 0xa020409 }, + { 0, 256, 0x80000, 0x80100, 0x1000000, 0x1000100, 0x1080000, 0x1080100, 16, 272, 0x80010, 0x80110, + 0x1000010, 0x1000110, 0x1080010, 0x1080110, 0x200000, 0x200100, 0x280000, 0x280100, 0x1200000, + 0x1200100, 0x1280000, 0x1280100, 0x200010, 0x200110, 0x280010, 0x280110, 0x1200010, 0x1200110, + 0x1280010, 0x1280110, 512, 768, 0x80200, 0x80300, 0x1000200, 0x1000300, 0x1080200, 0x1080300, 528, + 784, 0x80210, 0x80310, 0x1000210, 0x1000310, 0x1080210, 0x1080310, 0x200200, 0x200300, 0x280200, + 0x280300, 0x1200200, 0x1200300, 0x1280200, 0x1280300, 0x200210, 0x200310, 0x280210, 0x280310, + 0x1200210, 0x1200310, 0x1280210, 0x1280310 }, + { 0, 0x4000000, 0x40000, 0x4040000, 2, 0x4000002, 0x40002, 0x4040002, 8192, 0x4002000, 0x42000, 0x4042000, + 8194, 0x4002002, 0x42002, 0x4042002, 32, 0x4000020, 0x40020, 0x4040020, 34, 0x4000022, 0x40022, + 0x4040022, 8224, 0x4002020, 0x42020, 0x4042020, 8226, 0x4002022, 0x42022, 0x4042022, 2048, + 0x4000800, 0x40800, 0x4040800, 2050, 0x4000802, 0x40802, 0x4040802, 10240, 0x4002800, 0x42800, + 0x4042800, 10242, 0x4002802, 0x42802, 0x4042802, 2080, 0x4000820, 0x40820, 0x4040820, 2082, + 0x4000822, 0x40822, 0x4040822, 10272, 0x4002820, 0x42820, 0x4042820, 10274, 0x4002822, 0x42822, + 0x4042822 } }; + + private static final int[][] SP_TRANS = { + { 0x820200, 0x20000, 0x80800000, 0x80820200, 0x800000, 0x80020200, 0x80020000, 0x80800000, 0x80020200, + 0x820200, 0x820000, 0x80000200, 0x80800200, 0x800000, 0, 0x80020000, 0x20000, 0x80000000, + 0x800200, 0x20200, 0x80820200, 0x820000, 0x80000200, 0x800200, 0x80000000, 512, 0x20200, + 0x80820000, 512, 0x80800200, 0x80820000, 0, 0, 0x80820200, 0x800200, 0x80020000, 0x820200, + 0x20000, 0x80000200, 0x800200, 0x80820000, 512, 0x20200, 0x80800000, 0x80020200, 0x80000000, + 0x80800000, 0x820000, 0x80820200, 0x20200, 0x820000, 0x80800200, 0x800000, 0x80000200, 0x80020000, + 0, 0x20000, 0x800000, 0x80800200, 0x820200, 0x80000000, 0x80820000, 512, 0x80020200 }, + { 0x10042004, 0, 0x42000, 0x10040000, 0x10000004, 8196, 0x10002000, 0x42000, 8192, 0x10040004, 4, + 0x10002000, 0x40004, 0x10042000, 0x10040000, 4, 0x40000, 0x10002004, 0x10040004, 8192, 0x42004, + 0x10000000, 0, 0x40004, 0x10002004, 0x42004, 0x10042000, 0x10000004, 0x10000000, 0x40000, 8196, + 0x10042004, 0x40004, 0x10042000, 0x10002000, 0x42004, 0x10042004, 0x40004, 0x10000004, 0, + 0x10000000, 8196, 0x40000, 0x10040004, 8192, 0x10000000, 0x42004, 0x10002004, 0x10042000, 8192, 0, + 0x10000004, 4, 0x10042004, 0x42000, 0x10040000, 0x10040004, 0x40000, 8196, 0x10002000, 0x10002004, + 4, 0x10040000, 0x42000 }, + { 0x41000000, 0x1010040, 64, 0x41000040, 0x40010000, 0x1000000, 0x41000040, 0x10040, 0x1000040, 0x10000, + 0x1010000, 0x40000000, 0x41010040, 0x40000040, 0x40000000, 0x41010000, 0, 0x40010000, 0x1010040, + 64, 0x40000040, 0x41010040, 0x10000, 0x41000000, 0x41010000, 0x1000040, 0x40010040, 0x1010000, + 0x10040, 0, 0x1000000, 0x40010040, 0x1010040, 64, 0x40000000, 0x10000, 0x40000040, 0x40010000, + 0x1010000, 0x41000040, 0, 0x1010040, 0x10040, 0x41010000, 0x40010000, 0x1000000, 0x41010040, + 0x40000000, 0x40010040, 0x41000000, 0x1000000, 0x41010040, 0x10000, 0x1000040, 0x41000040, + 0x10040, 0x1000040, 0, 0x41010000, 0x40000040, 0x41000000, 0x40010040, 64, 0x1010000 }, + { 0x100402, 0x4000400, 2, 0x4100402, 0, 0x4100000, 0x4000402, 0x100002, 0x4100400, 0x4000002, 0x4000000, + 1026, 0x4000002, 0x100402, 0x100000, 0x4000000, 0x4100002, 0x100400, 1024, 2, 0x100400, 0x4000402, + 0x4100000, 1024, 1026, 0, 0x100002, 0x4100400, 0x4000400, 0x4100002, 0x4100402, 0x100000, + 0x4100002, 1026, 0x100000, 0x4000002, 0x100400, 0x4000400, 2, 0x4100000, 0x4000402, 0, 1024, + 0x100002, 0, 0x4100002, 0x4100400, 1024, 0x4000000, 0x4100402, 0x100402, 0x100000, 0x4100402, 2, + 0x4000400, 0x100402, 0x100002, 0x100400, 0x4100000, 0x4000402, 1026, 0x4000000, 0x4000002, + 0x4100400 }, + { 0x2000000, 16384, 256, 0x2004108, 0x2004008, 0x2000100, 16648, 0x2004000, 16384, 8, 0x2000008, 16640, + 0x2000108, 0x2004008, 0x2004100, 0, 16640, 0x2000000, 16392, 264, 0x2000100, 16648, 0, 0x2000008, + 8, 0x2000108, 0x2004108, 16392, 0x2004000, 256, 264, 0x2004100, 0x2004100, 0x2000108, 16392, + 0x2004000, 16384, 8, 0x2000008, 0x2000100, 0x2000000, 16640, 0x2004108, 0, 16648, 0x2000000, 256, + 16392, 0x2000108, 256, 0, 0x2004108, 0x2004008, 0x2004100, 264, 16384, 16640, 0x2004008, + 0x2000100, 264, 8, 16648, 0x2004000, 0x2000008 }, + { 0x20000010, 0x80010, 0, 0x20080800, 0x80010, 2048, 0x20000810, 0x80000, 2064, 0x20080810, 0x80800, + 0x20000000, 0x20000800, 0x20000010, 0x20080000, 0x80810, 0x80000, 0x20000810, 0x20080010, 0, 2048, + 16, 0x20080800, 0x20080010, 0x20080810, 0x20080000, 0x20000000, 2064, 16, 0x80800, 0x80810, + 0x20000800, 2064, 0x20000000, 0x20000800, 0x80810, 0x20080800, 0x80010, 0, 0x20000800, 0x20000000, + 2048, 0x20080010, 0x80000, 0x80010, 0x20080810, 0x80800, 16, 0x20080810, 0x80800, 0x80000, + 0x20000810, 0x20000010, 0x20080000, 0x80810, 0, 2048, 0x20000010, 0x20000810, 0x20080800, + 0x20080000, 2064, 16, 0x20080010 }, + { 4096, 128, 0x400080, 0x400001, 0x401081, 4097, 4224, 0, 0x400000, 0x400081, 129, 0x401000, 1, 0x401080, + 0x401000, 129, 0x400081, 4096, 4097, 0x401081, 0, 0x400080, 0x400001, 4224, 0x401001, 4225, + 0x401080, 1, 4225, 0x401001, 128, 0x400000, 4225, 0x401000, 0x401001, 129, 4096, 128, 0x400000, + 0x401001, 0x400081, 4225, 4224, 0, 128, 0x400001, 1, 0x400080, 0, 0x400081, 0x400080, 4224, 129, + 4096, 0x401081, 0x400000, 0x401080, 1, 4097, 0x401081, 0x400001, 0x401080, 0x401000, 4097 }, + { 0x8200020, 0x8208000, 32800, 0, 0x8008000, 0x200020, 0x8200000, 0x8208020, 32, 0x8000000, 0x208000, + 32800, 0x208020, 0x8008020, 0x8000020, 0x8200000, 32768, 0x208020, 0x200020, 0x8008000, 0x8208020, + 0x8000020, 0, 0x208000, 0x8000000, 0x200000, 0x8008020, 0x8200020, 0x200000, 32768, 0x8208000, 32, + 0x200000, 32768, 0x8000020, 0x8208020, 32800, 0x8000000, 0, 0x208000, 0x8200020, 0x8008020, + 0x8008000, 0x200020, 0x8208000, 32, 0x200020, 0x8008000, 0x8208020, 0x200000, 0x8200000, + 0x8000020, 0x208000, 32800, 0x8008020, 0x8200000, 32, 0x8208000, 0x208020, 0, 0x8000000, + 0x8200020, 32768, 0x208020 } }; + + private static byte[] crypt(final byte[] original, short salt) { + final int eSwap0 = salt >> 0 & 0x3F; + final int eSwap1 = (salt >> 6 & 0x3F) << 4; + final byte[] hash = new byte[8]; + final byte[] key = new byte[8]; + for (int i = 0; i < key.length; i++) { + key[i] = 0; + } + for (int i = 0; i < key.length && i < original.length; i++) { + final int iChar = original[i]; + key[i] = (byte) (iChar << 1); + } + final int[] schedule = desSetKey(key); + final int[] out = body(schedule, eSwap0, eSwap1); + intToFourBytes(out[0], hash, 0); + intToFourBytes(out[1], hash, 4); + return hash; + } + + private static int[] body(final int[] schedule, final int eSwap0, final int eSwap1) { + int left = 0; + int right = 0; + int t; + for (int j = 0; j < 25; j++) { + for (int i = 0; i < 32; i += 4) { + left = dEncrypt(left, right, i, eSwap0, eSwap1, schedule); + right = dEncrypt(right, left, i + 2, eSwap0, eSwap1, schedule); + } + t = left; + left = right; + right = t; + } + t = right; + right = left >>> 1 | left << 31; + left = t >>> 1 | t << 31; + final int[] results = new int[2]; + permOp(right, left, 1, 0x55555555, results); + right = results[0]; + left = results[1]; + permOp(left, right, 8, 0xff00ff, results); + left = results[0]; + right = results[1]; + permOp(right, left, 2, 0x33333333, results); + right = results[0]; + left = results[1]; + permOp(left, right, 16, 65535, results); + left = results[0]; + right = results[1]; + permOp(right, left, 4, 0xf0f0f0f, results); + right = results[0]; + left = results[1]; + final int[] out = new int[2]; + out[0] = left; + out[1] = right; + return out; + } + + private static int dEncrypt(int el, final int r, final int s, final int e0, final int e1, final int[] sArr) { + int v = r ^ r >>> 16; + int u = v & e0; + v &= e1; + u = u ^ u << 16 ^ r ^ sArr[s]; + int t = v ^ v << 16 ^ r ^ sArr[s + 1]; + t = t >>> 4 | t << 28; + el ^= SP_TRANS[1][t & 0x3f] | SP_TRANS[3][t >>> 8 & 0x3f] | SP_TRANS[5][t >>> 16 & 0x3f] | + SP_TRANS[7][t >>> 24 & 0x3f] | SP_TRANS[0][u & 0x3f] | SP_TRANS[2][u >>> 8 & 0x3f] | + SP_TRANS[4][u >>> 16 & 0x3f] | SP_TRANS[6][u >>> 24 & 0x3f]; + return el; + } + private static int[] desSetKey(final byte[] key) { + final int[] schedule = new int[32]; + int c = fourBytesToInt(key, 0); + int d = fourBytesToInt(key, 4); + final int[] results = new int[2]; + permOp(d, c, 4, 0xf0f0f0f, results); + d = results[0]; + c = results[1]; + c = hPermOp(c, -2, 0xcccc0000); + d = hPermOp(d, -2, 0xcccc0000); + permOp(d, c, 1, 0x55555555, results); + d = results[0]; + c = results[1]; + permOp(c, d, 8, 0xff00ff, results); + c = results[0]; + d = results[1]; + permOp(d, c, 1, 0x55555555, results); + d = results[0]; + c = results[1]; + d = (d & 0xff) << 16 | d & 0xff00 | (d & 0xff0000) >>> 16 | (c & 0xf0000000) >>> 4; + c &= 0xfffffff; + int j = 0; + for (int i = 0; i < 16; i++) { + if (SHIFT2[i]) { + c = c >>> 2 | c << 26; + d = d >>> 2 | d << 26; + } else { + c = c >>> 1 | c << 27; + d = d >>> 1 | d << 27; + } + c &= 0xfffffff; + d &= 0xfffffff; + int s = SKB[0][c & 0x3f] | SKB[1][c >>> 6 & 0x3 | c >>> 7 & 0x3c] | + SKB[2][c >>> 13 & 0xf | c >>> 14 & 0x30] | + SKB[3][c >>> 20 & 0x1 | c >>> 21 & 0x6 | c >>> 22 & 0x38]; + final int t = SKB[4][d & 0x3f] | SKB[5][d >>> 7 & 0x3 | d >>> 8 & 0x3c] | SKB[6][d >>> 15 & 0x3f] | + SKB[7][d >>> 21 & 0xf | d >>> 22 & 0x30]; + schedule[j++] = (t << 16 | s & 0xffff); + s = s >>> 16 | t & 0xffff0000; + s = s << 4 | s >>> 28; + schedule[j++] = s; + } + return schedule; + } + private static int fourBytesToInt(final byte[] b, int offset) { + final byte b4 = b[offset++]; + int value = b4 & 0xff; + final byte b3 = b[offset++]; + value |= (b3 & 0xff) << 8; + final byte b2 = b[offset++]; + value |= (b2 & 0xff) << 16; + final byte b1 = b[offset ]; + value |= (b1 & 0xff) << 24; + return value; + } + private static int hPermOp(int a, final int n, final int m) { + final int t = (a << 16 - n ^ a) & m; + a = a ^ t ^ t >>> 16 - n; + return a; + } + private static void intToFourBytes(final int iValue, final byte[] b, int offset) { + b[offset++] = (byte) (iValue & 0xff); + b[offset++] = (byte) (iValue >>> 8 & 0xff); + b[offset++] = (byte) (iValue >>> 16 & 0xff); + b[offset ] = (byte) (iValue >>> 24 & 0xff); + } + private static void permOp(int a, int b, final int n, final int m, final int[] results) { + final int t = (a >>> n ^ b) & m; + a ^= t << n; + b ^= t; + results[0] = a; + results[1] = b; + } + + public int hashCode() { + return multiHashOrdered(Arrays.hashCode(hash), salt); + } + + public boolean equals(final Object obj) { + if (! (obj instanceof UnixDESCryptPasswordImpl)) { + return false; + } + UnixDESCryptPasswordImpl other = (UnixDESCryptPasswordImpl) obj; + return salt == other.salt && Arrays.equals(hash, other.hash); + } + + private void readObject(ObjectInputStream ignored) throws NotSerializableException { + throw new NotSerializableException(); + } + + Object writeReplace() { + return UnixDESCryptPassword.createRaw(getAlgorithm(), salt, hash); + } + + public UnixDESCryptPasswordImpl clone() { + return this; + } + +} Index: 3rdParty_sources/elytron/org/wildfly/security/password/impl/UnixMD5CryptPasswordImpl.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/password/impl/UnixMD5CryptPasswordImpl.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/password/impl/UnixMD5CryptPasswordImpl.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,262 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2014 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.password.impl; + +import static org.wildfly.common.math.HashMath.multiHashOrdered; +import static org.wildfly.security.password.impl.ElytronMessages.log; + +import java.io.NotSerializableException; +import java.io.ObjectInputStream; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.security.InvalidKeyException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.KeySpec; +import java.util.Arrays; + +import org.wildfly.security.password.spec.SaltedPasswordAlgorithmSpec; +import org.wildfly.security.password.interfaces.UnixMD5CryptPassword; +import org.wildfly.security.password.spec.ClearPasswordSpec; +import org.wildfly.security.password.spec.SaltedHashPasswordSpec; + +/** + * Implementation of the Unix MD5 Crypt password. + * + * @author Farah Juma + * @author David M. Lloyd + */ +final class UnixMD5CryptPasswordImpl extends AbstractPasswordImpl implements UnixMD5CryptPassword { + + private static final long serialVersionUID = 8315521712238708363L; + + static final String MD5 = "MD5"; + static final byte[] MAGIC_BYTES = "$1$".getBytes(StandardCharsets.UTF_8); + + private final byte[] hash; + private final byte[] salt; + + UnixMD5CryptPasswordImpl(final byte[] clonedHash, final byte[] clonedSalt) { + this.hash = clonedHash; + this.salt = clonedSalt; + } + + UnixMD5CryptPasswordImpl(UnixMD5CryptPassword password) { + this(password.getHash().clone(), truncatedClone(password.getSalt())); + } + + UnixMD5CryptPasswordImpl(final SaltedHashPasswordSpec spec) { + this(spec.getHash().clone(), truncatedClone(spec.getSalt())); + } + + UnixMD5CryptPasswordImpl(final ClearPasswordSpec spec) throws NoSuchAlgorithmException { + this.salt = PasswordUtil.generateRandomSalt(SALT_SIZE); + this.hash = encode(getNormalizedPasswordBytes(spec.getEncodedPassword()), this.salt); + } + + UnixMD5CryptPasswordImpl(final char[] password, final Charset hashCharset) throws NoSuchAlgorithmException { + this(password, PasswordUtil.generateRandomSalt(SALT_SIZE), hashCharset); + } + + UnixMD5CryptPasswordImpl(final char[] password, final SaltedPasswordAlgorithmSpec spec, final Charset hashCharset) throws NoSuchAlgorithmException { + this(password, truncatedClone(spec.getSalt()), hashCharset); + } + + UnixMD5CryptPasswordImpl(final char[] password, final byte[] salt, final Charset hashCharset) throws NoSuchAlgorithmException { + this(encode(getNormalizedPasswordBytes(password, hashCharset), salt), salt); + } + + private static byte[] truncatedClone(final byte[] salt) { + if (salt.length <= SALT_SIZE) { + return salt.clone(); + } else { + return Arrays.copyOf(salt, SALT_SIZE); + } + } + + @Override + public String getAlgorithm() { + return ALGORITHM_CRYPT_MD5; + } + + @Override + public byte[] getHash() { + return hash.clone(); + } + + @Override + public byte[] getSalt() { + return salt.clone(); + } + + @Override + S getKeySpec(final Class keySpecType) throws InvalidKeySpecException { + if (keySpecType.isAssignableFrom(SaltedHashPasswordSpec.class)) { + return keySpecType.cast(new SaltedHashPasswordSpec(getHash(), getSalt())); + } + throw new InvalidKeySpecException(); + } + + @Override + boolean verify(final char[] guess) throws InvalidKeyException { + return verify(guess, StandardCharsets.UTF_8); + } + + @Override + boolean verify(char[] guess, Charset hashCharset) throws InvalidKeyException { + byte[] guessAsBytes = getNormalizedPasswordBytes(guess, hashCharset); + byte[] test; + try { + test = encode(guessAsBytes, getSalt()); + } catch (NoSuchAlgorithmException e) { + throw log.invalidKeyCannotVerifyPassword(e); + } + return MessageDigest.isEqual(getHash(), test); + } + + @Override + boolean convertibleTo(final Class keySpecType) { + return keySpecType.isAssignableFrom(SaltedHashPasswordSpec.class); + } + + /** + * Hashes the given password using the MD5 Crypt algorithm. + * + * @param password the password to be hashed + * @param salt the salt, will be truncated to an array of 8 bytes if an array larger than 8 bytes is given + * @return a {@code byte[]} containing the hashed password + * @throws NoSuchAlgorithmException if a {@code MessageDigest} object that implements MD5 cannot be retrieved + */ + static byte[] encode(final byte[] password, byte[] salt) throws NoSuchAlgorithmException { + // Note that many of the comments below have been taken from or are based on comments from: + // ftp://ftp.arlut.utexas.edu/pub/java_hashes/SHA-crypt.txt and + // http://svnweb.freebsd.org/base/head/lib/libcrypt/crypt.c?revision=4246&view=markup (this is + // the original C implementation of the algorithm) + + if (salt.length > SALT_SIZE) { + salt = Arrays.copyOfRange(salt, 0, SALT_SIZE); + } + + // Add the password to digest A first since that is what is most unknown, then our magic + // string, then the raw salt + MessageDigest digestA = getMD5MessageDigest(); + digestA.update(password); + digestA.update(MAGIC_BYTES); + digestA.update(salt); + + // Add the password to digest B, followed by the salt, followed by the password again + MessageDigest digestB = getMD5MessageDigest(); + digestB.update(password); + digestB.update(salt); + digestB.update(password); + + // Finish digest B + byte[] finalDigest = digestB.digest(); + + // For each block of 16 bytes in the password string, add digest B to digest A and for the + // remaining N bytes of the password string, add the first N bytes of digest B to digest A + for (int i = password.length; i > 0; i -= 16) { + digestA.update(finalDigest, 0, i > 16 ? 16 : i); + } + + // Don't leave anything around in vm they could use + Arrays.fill(finalDigest, (byte) 0); + + // For each bit in the binary representation of the length of the password string up to + // and including the highest 1-digit, starting from the lowest bit position (numeric value 1): + // a) for a 1-digit, add a null character to digest A + // b) for a 0-digit, add the first character of the password to digest A + for (int i = password.length; i > 0; i >>= 1) { + if ((i & 1) == 1) { + digestA.update(finalDigest, 0, 1); + } else { + digestA.update(password, 0, 1); + } + } + + // Finish digest A + finalDigest = digestA.digest(); + + // The algorithm uses a fixed number of iterations + for (int i = 0; i < ITERATION_COUNT; i++) { + + // Start a new digest + digestB = getMD5MessageDigest(); + + // If the round is odd, add the password to this digest + // Otherwise, add the previous round's digest (or digest A if this is round 0) + if ((i & 1) == 1) { + digestB.update(password); + } else { + digestB.update(finalDigest, 0, 16); + } + + // If the round is not divisible by 3, add the salt + if ((i % 3) != 0) { + digestB.update(salt); + } + + // If the round is not divisible by 7, add the password + if ((i % 7) != 0) { + digestB.update(password); + } + + // If the round is odd, add the previous round's digest (or digest A if this is round 0) + // Otherwise, add the password + if ((i & 1) == 1) { + digestB.update(finalDigest, 0, 16); + } else { + digestB.update(password); + } + + finalDigest = digestB.digest(); + } + return finalDigest; + } + + static MessageDigest getMD5MessageDigest() throws NoSuchAlgorithmException { + return MessageDigest.getInstance(MD5); + } + + public int hashCode() { + return multiHashOrdered(Arrays.hashCode(hash), Arrays.hashCode(salt)); + } + + public boolean equals(final Object obj) { + if (! (obj instanceof UnixMD5CryptPasswordImpl)) { + return false; + } + UnixMD5CryptPasswordImpl other = (UnixMD5CryptPasswordImpl) obj; + return MessageDigest.isEqual(hash, other.hash) && Arrays.equals(salt, other.salt); + } + + private void readObject(ObjectInputStream ignored) throws NotSerializableException { + throw new NotSerializableException(); + } + + Object writeReplace() { + return UnixMD5CryptPassword.createRaw(getAlgorithm(), salt, hash); + } + + public UnixMD5CryptPasswordImpl clone() { + return this; + } + +} Index: 3rdParty_sources/elytron/org/wildfly/security/password/impl/UnixSHACryptPasswordImpl.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/password/impl/UnixSHACryptPasswordImpl.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/password/impl/UnixSHACryptPasswordImpl.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,452 @@ +/* + * JBoss, Home of Professional Open Source + * Copyright 2014 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.password.impl; + +import static org.wildfly.common.math.HashMath.multiHashOrdered; +import static org.wildfly.security.password.impl.ElytronMessages.log; +import static java.lang.Math.max; +import static java.lang.Math.min; + +import java.io.NotSerializableException; +import java.io.ObjectInputStream; +import java.nio.ByteBuffer; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.security.InvalidKeyException; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.KeySpec; +import java.util.Arrays; + +import org.wildfly.common.Assert; +import org.wildfly.security.password.spec.IteratedPasswordAlgorithmSpec; +import org.wildfly.security.password.spec.SaltedPasswordAlgorithmSpec; +import org.wildfly.security.password.interfaces.UnixSHACryptPassword; +import org.wildfly.security.password.spec.ClearPasswordSpec; +import org.wildfly.security.password.spec.IteratedSaltedPasswordAlgorithmSpec; +import org.wildfly.security.password.spec.IteratedSaltedHashPasswordSpec; +import org.wildfly.security.password.spec.SaltedHashPasswordSpec; + +/** + * @author Juraci Paixão Kröhling + * @author David M. Lloyd + */ +final class UnixSHACryptPasswordImpl extends AbstractPasswordImpl implements UnixSHACryptPassword { + + private static final long serialVersionUID = 1414406780966627792L; + + private final String algorithm; + private final byte[] salt; + private final int iterationCount; + private final byte[] hash; + + UnixSHACryptPasswordImpl(UnixSHACryptPassword password) { + this(password.getAlgorithm(), truncatedClone(password.getSalt()), password.getIterationCount(), password.getHash().clone()); + } + + UnixSHACryptPasswordImpl(String algorithm, byte[] clonedSalt, int iterationCount, byte[] hash) { + Assert.checkNotNullParam("algorithm", algorithm); + if (!ALGORITHM_CRYPT_SHA_256.equals(algorithm) && !ALGORITHM_CRYPT_SHA_512.equals(algorithm)) { + throw log.unrecognizedAlgorithm(algorithm); + } + + this.salt = clonedSalt; + this.iterationCount = iterationCount; + this.algorithm = algorithm; + this.hash = hash; + } + + UnixSHACryptPasswordImpl(final String algorithm, final char[] passwordChars, final Charset hashCharset) throws NoSuchAlgorithmException { + this(algorithm, PasswordUtil.generateRandomSalt(SALT_SIZE), DEFAULT_ITERATION_COUNT, passwordChars, hashCharset); + } + + UnixSHACryptPasswordImpl(final String algorithm, final IteratedSaltedHashPasswordSpec spec) { + this(algorithm, truncatedClone(spec.getSalt()), min(999_999_999, max(1_000, spec.getIterationCount())), spec.getHash().clone()); + } + + UnixSHACryptPasswordImpl(final String algorithm, final SaltedHashPasswordSpec spec) { + this(algorithm, truncatedClone(spec.getSalt()), min(999_999_999, max(1_000, DEFAULT_ITERATION_COUNT)), spec.getHash().clone()); + } + + UnixSHACryptPasswordImpl(final String algorithm, final ClearPasswordSpec spec) throws NoSuchAlgorithmException { + this(algorithm, spec.getEncodedPassword(), StandardCharsets.UTF_8); + } + + UnixSHACryptPasswordImpl(final String algorithm, final IteratedSaltedPasswordAlgorithmSpec parameterSpec, final char[] password, final Charset hashCharset) throws NoSuchAlgorithmException { + this(algorithm, truncatedClone(parameterSpec.getSalt()), min(999_999_999, max(1_000, parameterSpec.getIterationCount())), password, hashCharset); + } + + UnixSHACryptPasswordImpl(final String algorithm, final SaltedPasswordAlgorithmSpec parameterSpec, final char[] password, final Charset hashCharset) throws NoSuchAlgorithmException { + this(algorithm, truncatedClone(parameterSpec.getSalt()), DEFAULT_ITERATION_COUNT, password, hashCharset); + } + + UnixSHACryptPasswordImpl(final String algorithm, final IteratedPasswordAlgorithmSpec parameterSpec, final char[] password, final Charset hashCharset) throws NoSuchAlgorithmException { + this(algorithm, PasswordUtil.generateRandomSalt(SALT_SIZE), min(999_999_999, max(1_000, parameterSpec.getIterationCount())), password, hashCharset); + } + + UnixSHACryptPasswordImpl(final String algorithm, final byte[] clonedSalt, final int adjustedIterationCount, final char[] password, final Charset hashCharset) throws NoSuchAlgorithmException { + this(algorithm, clonedSalt, adjustedIterationCount, doEncode(algorithm, getNormalizedPasswordBytes(password, hashCharset), clonedSalt, adjustedIterationCount)); + } + + private static byte[] truncatedClone(final byte[] salt) { + if (salt.length <= 16) { + return salt.clone(); + } else { + return Arrays.copyOf(salt, 16); + } + } + + @Override + public byte[] getSalt() { + return salt.clone(); + } + + @Override + public int getIterationCount() { + return iterationCount; + } + + @Override + public byte[] getHash() { + return hash.clone(); + } + + @Override + public String getAlgorithm() { + return algorithm; + } + + @Override + S getKeySpec(Class keySpecType) throws InvalidKeySpecException { + if (keySpecType.isAssignableFrom(IteratedSaltedHashPasswordSpec.class)) { + return keySpecType.cast(new IteratedSaltedHashPasswordSpec(this.getHash(), this.getSalt(), this.getIterationCount())); + } else { + throw log.invalidKeySpecExpectedSpecGotSpec(IteratedSaltedHashPasswordSpec.class.getName(), keySpecType.getName()); + } + } + + @Override + boolean verify(final char[] guess) throws InvalidKeyException { + return verify(guess, StandardCharsets.UTF_8); + } + + @Override + boolean verify(char[] guess, Charset hashCharset) throws InvalidKeyException { + try { + byte[] password = getNormalizedPasswordBytes(guess, hashCharset); + byte[] encodedGuess = doEncode(algorithm, password, salt, iterationCount); + return MessageDigest.isEqual(getHash(), encodedGuess); + } catch (NoSuchAlgorithmException e) { + throw log.invalidKeyCannotVerifyPassword(e); + } + } + + @Override + boolean convertibleTo(Class keySpecType) { + return keySpecType.isAssignableFrom(IteratedSaltedHashPasswordSpec.class); + } + + static byte[] doEncode(final String algorithm, final byte[] password, final byte[] salt, final int iterationCount) throws NoSuchAlgorithmException { + // see ftp://ftp.arlut.utexas.edu/pub/java_hashes/SHA-crypt.txt + // most of the comments from this point and on are copy/paste from the url above, to make it easier + // to correlate the code with the steps. + + // implementation note: we use "digestAC" here, because we don't need to duplicate digestA into digestAC, + // as at the time the "digestAC" is "C", then "A" is not needed anymore. + byte[] digestAC = getDigestA(algorithm, password, salt); // at this point, digestAC is "A" + byte[] sequenceP = getSequenceP(algorithm, password); + byte[] sequenceS = getSequenceS(algorithm, digestAC, salt); + for (int i = 0 ; i < iterationCount; i++) { + // 21. repeat a loop according to the number specified in the rounds= + // specification in the salt (or the default value if none is + // present). Each round is numbered, starting with 0 and up to N-1. + // + // The loop uses a digest as input. In the first round it is the + // digest produced in step 12. In the latter steps it is the digest + // produced in step 21.h. The following text uses the notation + // "digest A/C" to describe this behavior. + digestAC = getDigestC(algorithm, digestAC, sequenceP, sequenceS, i); + // implementation note: at this point, digestAC is "C" + } + + return digestAC; + } + + /** + * Calculates the "digest A", derived from the password and salt. + * @param password the encoded password bytes + * @return the digest A + * @throws NoSuchAlgorithmException + */ + private static byte[] getDigestA(final String algorithm, final byte[] password, final byte[] salt) throws NoSuchAlgorithmException { + byte[] digestBResult = getDigestB(password, salt, algorithm); + int length = password.length; + + // 1. start digest A + MessageDigest digestA = getMessageDigest(algorithm); + + // 2. the password string is added to digest A + digestA.update(password, 0, length); + + // 3. the salt string is added to digest A. + digestA.update(salt, 0, salt.length); + + // 9. For each block of 32 or 64 bytes in the password string, add digest B to digest A + int numberOfBlocksPassword = length / getInputSize(algorithm); + for (int i = 0 ; i < numberOfBlocksPassword ; i++ ) { + digestA.update(digestBResult, 0, getInputSize(algorithm)); + } + + // 10. For the remaining N bytes of the password string add the first N bytes of digest B to digest A + int remainingBytesSizePassword = length % getInputSize(algorithm); + digestA.update(digestBResult, 0, remainingBytesSizePassword); + + // 11. For each bit of the binary representation of the length of the + // password string up to and including the highest 1-digit, starting + // from to lowest bit position (numeric value 1): + // + // a) for a 1-digit add digest B to digest A + // + // b) for a 0-digit add the password string + for (int i = length; i > 0 ; i >>= 1) { + if (i % 2 != 0) { + digestA.update(digestBResult, 0, getInputSize(algorithm)); + } else { + digestA.update(password, 0, length); + } + } + + // 12. finish digest A + return digestA.digest(); + } + + + /** + * Calculates the "sequence S", based on a given "digest A" + * + * @param digestA the digest A + * @return the sequence S + * @throws NoSuchAlgorithmException + */ + private static byte[] getSequenceS(String algorithm, byte[] digestA, byte[] salt) throws NoSuchAlgorithmException { + // 20. produce byte sequence S of the same length as the salt string where + // + // a) for each block of 32 or 64 bytes of length of the salt string + // the entire digest DS is used + // + // b) for the remaining N (up to 31 or 63) bytes use the first N + // bytes of digest DS + byte[] sequenceS = new byte[salt.length]; + byte[] digestDSResult = getDigestDS(algorithm, digestA, salt); + ByteBuffer bufferSequenceS = ByteBuffer.wrap(sequenceS); + int numberOfBlocksSalt = salt.length / getInputSize(algorithm); + int remainingBytesSizeSalt = salt.length % getInputSize(algorithm); + + for (int i = 0 ; i < numberOfBlocksSalt ; i++ ) { + bufferSequenceS.put(Arrays.copyOfRange(digestDSResult, 0, getInputSize(algorithm))); + } + bufferSequenceS.put(Arrays.copyOfRange(digestDSResult, 0, remainingBytesSizeSalt)); + return sequenceS; + } + + /** + * Calculates the "digest DS", derived from the salt and on the "digest A" + * + * @param digestA the digest A + * @return the digest DS + * @throws NoSuchAlgorithmException + */ + private static byte[] getDigestDS(String algorithm, byte[] digestA, byte[] salt) throws NoSuchAlgorithmException { + // 17. start digest DS + MessageDigest digestDS = getMessageDigest(algorithm); + + // 18. repeat the following 16+A[0] times, where A[0] represents the first + // byte in digest A interpreted as an 8-bit unsigned value + // + // add the salt to digest DS + int repeatTimes = 16 + (digestA[0] & 0xFF); // this binary-and converts the byte into "8-bit unsigned" value + for (int i = 0 ; i < repeatTimes ; i++) { + digestDS.update(salt, 0, salt.length); + } + + // 19. finish digest DS + return digestDS.digest(); + } + + /** + * Returns the "digest B", derived from the password and salt + * + * @param password the encoded password bytes + * @return the digest B + * @throws NoSuchAlgorithmException + */ + private static byte[] getDigestB(final byte[] password, final byte[] salt, final String algorithm) throws NoSuchAlgorithmException { + // 4. start digest B + MessageDigest digestB = getMessageDigest(algorithm); + + // 5. add the password to digest B + digestB.update(password, 0, password.length); + + // 6. add the salt string to digest B + digestB.update(salt, 0, salt.length); + + // 7. add the password again to digest B + digestB.update(password, 0, password.length); + + // 8. finish digest B + return digestB.digest(); + } + + /** + * Calculates the "digest DP", derived from the password + * + * @param password the encoded password bytes + * @return the digest DP + * @throws NoSuchAlgorithmException + */ + private static byte[] getDigestDP(final String algorithm, final byte[] password) throws NoSuchAlgorithmException { + // 13. start digest DP + MessageDigest digestDP = getMessageDigest(algorithm); + + // 14. for every byte in the password add the password to digest DP + for (byte ignored : password) { + digestDP.update(password, 0, password.length); + } + + // 15. finish digest DP + return digestDP.digest(); + } + + /** + * Calculates the "sequence P", derived from the password + * + * @param password the encoded password bytes + * @return the sequence P + * @throws NoSuchAlgorithmException + */ + private static byte[] getSequenceP(final String algorithm, final byte[] password) throws NoSuchAlgorithmException { + // 16. produce byte sequence P of the same length as the password where + // + // a) for each block of 32 or 64 bytes of length of the password string + // the entire digest DP is used + // + // b) for the remaining N (up to 31 or 63) bytes use the first N + // bytes of digest DP + byte[] digestDPResult = getDigestDP(algorithm, password); + byte[] sequenceP = new byte[password.length]; + ByteBuffer bufferSequenceP = ByteBuffer.wrap(sequenceP); + + int numberOfBlocksPassword = password.length / getInputSize(algorithm); + for (int i = 0 ; i < numberOfBlocksPassword ; i++ ) { + bufferSequenceP.put(Arrays.copyOfRange(digestDPResult, 0, getInputSize(algorithm))); + } + + int remainingBytesSizePassword = password.length % getInputSize(algorithm); + bufferSequenceP.put(Arrays.copyOfRange(digestDPResult, 0, remainingBytesSizePassword)); + + return sequenceP; + } + + + /** + * Calculates the "digest C", derived from the sequenceP, sequenceS, digestAC and the iteration round + * + * @param digestAC the digest A/C + * @param sequenceP the sequence P + * @param sequenceS the sequence S + * @param round the iteration round + * @return the resulting digest C + * @throws NoSuchAlgorithmException + */ + private static byte[] getDigestC(String algorithm, byte[] digestAC, byte[] sequenceP, byte[] sequenceS, int round) throws NoSuchAlgorithmException { + // a) start digest C + MessageDigest digestC = getMessageDigest(algorithm); + + // b) for odd round numbers add the byte sequence P to digest C + // c) for even round numbers add digest A/C + if (round % 2 != 0) { + digestC.update(sequenceP, 0, sequenceP.length); + } else { + digestC.update(digestAC, 0, digestAC.length); + } + + // d) for all round numbers not divisible by 3 add the byte sequence S + if (round % 3 != 0) { + digestC.update(sequenceS, 0, sequenceS.length); + } + + // e) for all round numbers not divisible by 7 add the byte sequence P + if (round % 7 != 0) { + digestC.update(sequenceP, 0, sequenceP.length); + } + + // f) for odd round numbers add digest A/C + // g) for even round numbers add the byte sequence P + if (round % 2 != 0) { + digestC.update(digestAC, 0, digestAC.length); + } else { + digestC.update(sequenceP, 0, sequenceP.length); + } + + // h) finish digest C. + // from the javadoc: After digest has been called, the MessageDigest object is reset to its initialized state. + return digestC.digest(); + } + + private static MessageDigest getMessageDigest(String algorithm) throws NoSuchAlgorithmException { + switch (algorithm) { + case ALGORITHM_CRYPT_SHA_256: return MessageDigest.getInstance("SHA-256"); + case ALGORITHM_CRYPT_SHA_512: return MessageDigest.getInstance("SHA-512"); + default: throw log.noSuchAlgorithmInvalidAlgorithm(algorithm); + } + } + + private static int getInputSize(final String algorithm) throws NoSuchAlgorithmException { + switch (algorithm) { + case ALGORITHM_CRYPT_SHA_256: return 32; + case ALGORITHM_CRYPT_SHA_512: return 64; + default: throw log.noSuchAlgorithmInvalidAlgorithm(algorithm); + } + } + + public int hashCode() { + return multiHashOrdered(multiHashOrdered(multiHashOrdered(Arrays.hashCode(hash), Arrays.hashCode(salt)), iterationCount), algorithm.hashCode()); + } + + public boolean equals(final Object obj) { + if (! (obj instanceof UnixSHACryptPasswordImpl)) { + return false; + } + UnixSHACryptPasswordImpl other = (UnixSHACryptPasswordImpl) obj; + return iterationCount == other.iterationCount && algorithm.equals(other.algorithm) && Arrays.equals(hash, other.hash) && Arrays.equals(salt, other.salt); + } + + private void readObject(ObjectInputStream ignored) throws NotSerializableException { + throw new NotSerializableException(); + } + + Object writeReplace() { + return UnixSHACryptPassword.createRaw(algorithm, salt, hash, iterationCount); + } + + public UnixSHACryptPasswordImpl clone() { + return this; + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/password/impl/package-info.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/password/impl/package-info.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/password/impl/package-info.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,22 @@ +/* + * 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. + */ + +/** + * The Elytron implementation of password types. + */ +package org.wildfly.security.password.impl; Index: 3rdParty_sources/elytron/org/wildfly/security/permission/AbstractActionPermission.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/permission/AbstractActionPermission.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/permission/AbstractActionPermission.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,200 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2016 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.permission; + +import java.security.Permission; + +import org.wildfly.common.Assert; + +/** + * An abstract base class for named permissions that have actions, with useful API and implementation methods. All + * the constraints described in {@link AbstractNamedPermission} apply. + * + * @author David M. Lloyd + */ +public abstract class AbstractActionPermission> extends AbstractNamedPermission { + private static final long serialVersionUID = - 1366777243917643233L; + + /** + * Construct a new instance. + * + * @param name the permission name + */ + protected AbstractActionPermission(final String name) { + super(name); + } + + /** + * Determine whether this permission implies another permission. Returns {@code true} if + * both {@link #impliesActions(AbstractActionPermission)} and {@link #impliesName(AbstractNamedPermission)} + * return {@code true}. + * + * @param permission the other permission + * @return {@code true} if this permission implies the other; {@code false} otherwise + */ + public final boolean implies(final This permission) { + return permission != null && impliesActions(permission) && impliesName(permission); + } + + /** + * Determine whether this permission is equal to another permission. Returns {@code true} if + * both {@link #actionsEquals(AbstractActionPermission)} and {@link #nameEquals(AbstractNamedPermission)} + * return {@code true}. + * + * @param permission the other permission + * @return {@code true} if this permission implies the other; {@code false} otherwise + */ + public final boolean equals(final This permission) { + return super.equals(permission) && actionsEquals(permission); + } + + public final int hashCode() { + return super.hashCode() * 53 + actionsHashCode(); + } + + /** + * Determine whether the actions of this permission are equal to the given {@code actions}. + * + * @param actions the actions string (must not be {@code null}) + * @return {@code true} if the actions are equal, {@code false} otherwise + */ + public abstract boolean actionsEquals(String actions); + + /** + * Determine whether the actions of this permission are equal to the actions of given {@code permission}. If + * the permission is not of the same type as this permission, {@code false} is returned. + * + * @param permission the permission whose actions are to be compared + * @return {@code true} if the actions are equal, {@code false} otherwise + */ + @SuppressWarnings("unchecked") + public final boolean actionsEquals(Permission permission) { + return permission != null && permission.getClass() == getClass() && actionsEquals((This) permission); + } + + /** + * Determine whether the actions of this permission are equal to the actions of given {@code permission}. + * + * @param permission the permission whose actions are to be compared + * @return {@code true} if the actions are equal, {@code false} otherwise + */ + public abstract boolean actionsEquals(This permission); + + /** + * Get the actions hash code. + * + * @return the actions hash code + */ + protected abstract int actionsHashCode(); + + /** + * Determine whether this permission's actions value implies the given actions value. + * + * @param actions the actions to test (must not be {@code null}) + * @return {@code true} if this permission implies the other; {@code false} otherwise + */ + public abstract boolean impliesActions(String actions); + + /** + * Determine whether this permission's actions value implies the actions of the given {@code permission}. If + * the permission is not of the same type as this permission, {@code false} is returned. + * + * @param permission the permission whose actions are to be compared + * @return {@code true} if this permission implies the other; {@code false} otherwise + */ + @SuppressWarnings("unchecked") + public final boolean impliesActions(Permission permission) { + return permission != null && permission.getClass() == getClass() && impliesActions((This) permission); + } + + /** + * Determine whether this permission's actions value implies the actions of the given {@code permission}. + * + * @param permission the permission whose actions are to be compared + * @return {@code true} if this permission implies the other; {@code false} otherwise + */ + public abstract boolean impliesActions(This permission); + + /** + * Get a permission which is identical to this one, but with new actions which consist of the union of the actions + * from this permission and the actions from the given string. The returned permission may or may not be a new + * instance, and may be equal to this instance. + * + * @param actionsString the actions string (must not be {@code null}) + * @return the permission (not {@code null}) + */ + public abstract This withActions(String actionsString); + + /** + * Get a permission which is identical to this one, but with new actions which consist of the union of the actions + * from this permission and the actions from the given permission. The returned permission may or may not be a new + * instance, and may be equal to this instance. + * + * @param permission the other permission (must not be {@code null}) + * @return the permission (not {@code null}) + */ + public This withActionsFrom(This permission) { + Assert.checkNotNullParam("permission", permission); + return withActions(permission.getActions()); + } + + /** + * Get a permission which is identical to this one, but with new actions which consist of the actions + * from this permission without the actions from the given string. The returned permission may or may not be a new + * instance, and may be equal to this instance. + * + * @param actionsString the actions string (must not be {@code null}) + * @return the permission (not {@code null}) + */ + public abstract This withoutActions(String actionsString); + + /** + * Get a permission which is identical to this one, but with new actions which consist of the actions + * from this permission without the actions from the given permission. The returned permission may or may not be a new + * instance, and may be equal to this instance. + * + * @param permission the other permission (must not be {@code null}) + * @return the permission (not {@code null}) + */ + public This withoutActionsFrom(This permission) { + Assert.checkNotNullParam("permission", permission); + return withActions(permission.getActions()); + } + + /** + * Get a permission which is identical to this one, but with new actions as given by {@code actionsString}. + * The returned permission may or may not be a new instance, and may be equal to this instance. + * + * @param actionsString the actions string (must not be {@code null}) + * @return the permission (not {@code null}) + */ + public abstract This withNewActions(String actionsString); + + /** + * Get a permission which is identical to this one, but with new actions as given by {@code actionsString}. + * The returned permission may or may not be a new instance, and may be equal to this instance. + * + * @param permission the other permission (must not be {@code null}) + * @return the permission (not {@code null}) + */ + public This withNewActionsFrom(This permission) { + Assert.checkNotNullParam("permission", permission); + return withNewActions(permission.getActions()); + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/permission/AbstractActionSetPermission.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/permission/AbstractActionSetPermission.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/permission/AbstractActionSetPermission.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,214 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2016 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.permission; + +import org.wildfly.security.util.StringEnumeration; + +/** + * An abstract base class for permissions which use a bit set to represent actions. + * + * @author David M. Lloyd + */ +public abstract class AbstractActionSetPermission> extends AbstractActionPermission { + private static final long serialVersionUID = 897239118282921196L; + + private final StringEnumeration actionEnumeration; + private final int actionBits; + private String actions; + + /** + * Construct a new instance. The given bits are masked by {@link #actionsMask()} before being stored in the object + * instance. + * + * @param name the permission name + * @param actionBits the permission action bits + * @param actionEnumeration the permission actions enumeration + */ + protected AbstractActionSetPermission(final String name, final int actionBits, final StringEnumeration actionEnumeration) { + super(name); + this.actionEnumeration = actionEnumeration; + this.actionBits = actionBits & actionsMask(); + if (actionBits == actionsMask()) actions = "*"; + } + + /** + * Construct a new instance. + * + * @param name the permission name + * @param actions the permission actions string + * @param actionEnumeration the permission actions enumeration + */ + protected AbstractActionSetPermission(final String name, final String actions, final StringEnumeration actionEnumeration) { + super(name); + this.actionEnumeration = actionEnumeration; + final int actionBits = parseActions(actions); + this.actionBits = actionBits & actionsMask(); + if (actionBits == actionsMask()) this.actions = "*"; + } + + /** + * Get the action bits of this permission. + * + * @return the action bits + */ + public final int getActionBits() { + return actionBits; + } + + public final boolean actionsEquals(final This permission) { + return permission != null && actionBits == permission.getActionBits(); + } + + public final boolean impliesActions(final This permission) { + return permission != null && isSet(actionBits, permission.getActionBits()); + } + + public final boolean impliesActions(final String actions) { + return impliesActionBits(parseActions(actions)); + } + + /** + * Determine whether this permission's actions value implies the given action bits. + * + * @param actionBits the actions bits to test + * @return {@code true} if this permission implies the given action bits; {@code false} otherwise + */ + public final boolean impliesActionBits(final int actionBits) { + return isSet(this.actionBits, actionBits & actionsMask()); + } + + private int actionsMask() { + return (1 << actionEnumeration.size()) - 1; + } + + private int getActionBit(final String actionName) throws IllegalArgumentException { + return 1 << actionEnumeration.indexOf(actionName); + } + + private String getActionName(final int bit) throws IllegalArgumentException { + return actionEnumeration.nameOf(Integer.numberOfTrailingZeros(bit)); + } + + protected final int actionsHashCode() { + return actionBits; + } + + /** + * Get the actions string. The string is computed the first time this method is called, and cached thereafter. + * + * @return the actions string (not {@code null}) + */ + public final String getActions() { + final String actions = this.actions; + if (actions != null) { + return actions; + } + return this.actions = PermissionUtil.toActionsString(actionBits, this::getActionName); + } + + /** + * Parse the actions string into a bit set. + * + * @param actionsString the actions string + * @return the bit set + * @throws IllegalArgumentException if the actions string contained an invalid name or invalid syntax + */ + public final int parseActions(final String actionsString) throws IllegalArgumentException { + return PermissionUtil.parseActions(actionsString, this::getActionBit); + } + + public final This withActions(final String actionsString) { + return withActionBits(parseActions(actionsString)); + } + + public final This withActionsFrom(final This permission) { + return withActionBits(permission.getActionBits()); + } + + /** + * Get a permission which is identical to this one, but with new actions which consist of the union of the actions + * from this permission and the action bits from the given value. The returned permission may or may not be a new + * instance, and may be equal to this instance. + * + * @param actionBits the action bits + * @return the permission (not {@code null}) + */ + public final This withActionBits(int actionBits) { + return withNewActionBits(this.actionBits | actionBits & actionsMask()); + } + + public final This withoutActions(String actionsString) { + return withoutActionBits(parseActions(actionsString)); + } + + public final This withoutActionsFrom(final This permission) { + return withoutActionBits(permission.getActionBits()); + } + + /** + * Get a permission which is identical to this one, but with new actions which consist of the actions + * from this permission without the action bits from the given value. The returned permission may or may not be a new + * instance, and may be equal to this instance. + * + * @param actionBits the action bits + * @return the permission (not {@code null}) + */ + public final This withoutActionBits(int actionBits) { + return withNewActionBits(this.actionBits & ~actionBits); + } + + public final This withNewActions(String actionsString) { + return withNewActionBits(parseActions(actionsString)); + } + + public final This withNewActionsFrom(final This permission) { + return withNewActionBits(permission.getActionBits()); + } + + /** + * Get a permission which is identical to this one, but with new action bits as given by {@code actionBits}. + * The returned permission may or may not be a new instance, and may be equal to this instance. + * + * @param actionBits the action bits + * @return the permission (not {@code null}) + */ + @SuppressWarnings("unchecked") + public final This withNewActionBits(int actionBits) { + final int masked = actionBits & actionsMask(); + if (masked == this.actionBits) { + return (This) this; + } else { + return constructWithActionBits(masked); + } + } + + /** + * Construct or return a permission of this type with the same name as this one but with the given action bits. + * + * @param actionBits the action bits + * @return the permission + */ + protected abstract This constructWithActionBits(int actionBits); + + // private + + private static boolean isSet(final int bits, final int test) { + return (bits & test) == test; + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/permission/AbstractBooleanPermission.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/permission/AbstractBooleanPermission.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/permission/AbstractBooleanPermission.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,49 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2016 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.permission; + +/** + * A base class for nameless and actionless permissions that are either granted or not granted. + * + * @author David M. Lloyd + */ +public abstract class AbstractBooleanPermission> extends AbstractPermission { + /** + * Construct a new instance. + */ + protected AbstractBooleanPermission() { + super(""); + } + + public boolean implies(final This permission) { + return permission != null; + } + + public boolean equals(final This other) { + return other != null; + } + + public int hashCode() { + return getClass().hashCode(); + } + + public AbstractPermissionCollection newPermissionCollection() { + return new BooleanPermissionCollection(this); + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/permission/AbstractNameOnlyPermission.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/permission/AbstractNameOnlyPermission.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/permission/AbstractNameOnlyPermission.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,39 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2016 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.permission; + +/** + * A permission which has a name only, and no actions. + * + * @author David M. Lloyd + */ +public abstract class AbstractNameOnlyPermission> extends AbstractNamedPermission { + /** + * Construct a new instance. + * + * @param name the permission name (must not be {@code null}) + */ + protected AbstractNameOnlyPermission(final String name) { + super(name); + } + + public AbstractPermissionCollection newPermissionCollection() { + return new ByNamePermissionCollection(this); + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/permission/AbstractNameSetOnlyPermission.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/permission/AbstractNameSetOnlyPermission.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/permission/AbstractNameSetOnlyPermission.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,62 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2016 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.permission; + +import org.wildfly.security.util.StringEnumeration; + +/** + * An actionless permission with a finite, fixed set of possible names. + * + * @author David M. Lloyd + */ +public abstract class AbstractNameSetOnlyPermission> extends AbstractNameOnlyPermission { + + private final StringEnumeration nameEnumeration; + + /** + * Construct a new instance. + * + * @param name the name of this permission + * @param nameEnumeration the set of valid names for this permission type + */ + protected AbstractNameSetOnlyPermission(final String name, final StringEnumeration nameEnumeration) { + super("*".equals(name) ? "*" : nameEnumeration.canonicalName(name)); + this.nameEnumeration = nameEnumeration; + } + + StringEnumeration getNameEnumeration() { + return nameEnumeration; + } + + public AbstractPermissionCollection newPermissionCollection() { + return NameSetPermissionCollection.newInstance(this, nameEnumeration); + } + + public final boolean nameEquals(final String name) { + return super.nameEquals(name); + } + + public final boolean impliesName(final String name) { + return super.impliesName(name); + } + + protected final int nameHashCode() { + return nameEnumeration.indexOf(getName()); + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/permission/AbstractNamedPermission.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/permission/AbstractNamedPermission.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/permission/AbstractNamedPermission.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,140 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2016 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.permission; + +import org.wildfly.common.Assert; + +import java.security.Permission; + +/** + * An abstract base class for named permissions with useful API and implementation methods. + *

+ * Subclasses of this class are always serialized as a special serialized permission object, which captures the type class, + * the permission name (if any), and the permission action (if any) as a string. Therefore, none of the fields of any + * subclass of this class are serialized unless they are included in the name or actions properties. + *

+ * Concrete subclasses are expected to be immutable and final. + * + * @author David M. Lloyd + */ +public abstract class AbstractNamedPermission> extends AbstractPermission { + private static final long serialVersionUID = 5774685776540853292L; + + /** + * Construct a new instance. + * + * @param name the permission name (must not be {@code null}) + */ + protected AbstractNamedPermission(final String name) { + super(Assert.checkNotNullParam("name", name)); + } + + /** + * Create a new permission which is identical to this one, except with a new {@code name}. + * + * @param name the name to use (must not be {@code null}) + * @return the new permission + */ + public abstract This withName(String name); + + /** + * Determine whether this permission has a name equal to the given name. + * + * @param name the name to check + * @return {@code true} if this permission's name is equal to the given name, {@code false} otherwise + */ + public boolean nameEquals(final String name) { + return getName().equals(name); + } + + /** + * Determine whether this permission has a name equal to the name of the given permission. If the given permission + * is of a different type than this permission, {@code false} is returned. + * + * @param permission the permission whose name is to be checked + * @return {@code true} if this permission's name is equal to the given permission's name, {@code false} otherwise + */ + @SuppressWarnings("unchecked") + public final boolean nameEquals(final Permission permission) { + return permission != null && permission.getClass() == getClass() && nameEquals((This) permission); + } + + /** + * Determine whether this permission has a name equal to the name of the given permission. + * + * @param permission the permission whose name is to be checked + * @return {@code true} if this permission's name is equal to the given permission's name, {@code false} otherwise + */ + public final boolean nameEquals(final This permission) { + return permission != null && nameEquals(permission.getName()); + } + + /** + * Get the hash code of the name. The default implementation returns {@code getName().hashCode()}. + * + * @return the hash code of the name + */ + protected int nameHashCode() { + return getName().hashCode(); + } + + /** + * Determine whether this permission implies the given name. + * + * @param name the name to check + * @return {@code true} if this permission's name implies the given name, {@code false} otherwise + */ + public boolean impliesName(final String name) { + return nameEquals("*") || nameEquals(name); + } + + /** + * Determine whether this permission implies the name of the given permission. If + * the permission is not of the same type as this permission, {@code false} is returned. + * + * @param permission the permission whose name is to be checked + * @return {@code true} if this permission's name implies the given name, {@code false} otherwise + */ + @SuppressWarnings("unchecked") + public final boolean impliesName(final Permission permission) { + return permission != null && permission.getClass() == getClass() && impliesName((This) permission); + } + + /** + * Determine whether this permission implies the name of the given permission. + * + * @param permission the permission whose name is to be checked + * @return {@code true} if this permission's name implies the given name, {@code false} otherwise + */ + public boolean impliesName(final This permission) { + return permission != null && impliesName(permission.getName()); + } + + public boolean implies(final This permission) { + return permission != null && impliesName(permission); + } + + public boolean equals(final This other) { + return other != null && nameEquals(other); + } + + public int hashCode() { + return getClass().hashCode() * 71 + nameHashCode(); + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/permission/AbstractPermission.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/permission/AbstractPermission.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/permission/AbstractPermission.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,119 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2016 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.permission; + +import java.security.Permission; + +/** + * An abstract base class for any permission. + * + * @author David M. Lloyd + */ +public abstract class AbstractPermission> extends Permission implements PermissionVerifier { + /** + * Construct a new instance. + * + * @param name the permission name + */ + protected AbstractPermission(final String name) { + super(name); + } + + /** + * Determine whether this permission implies another permission. + * + * @param permission the other permission + * @return {@code true} if this permission implies the other; {@code false} otherwise + */ + @SuppressWarnings("unchecked") + public final boolean implies(Permission permission) { + return permission != null && getClass() == permission.getClass() && implies((This) permission); + } + + /** + * Determine whether this permission implies another permission. + * + * @param permission the other permission + * @return {@code true} if this permission implies the other; {@code false} otherwise + */ + public abstract boolean implies(This permission); + + /** + * Determine whether this permission object is equal to another object. + * + * @param obj the object to compare to + * @return {@code true} if the object is a permission equal to this one; {@code false} otherwise + */ + @SuppressWarnings("unchecked") + public final boolean equals(final Object obj) { + return obj != null && obj.getClass() == getClass() && equals((This) obj); + } + + /** + * Determine whether this permission object is equal to another object of this permission type. + * + * @param other the permission to compare to + * @return {@code true} if the object is a permission equal to this one; {@code false} otherwise + */ + public abstract boolean equals(This other); + + /** + * Get the hash code of this permission. The result must be consistent with the defined {@link #equals(AbstractPermission)} + * result. + * + * @return the hash code of this permission + */ + public abstract int hashCode(); + + /** + * Get the actions string. The default implementation always returns an empty string. + * + * @return the actions string (not {@code null}) + */ + public String getActions() { + return ""; + } + + /** + * Get an empty permission collection which is capable of holding instances of this permission type. + *

+ * The default implementation returns a {@link SimplePermissionCollection}. + * + * @return the permission collection to use + */ + public AbstractPermissionCollection newPermissionCollection() { + return new SimplePermissionCollection(this); + } + + /** + * Check to ensure that the given action string is empty or {@code null}; otherwise, throw an exception. + * + * @param actions the actions string + * @throws IllegalArgumentException if the actions string is not empty + */ + protected static void requireEmptyActions(final String actions) throws IllegalArgumentException { + if (actions != null && ! actions.isEmpty()) { + throw ElytronMessages.log.expectedEmptyActions(actions); + } + } + + final Object writeReplace() { + return new SerializedPermission(getClass(), getName(), getActions()); + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/permission/AbstractPermissionCollection.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/permission/AbstractPermissionCollection.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/permission/AbstractPermissionCollection.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,100 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2016 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.permission; + +import java.security.Permission; +import java.security.PermissionCollection; +import java.util.Enumeration; +import java.util.Iterator; + +import org.wildfly.common.Assert; + +/** + * Base class for useful permission collections. + * + * @author David M. Lloyd + */ +public abstract class AbstractPermissionCollection extends PermissionCollection implements Iterable, PermissionVerifier { + private static final long serialVersionUID = - 7532778883140764647L; + + private final AbstractPermission sourcePermission; + + /** + * Construct a new instance. + * + * @param sourcePermission the source permission for this collection (must not be {@code null}) + */ + protected AbstractPermissionCollection(final AbstractPermission sourcePermission) { + Assert.checkNotNullParam("sourcePermission", sourcePermission); + this.sourcePermission = sourcePermission; + } + + /** + * Get the size of this permission collection. + * + * @return the size of this permission collection + */ + public abstract int size(); + + /** + * Iterate over this permission collection. + * + * @return the iterator (not {@code null}) + */ + public abstract Iterator iterator(); + + /** + * Iterate over this permission collection. + * + * @return the iterator (not {@code null}) + */ + public abstract Enumeration elements(); + + /** + * Add an item to this collection. The permission class must be the same as the source permission's class. + * + * @param permission the permission to add (must not be {@code null}) + */ + public final void add(final Permission permission) { + Assert.checkNotNullParam("permission", permission); + if (isReadOnly()) throw ElytronMessages.log.readOnlyPermissionCollection(); + @SuppressWarnings("rawtypes") + Class expected = sourcePermission.getClass().asSubclass(AbstractPermission.class); + if (expected != permission.getClass()) { + throw ElytronMessages.log.invalidPermissionType(expected, permission); + } + doAdd(expected.cast(permission)); + } + + /** + * Perform the work of adding a permission. The permission is guaranteed to be of the correct type and the collection + * is guaranteed to have been writable at the time the {@link #add(Permission)} method was called. + * + * @param permission the non-{@code null} permission + */ + protected abstract void doAdd(final AbstractPermission permission); + + final AbstractPermission getSourcePermission() { + return sourcePermission; + } + + final Object writeReplace() { + return new SerializedPermissionCollection(this); + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/permission/BooleanPermissionCollection.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/permission/BooleanPermissionCollection.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/permission/BooleanPermissionCollection.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,67 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2016 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.permission; + +import java.security.Permission; +import java.util.Collections; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.Set; + +/** + * A permission collection type which either does or does not hold its instance. + * + * @author David M. Lloyd + */ +public final class BooleanPermissionCollection extends AbstractPermissionCollection { + private volatile boolean added; + + /** + * Construct a new instance. + * + * @param sourcePermission the source permission for this collection (must not be {@code null}) + */ + public BooleanPermissionCollection(final AbstractPermission sourcePermission) { + super(sourcePermission); + } + + public int size() { + return added ? 1 : 0; + } + + public Iterator iterator() { + return added ? getSingletonCollection().iterator() : Collections.emptyIterator(); + } + + public Enumeration elements() { + return added ? Collections.enumeration(getSingletonCollection()) : Collections.emptyEnumeration(); + } + + private Set getSingletonCollection() { + return Collections.singleton(getSourcePermission()); + } + + protected void doAdd(final AbstractPermission permission) { + added = true; + } + + public boolean implies(final Permission permission) { + return added && permission.getClass() == getSourcePermission().getClass(); + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/permission/ByNamePermissionCollection.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/permission/ByNamePermissionCollection.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/permission/ByNamePermissionCollection.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,83 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2016 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.permission; + +import java.security.Permission; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.concurrent.ConcurrentHashMap; + +/** + * A permission collection for actionless permissions which are organized by name. + * + * @author David M. Lloyd + */ +public final class ByNamePermissionCollection extends AbstractPermissionCollection { + private final ConcurrentHashMap byName = new ConcurrentHashMap<>(); + private volatile Permission all; + + /** + * The source permission used to construct this collection. + * + * @param sourcePermission the source permission (must not be {@code null}) + */ + public ByNamePermissionCollection(final AbstractPermission sourcePermission) { + super(sourcePermission); + } + + public int size() { + return all != null ? 1 : byName.size(); + } + + public Iterator iterator() { + return getIterablePermissions().iterator(); + } + + public Enumeration elements() { + return Collections.enumeration(getIterablePermissions()); + } + + private Collection getIterablePermissions() { + return all != null ? Collections.singleton(all) : Arrays.asList(byName.values().toArray(PermissionUtil.NO_PERMISSIONS)); + } + + protected void doAdd(final AbstractPermission permission) { + if (permission.getName().equals("*")) { + all = permission; + byName.clear(); + } else { + byName.putIfAbsent(permission.getName(), permission); + } + } + + public boolean implies(final Permission permission) { + if (permission == null || getSourcePermission().getClass() != permission.getClass()) { + return false; + } + final Permission all = this.all; + if (all != null) { + return all.implies(permission); + } + final Permission ourPermission = byName.get(permission.getName()); + return ourPermission != null && ourPermission.implies(permission); + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/permission/ElytronMessages.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/permission/ElytronMessages.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/permission/ElytronMessages.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,73 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2014 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.permission; + +import java.io.InvalidObjectException; +import java.security.Permission; + +import org.jboss.logging.BasicLogger; +import org.jboss.logging.Logger; +import org.jboss.logging.annotations.Cause; +import org.jboss.logging.annotations.Message; +import org.jboss.logging.annotations.MessageLogger; +import org.jboss.logging.annotations.ValidIdRange; +import org.jboss.logging.annotations.ValidIdRanges; + +/** + * Log messages and exceptions for Elytron. + * + * @author David M. Lloyd + * @author Darran Lofthouse + */ +@MessageLogger(projectCode = "ELY", length = 5) +@ValidIdRanges({ + @ValidIdRange(min = 3013, max = 3022) +}) +interface ElytronMessages extends BasicLogger { + + ElytronMessages log = Logger.getMessageLogger(ElytronMessages.class, "org.wildfly.security"); + + @Message(id = 3013, value = "Permission collection must be read-only") + SecurityException permissionCollectionMustBeReadOnly(); + + @Message(id = 3015, value = "Could not load permission class \"%s\"") + InvalidPermissionClassException permissionClassMissing(String className, @Cause ClassNotFoundException cause); + + @Message(id = 3016, value = "Could not instantiate permission class \"%s\"") + InvalidPermissionClassException permissionInstantiation(String className, @Cause Throwable cause); + + @Message(id = 3017, value = "No valid permission constructor found on class \"%s\"") + InvalidPermissionClassException noPermissionConstructor(String className); + + @Message(id = 3018, value = "Cannot add permissions to a read-only permission collection") + SecurityException readOnlyPermissionCollection(); + + @Message(id = 3019, value = "Failure to deserialize object: field \"%s\" is null") + InvalidObjectException invalidObjectNull(String fieldName); + + @Message(id = 3020, value = "Expected empty actions string, got \"%s\"") + IllegalArgumentException expectedEmptyActions(String actions); + + @Message(id = 3021, value = "Invalid permission type; expected %s, got %s") + IllegalArgumentException invalidPermissionType(Class expected, Permission actual); + + @Message(id = 3022, value = "Permission check failed: %s is not implied by %s") + SecurityException permissionCheckFailed(Permission permission, PermissionVerifier permissionVerifier); + +} Index: 3rdParty_sources/elytron/org/wildfly/security/permission/ElytronMessages_$logger.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/permission/ElytronMessages_$logger.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/permission/ElytronMessages_$logger.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,120 @@ +package org.wildfly.security.permission; + +import java.util.Locale; +import org.wildfly.security.permission.PermissionVerifier; +import java.io.Serializable; +import javax.annotation.Generated; +import org.jboss.logging.DelegatingBasicLogger; +import java.lang.SecurityException; +import java.lang.String; +import org.jboss.logging.Logger; +import org.wildfly.security.permission.InvalidPermissionClassException; +import org.jboss.logging.BasicLogger; +import java.security.Permission; +import java.lang.Throwable; +import java.io.InvalidObjectException; +import java.lang.Class; +import java.util.Arrays; +import java.lang.ClassNotFoundException; +import java.lang.IllegalArgumentException; + +/** + * Warning this class consists of generated code. + */ +@Generated(value = "org.jboss.logging.processor.generator.model.MessageLoggerImplementor", date = "2024-01-15T21:44:14+0100") +public class ElytronMessages_$logger extends DelegatingBasicLogger implements ElytronMessages, BasicLogger, Serializable { + private static final long serialVersionUID = 1L; + private static final String FQCN = ElytronMessages_$logger.class.getName(); + public ElytronMessages_$logger(final Logger log) { + super(log); + } + private static final Locale LOCALE = Locale.ROOT; + protected Locale getLoggingLocale() { + return LOCALE; + } + protected String permissionCollectionMustBeReadOnly$str() { + return "ELY03013: Permission collection must be read-only"; + } + @Override + public final SecurityException permissionCollectionMustBeReadOnly() { + final SecurityException result = new SecurityException(String.format(getLoggingLocale(), permissionCollectionMustBeReadOnly$str())); + _copyStackTraceMinusOne(result); + return result; + } + private static void _copyStackTraceMinusOne(final Throwable e) { + final StackTraceElement[] st = e.getStackTrace(); + e.setStackTrace(Arrays.copyOfRange(st, 1, st.length)); + } + protected String permissionClassMissing$str() { + return "ELY03015: Could not load permission class \"%s\""; + } + @Override + public final org.wildfly.security.permission.InvalidPermissionClassException permissionClassMissing(final String className, final ClassNotFoundException cause) { + final org.wildfly.security.permission.InvalidPermissionClassException result = new org.wildfly.security.permission.InvalidPermissionClassException(String.format(getLoggingLocale(), permissionClassMissing$str(), className), cause); + _copyStackTraceMinusOne(result); + return result; + } + protected String permissionInstantiation$str() { + return "ELY03016: Could not instantiate permission class \"%s\""; + } + @Override + public final org.wildfly.security.permission.InvalidPermissionClassException permissionInstantiation(final String className, final Throwable cause) { + final org.wildfly.security.permission.InvalidPermissionClassException result = new org.wildfly.security.permission.InvalidPermissionClassException(String.format(getLoggingLocale(), permissionInstantiation$str(), className), cause); + _copyStackTraceMinusOne(result); + return result; + } + protected String noPermissionConstructor$str() { + return "ELY03017: No valid permission constructor found on class \"%s\""; + } + @Override + public final org.wildfly.security.permission.InvalidPermissionClassException noPermissionConstructor(final String className) { + final org.wildfly.security.permission.InvalidPermissionClassException result = new org.wildfly.security.permission.InvalidPermissionClassException(String.format(getLoggingLocale(), noPermissionConstructor$str(), className)); + _copyStackTraceMinusOne(result); + return result; + } + protected String readOnlyPermissionCollection$str() { + return "ELY03018: Cannot add permissions to a read-only permission collection"; + } + @Override + public final SecurityException readOnlyPermissionCollection() { + final SecurityException result = new SecurityException(String.format(getLoggingLocale(), readOnlyPermissionCollection$str())); + _copyStackTraceMinusOne(result); + return result; + } + protected String invalidObjectNull$str() { + return "ELY03019: Failure to deserialize object: field \"%s\" is null"; + } + @Override + public final InvalidObjectException invalidObjectNull(final String fieldName) { + final InvalidObjectException result = new InvalidObjectException(String.format(getLoggingLocale(), invalidObjectNull$str(), fieldName)); + _copyStackTraceMinusOne(result); + return result; + } + protected String expectedEmptyActions$str() { + return "ELY03020: Expected empty actions string, got \"%s\""; + } + @Override + public final IllegalArgumentException expectedEmptyActions(final String actions) { + final IllegalArgumentException result = new IllegalArgumentException(String.format(getLoggingLocale(), expectedEmptyActions$str(), actions)); + _copyStackTraceMinusOne(result); + return result; + } + protected String invalidPermissionType$str() { + return "ELY03021: Invalid permission type; expected %s, got %s"; + } + @Override + public final IllegalArgumentException invalidPermissionType(final Class expected, final Permission actual) { + final IllegalArgumentException result = new IllegalArgumentException(String.format(getLoggingLocale(), invalidPermissionType$str(), expected, actual)); + _copyStackTraceMinusOne(result); + return result; + } + protected String permissionCheckFailed$str() { + return "ELY03022: Permission check failed: %s is not implied by %s"; + } + @Override + public final SecurityException permissionCheckFailed(final Permission permission, final org.wildfly.security.permission.PermissionVerifier permissionVerifier) { + final SecurityException result = new SecurityException(String.format(getLoggingLocale(), permissionCheckFailed$str(), permission, permissionVerifier)); + _copyStackTraceMinusOne(result); + return result; + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/permission/ElytronPermission.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/permission/ElytronPermission.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/permission/ElytronPermission.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,111 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2014 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.permission; + +import org.wildfly.common.Assert; +import org.wildfly.security.util.StringEnumeration; +import org.wildfly.security.util.StringMapping; + +/** + * A general Elytron permission. The permission {@code name} must be one of the following: + *

    + *
  • {@code authenticate}
  • + *
  • {@code createAuthenticator}
  • + *
  • {@code createAuthenticationContextConfigurationClient}
  • + *
  • {@code createSecurityDomain}
  • + *
  • {@code createSecurityRealm}
  • + *
  • {@code registerSecurityDomain}
  • + *
  • {@code getSecurityDomain}
  • + *
  • {@code unregisterSecurityDomain}
  • + *
  • {@code setRunAsPrincipal}
  • + *
  • {@code createServerAuthenticationContext}
  • + *
  • {@code getPrivateCredentials}
  • + *
  • {@code getIdentity}
  • + *
  • {@code getIdentityForUpdate}
  • + *
  • {@code createAdHocIdentity}
  • + *
  • {@code withDefaultRoleMapper}
  • + *
  • {@code handleSecurityEvent}
  • + *
+ * The {@code actions} are not used and should be empty or {@code null}. + * + * @author David M. Lloyd + */ +public final class ElytronPermission extends AbstractNameSetOnlyPermission { + + private static final long serialVersionUID = 6124294238228442419L; + + private static final StringEnumeration strings = StringEnumeration.of( + "authenticate", + "createAuthenticator", + "createAuthenticationContextConfigurationClient", + "createSecurityDomain", + "createSecurityRealm", + "registerSecurityDomain", + "getSecurityDomain", + "unregisterSecurityDomain", + "setRunAsPrincipal", + "createServerAuthenticationContext", + "getPrivateCredentials", + "getIdentity", + "getIdentityForUpdate", + "createAdHocIdentity", + "withDefaultRoleMapper", + "handleSecurityEvent" + ); + + static final StringMapping mapping = new StringMapping<>(strings, ElytronPermission::new); + + private static final ElytronPermission allPermission = new ElytronPermission("*"); + + /** + * Construct a new instance. + * + * @param name the name of the permission + */ + public ElytronPermission(final String name) { + this(name, null); + } + + /** + * Construct a new instance. + * + * @param name the name of the permission + * @param actions the actions (should be empty) + */ + public ElytronPermission(final String name, final String actions) { + super(name, strings); + requireEmptyActions(actions); + } + + public ElytronPermission withName(final String name) { + return forName(name); + } + + /** + * Get the permission with the given name. + * + * @param name the name (must not be {@code null}) + * @return the permission (not {@code null}) + * @throws IllegalArgumentException if the name is not valid + */ + public static ElytronPermission forName(final String name) { + Assert.checkNotNullParam("name", name); + return name.equals("*") ? allPermission : mapping.getItemByString(name); + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/permission/ElytronPermissionCollection.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/permission/ElytronPermissionCollection.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/permission/ElytronPermissionCollection.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,71 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2016 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.permission; + +import java.io.Serializable; +import java.security.Permission; +import java.security.PermissionCollection; +import java.util.Enumeration; + +import org.wildfly.common.Assert; +import org.wildfly.security.util.StringMapping; + +/** + * Stub class for the unlikely event that a serialized instance is lying around somewhere. + * + * @author David M. Lloyd + */ +@Deprecated +final class ElytronPermissionCollection extends PermissionCollection implements Serializable { + + private static final long serialVersionUID = 1L; + + private final int p1; + + ElytronPermissionCollection(final int p1) { + this.p1 = p1; + } + + public void add(final Permission permission) { + throw Assert.unsupported(); + } + + public boolean implies(final Permission permission) { + throw Assert.unsupported(); + } + + public Enumeration elements() { + throw Assert.unsupported(); + } + + Object readResolve() { + final AbstractPermissionCollection collection = new ElytronPermission("*").newPermissionCollection(); + final StringMapping mapping = ElytronPermission.mapping; + int bits = p1; + int test; + while (bits != 0) { + collection.add(mapping.getItemById(Integer.numberOfTrailingZeros(test = Integer.lowestOneBit(bits)))); + bits &= ~test; + } + if (isReadOnly()) { + collection.setReadOnly(); + } + return collection; + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/permission/IntNameSetPermissionCollection.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/permission/IntNameSetPermissionCollection.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/permission/IntNameSetPermissionCollection.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,118 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2016 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.permission; + +import java.security.Permission; +import java.util.NoSuchElementException; +import java.util.concurrent.atomic.AtomicInteger; + +import org.wildfly.security.util.EnumerationIterator; +import org.wildfly.security.util.StringEnumeration; + +final class IntNameSetPermissionCollection extends NameSetPermissionCollection { + + private final AtomicInteger bitSet = new AtomicInteger(); + + IntNameSetPermissionCollection(final AbstractPermission sourcePermission, final StringEnumeration nameEnumeration) { + super(sourcePermission, nameEnumeration); + } + + private Permission permissionFor(int id) { + return ((AbstractNamedPermission)getSourcePermission()).withName(getNameEnumeration().nameOf(id)); + } + + protected void doAdd(final AbstractPermission permission) { + int setBits= getBitsForName(permission); + final AtomicInteger bitSet = this.bitSet; + int oldVal; + do { + oldVal = bitSet.get(); + if ((oldVal & setBits) == setBits) { + return; + } + } while (! bitSet.compareAndSet(oldVal, oldVal | setBits)); + } + + public boolean implies(final Permission permission) { + if (permission.getClass() != getSourcePermission().getClass()) { + return false; + } + long testBits = getBitsForName(permission); + return (bitSet.get() & testBits) == testBits; + } + + public int size() { + final int size = Integer.bitCount(bitSet.get()); + return size == getNameEnumeration().size() ? 1 : size; + } + + public EnumerationIterator iterator() { + return new Iter(bitSet.get()); + } + + public EnumerationIterator elements() { + return iterator(); + } + + private int getBitsForName(final Permission permission) { + final int bits; + final String name = permission.getName(); + final StringEnumeration nameEnumeration = getNameEnumeration(); + if ("*".equals(name)) { + // add all names + bits = (1 << nameEnumeration.size()) - 1; + } else { + bits = 1 << nameEnumeration.indexOf(name); + } + return bits; + } + + private class Iter implements EnumerationIterator { + private int bits; + + Iter(final int bits) { + this.bits = bits; + } + + public boolean hasMoreElements() { + return bits != 0; + } + + public Permission nextElement() { + final int bits = this.bits; + if (bits == 0) throw new NoSuchElementException(); + if (Integer.bitCount(bits) == getNameEnumeration().size()) { + this.bits = 0; + return ((AbstractNamedPermission) getSourcePermission()).withName("*"); + } + int bit = Integer.lowestOneBit(bits); + this.bits = bits & ~bit; + return permissionFor(Integer.numberOfTrailingZeros(bit)); + } + + public boolean hasNext() { + return hasMoreElements(); + } + + public Permission next() { + return nextElement(); + } + } + +} Index: 3rdParty_sources/elytron/org/wildfly/security/permission/IntersectionPermissionCollection.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/permission/IntersectionPermissionCollection.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/permission/IntersectionPermissionCollection.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,54 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2016 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.permission; + +import java.security.Permission; +import java.security.PermissionCollection; +import java.util.Enumeration; + +import org.wildfly.common.Assert; + + +final class IntersectionPermissionCollection extends PermissionCollection implements PermissionVerifier { + private static final long serialVersionUID = 8045087406778847303L; + + private final PermissionCollection pc1; + private final PermissionCollection pc2; + + IntersectionPermissionCollection(final PermissionCollection pc1, final PermissionCollection pc2) { + this.pc1 = pc1; + this.pc2 = pc2; + setReadOnly(); + } + + public void add(final Permission permission) { + throw ElytronMessages.log.readOnlyPermissionCollection(); + } + + public boolean implies(final Permission permission) { + return pc1.implies(permission) && pc2.implies(permission); + } + + public Enumeration elements() { + // TODO: this is theoretically possible to implement using an IntersectionCollectionPermission; + // however the primary use case is going to be in protection domains and verification scenarios so we may + // not ever actually need this + throw Assert.unsupported(); + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/permission/InvalidPermissionClassException.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/permission/InvalidPermissionClassException.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/permission/InvalidPermissionClassException.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,67 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2016 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.permission; + +/** + * An exception which is thrown when an invalid permission class is instantiated. + * + * @author David M. Lloyd + */ +public class InvalidPermissionClassException extends IllegalArgumentException { + private static final long serialVersionUID = 7910334218992625018L; + + /** + * Constructs a new {@code InvalidPermissionClassException} instance. The message is left blank ({@code null}), and + * no + * cause is specified. + */ + public InvalidPermissionClassException() { + } + + /** + * Constructs a new {@code InvalidPermissionClassException} instance with an initial message. No + * cause is specified. + * + * @param msg the message + */ + public InvalidPermissionClassException(final String msg) { + super(msg); + } + + /** + * Constructs a new {@code InvalidPermissionClassException} instance with an initial cause. If + * a non-{@code null} cause is specified, its message is used to initialize the message of this + * {@code InvalidPermissionClassException}; otherwise the message is left blank ({@code null}). + * + * @param cause the cause + */ + public InvalidPermissionClassException(final Throwable cause) { + super(cause); + } + + /** + * Constructs a new {@code InvalidPermissionClassException} instance with an initial message and cause. + * + * @param msg the message + * @param cause the cause + */ + public InvalidPermissionClassException(final String msg, final Throwable cause) { + super(msg, cause); + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/permission/LongNameSetPermissionCollection.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/permission/LongNameSetPermissionCollection.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/permission/LongNameSetPermissionCollection.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,118 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2016 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.permission; + +import java.security.Permission; +import java.util.NoSuchElementException; +import java.util.concurrent.atomic.AtomicLong; + +import org.wildfly.security.util.EnumerationIterator; +import org.wildfly.security.util.StringEnumeration; + +final class LongNameSetPermissionCollection extends NameSetPermissionCollection { + + private final AtomicLong bitSet = new AtomicLong(); + + LongNameSetPermissionCollection(final AbstractPermission sourcePermission, final StringEnumeration nameEnumeration) { + super(sourcePermission, nameEnumeration); + } + + private Permission permissionFor(int id) { + return ((AbstractNamedPermission)getSourcePermission()).withName(getNameEnumeration().nameOf(id)); + } + + protected void doAdd(final AbstractPermission permission) { + long setBits = getBitsForName(permission); + final AtomicLong bitSet = this.bitSet; + long oldVal; + do { + oldVal = bitSet.get(); + if ((oldVal & setBits) == setBits) { + return; + } + } while (! bitSet.compareAndSet(oldVal, oldVal | setBits)); + } + + public boolean implies(final Permission permission) { + if (permission.getClass() != getSourcePermission().getClass()) { + return false; + } + long testBits = getBitsForName(permission); + return (bitSet.get() & testBits) == testBits; + } + + public int size() { + final int size = Long.bitCount(bitSet.get()); + return size == getNameEnumeration().size() ? 1 : size; + } + + public EnumerationIterator iterator() { + return new Iter(bitSet.get()); + } + + public EnumerationIterator elements() { + return iterator(); + } + + private long getBitsForName(final Permission permission) { + final long bits; + final String name = permission.getName(); + final StringEnumeration nameEnumeration = getNameEnumeration(); + if ("*".equals(name)) { + // add all names + bits = (1L << nameEnumeration.size()) - 1; + } else { + bits = 1L << nameEnumeration.indexOf(name); + } + return bits; + } + + private class Iter implements EnumerationIterator { + private long bits; + + Iter(final long bits) { + this.bits = bits; + } + + public boolean hasMoreElements() { + return bits != 0; + } + + public Permission nextElement() { + final long bits = this.bits; + if (bits == 0) throw new NoSuchElementException(); + if (Long.bitCount(bits) == getNameEnumeration().size()) { + this.bits = 0; + return ((AbstractNamedPermission) getSourcePermission()).withName("*"); + } + long bit = Long.lowestOneBit(bits); + this.bits = bits & ~bit; + return permissionFor(Long.numberOfTrailingZeros(bit)); + } + + public boolean hasNext() { + return hasMoreElements(); + } + + public Permission next() { + return nextElement(); + } + } + +} Index: 3rdParty_sources/elytron/org/wildfly/security/permission/NameSetPermissionCollection.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/permission/NameSetPermissionCollection.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/permission/NameSetPermissionCollection.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,74 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2016 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.permission; + +import org.wildfly.common.Assert; +import org.wildfly.security.util.StringEnumeration; + +/** + * A permission collection for permissions with a finite set of names, which is based on a simple bit set. + * In this type of collection, each bit represents a unique permission of a given name. This type is not suitable for + * permissions with actions. + * + * @author David M. Lloyd + */ +public abstract class NameSetPermissionCollection extends AbstractPermissionCollection { + private static final long serialVersionUID = - 9191397492173027470L; + private final StringEnumeration nameEnumeration; + + /** + * Construct a new instance. The name enumeration is pulled from the source permission object. + * + * @param sourcePermission the source permission object (must not be {@code null}) + * @return the permission collection + */ + public static AbstractPermissionCollection newInstance(final AbstractNameSetOnlyPermission sourcePermission) { + return newInstance(sourcePermission, sourcePermission.getNameEnumeration()); + } + + /** + * Construct a new instance. + * + * @param sourcePermission the source permission object (must not be {@code null}) + * @param nameEnumeration the name enumeration for this permission type (must not be {@code null}) + * @return the permission collection + */ + public static AbstractPermissionCollection newInstance(final AbstractPermission sourcePermission, final StringEnumeration nameEnumeration) { + Assert.checkNotNullParam("sourcePermission", sourcePermission); + Assert.checkNotNullParam("nameEnumeration", nameEnumeration); + final int size = nameEnumeration.size(); + if (size <= 32) { + return new IntNameSetPermissionCollection(sourcePermission, nameEnumeration); + } else if (size <= 64) { + return new LongNameSetPermissionCollection(sourcePermission, nameEnumeration); + } else { + // TODO: add GiantNameSetPermissionCollection which uses AtomicIntegerArray + throw Assert.unsupported(); + } + } + + NameSetPermissionCollection(final AbstractPermission sourcePermission, final StringEnumeration nameEnumeration) { + super(sourcePermission); + this.nameEnumeration = nameEnumeration; + } + + StringEnumeration getNameEnumeration() { + return nameEnumeration; + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/permission/NoPermission.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/permission/NoPermission.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/permission/NoPermission.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,98 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2016 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.permission; + +/** + * A permission which implies nothing, not even itself. + * + * @author David M. Lloyd + */ +public final class NoPermission extends AbstractPermission { + private static final long serialVersionUID = 2339781690941885693L; + + private static final NoPermission INSTANCE = new NoPermission(); + + /** + * Construct a new instance. + */ + public NoPermission() { + super(""); + } + + /** + * Construct a new instance. The name parameter is ignored. + * + * @param ignored ignored + */ + public NoPermission(final String ignored) { + this(); + } + + /** + * Construct a new instance. The name and actions parameters are ignored. + * + * @param ignored1 ignored + * @param ignored2 ignored + */ + public NoPermission(final String ignored1, final String ignored2) { + this(); + } + + /** + * Get the no-permission instance. + * + * @return the no-permission instance (not {@code null}) + */ + public static NoPermission getInstance() { + return INSTANCE; + } + + /** + * Always returns {@code false}. + * + * @param permission ignored + * @return {@code false} + */ + public boolean implies(final NoPermission permission) { + return false; + } + + /** + * Always returns {@code true} if the argument is not {@code null}. + * + * @param other the permission to compare to + * @return {@code true} if {@code other} is not {@code null}; {@code false} otherwise + */ + public boolean equals(final NoPermission other) { + return other != null; + } + + /** + * Get the constant hash code. + * + * @return the constant hash code + */ + public int hashCode() { + return getClass().hashCode(); + } + + public AbstractPermissionCollection newPermissionCollection() { + return NoPermissionCollection.getInstance(); + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/permission/NoPermissionCollection.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/permission/NoPermissionCollection.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/permission/NoPermissionCollection.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,75 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2016 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.permission; + +import java.security.Permission; +import java.util.Collections; +import java.util.Enumeration; +import java.util.Iterator; + +/** + * The permission collection type for {@link NoPermission}. + * + * @author David M. Lloyd + */ +final class NoPermissionCollection extends AbstractPermissionCollection { + private static final long serialVersionUID = - 8826282614161412469L; + + private static NoPermissionCollection INSTANCE = new NoPermissionCollection(); + + NoPermissionCollection() { + super(NoPermission.getInstance()); + } + + static NoPermissionCollection getInstance() { + return INSTANCE; + } + + protected void doAdd(final AbstractPermission permission) { + // no action + } + + public boolean implies(final Permission permission) { + return false; + } + + public Enumeration elements() { + return Collections.emptyEnumeration(); + } + + public Iterator iterator() { + return Collections.emptyIterator(); + } + + public int size() { + return 0; + } + + Object readResolve() { + return INSTANCE; + } + + public boolean equals(final Object obj) { + return obj instanceof NoPermissionCollection; + } + + public int hashCode() { + return getClass().hashCode(); + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/permission/PermissionActions.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/permission/PermissionActions.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/permission/PermissionActions.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,437 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2016 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.permission; + +import static org.wildfly.security.permission.SecurityMessages.permission; + +import org.wildfly.common.Assert; + +import java.util.Arrays; +import java.util.EnumSet; +import java.util.Iterator; + +/** + * A helper class for defining permissions which use a finite list of actions. Define custom permissions using + * an {@code enum} of actions, where the string representation (via {@code toString()}) of each enum is one possible + * action name. Typically the {@code enum} should be non-public, and the constant names should be lowercase. If + * an action name contains a character which is not a valid Java identifier, then the {@code toString()} method of + * such constants should be overridden to report the correct string. The actions may be stored on the permission as + * an {@code EnumSet}, an {@code int}, or a {@code long}. The field should be marked {@code transient}, and + * the actions represented by a (possibly synthetic) field of type {@code String} which uses the canonical representation + * of the actions. + * + * @author David M. Lloyd + * + * @deprecated Use one of the abstract permission classes like {@link AbstractActionSetPermission} instead. + */ +@Deprecated +public final class PermissionActions { + + private PermissionActions() { + } + + static final class TrieNode { + private static final char[] C_EMPTY = new char[0]; + private static final TrieNode[] T_EMPTY = new TrieNode[0]; + + private E result; + private char[] matches = C_EMPTY; + @SuppressWarnings("unchecked") + private TrieNode[] children = T_EMPTY; + + void put(String s, int idx, E value) { + if (idx == s.length()) { + result = value; + return; + } + char c = s.charAt(idx); + final int i = Arrays.binarySearch(matches, c); + if (i < 0) { + // copy and add + final int oldLength = matches.length; + final char[] newMatches = Arrays.copyOf(matches, oldLength + 1); + final TrieNode[] newChildren = Arrays.copyOf(children, oldLength + 1); + // i is the negated insertion index + final int insertIndex = -i - 1; + System.arraycopy(newMatches, insertIndex, newMatches, insertIndex + 1, oldLength - insertIndex); + System.arraycopy(newChildren, insertIndex, newChildren, insertIndex + 1, oldLength - insertIndex); + newMatches[insertIndex] = c; + final TrieNode newNode = new TrieNode<>(); + newChildren[insertIndex] = newNode; + matches = newMatches; + children = newChildren; + newNode.put(s, idx + 1, value); + } else { + children[i].put(s, idx + 1, value); + } + } + + E get(String s, int idx, int end) { + if (idx == end) { + return result; + } + final char c = s.charAt(idx); + final int i = Arrays.binarySearch(matches, c); + if (i < 0) { + return null; + } + return children[i].get(s, idx + 1, end); + } + } + + static final class Info { + final TrieNode root; + final E[] constants; + + Info(final TrieNode root, final E[] constants) { + this.root = root; + this.constants = constants; + } + } + + private static final ClassValue> storedInfo = new ClassValue>() { + protected Info computeValue(final Class type) { + return computeReal(type); + } + + private Info computeReal(final Class type) { + final TrieNode root = new TrieNode<>(); + final E[] enumConstants = type.getEnumConstants(); + for (E e : enumConstants) { + root.put(e.toString(), 0, e); + } + return new Info<>(root, type.getEnumConstants()); + } + }; + + interface MatchAction> { + void matched(E item); + + void matchedAll(Class type); + } + + static class SetMatchAction> implements MatchAction { + private EnumSet set; + + SetMatchAction(final EnumSet set) { + this.set = set; + } + + public void matched(final E item) { + set.add(item); + } + + public void matchedAll(final Class type) { + set = EnumSet.allOf(type); + } + + public EnumSet getSet() { + return set; + } + } + + static class IntMatchAction> implements MatchAction { + private int result; + + IntMatchAction() { + } + + public void matched(final E item) { + result |= 1 << item.ordinal(); + } + + public void matchedAll(final Class type) { + result |= (1 << storedInfo.get(type).constants.length) - 1; + } + + public int getResult() { + return result; + } + } + + static class LongMatchAction> implements MatchAction { + private long result; + + LongMatchAction() { + } + + public void matched(final E item) { + result |= 1L << item.ordinal(); + } + + public void matchedAll(final Class type) { + result |= (1L << storedInfo.get(type).constants.length) - 1; + } + + public long getResult() { + return result; + } + } + + /** + * Parse an action string using the given action type to an {@code EnumSet}. + * + * @param actionType the action {@code enum} type class + * @param actionString the string to parse + * @param the action {@code enum} type + * + * @return the set of actions from the string + * + * @throws IllegalArgumentException if the string contained an invalid action + */ + public static > EnumSet parseActionStringToSet(Class actionType, String actionString) throws IllegalArgumentException { + Assert.checkNotNullParam("actionType", actionType); + Assert.checkNotNullParam("actionString", actionString); + final SetMatchAction matchAction = new SetMatchAction<>(EnumSet.noneOf(actionType)); + doParse(actionType, actionString, matchAction); + return matchAction.getSet(); + } + + /** + * Parse an action string using the given action type to an {@code int}. The given {@code enum} type must have + * 32 or fewer constant values. + * + * @param actionType the action {@code enum} type class + * @param actionString the string to parse + * @param the action {@code enum} type + * + * @return the set of actions from the string + * + * @throws IllegalArgumentException if the string contained an invalid action + */ + public static > int parseActionStringToInt(Class actionType, String actionString) throws IllegalArgumentException { + Assert.checkNotNullParam("actionType", actionType); + Assert.checkNotNullParam("actionString", actionString); + final IntMatchAction matchAction = new IntMatchAction<>(); + doParse(actionType, actionString, matchAction); + return matchAction.getResult(); + } + + /** + * Parse an action string using the given action type to a {@code long}. The given {@code enum} type must have + * 64 or fewer constant values. + * + * @param actionType the action {@code enum} type class + * @param actionString the string to parse + * @param the action {@code enum} type + * + * @return the set of actions from the string + * + * @throws IllegalArgumentException if the string contained an invalid action + */ + public static > long parseActionStringToLong(Class actionType, String actionString) throws IllegalArgumentException { + Assert.checkNotNullParam("actionType", actionType); + Assert.checkNotNullParam("actionString", actionString); + final LongMatchAction matchAction = new LongMatchAction<>(); + doParse(actionType, actionString, matchAction); + return matchAction.getResult(); + } + + private static > void doParse(final Class actionType, final String actionString, final MatchAction matchAction) { + @SuppressWarnings("unchecked") + final Info info = (Info) storedInfo.get(actionType); + final TrieNode rootNode = info.root; + // begin parse + char c; + final int length = actionString.length(); + int i = 0; + L0: for (;;) { + if (i == length) { + // OK + break L0; + } + c = actionString.charAt(i); + if (Character.isWhitespace(c)) { + i ++; + continue L0; + } + if (c == ',') { + // hmm, empty segment; ignore it + i ++; + continue L0; + } + if (c == '*') { + // potential star + matchAction.matchedAll(actionType); + for (;;) { + i ++; + if (i == length) { + // done + break L0; + } + c = actionString.charAt(i); + if (c == ',') { + // pointless, but go on + i ++; + continue L0; + } + if (! Character.isWhitespace(c)) { + throw permission.unexpectedActionCharacter(c, i, actionString); + } + } + // not reachable + } + // else it's a potentially valid character + int start = i; + for (;;) { + i++; + c = i < length ? actionString.charAt(i) : 0; + if (i == length || Character.isWhitespace(c) || c == ',') { + // action string ends here + final E action = rootNode.get(actionString, start, i); + if (action == null) { + throw permission.invalidAction(actionString.substring(start, i), start, actionString); + } + matchAction.matched(action); + if (i == length) { + // done + break L0; + } + while (Character.isWhitespace(c)) { + i++; + if (i == length) { + // done + break L0; + } + c = actionString.charAt(i); + } + if (c != ',') { + throw permission.unexpectedActionCharacter(c, i, actionString); + } + i ++; + continue L0; + } + } + // not reachable + } + } + + /** + * Get the canonical action string representation for the given action set. + * + * @param set the action set + * @param the action type + * @return the canonical representation + */ + public static > String getCanonicalActionString(EnumSet set) { + if (set == null || set.isEmpty()) return ""; + final StringBuilder b = new StringBuilder(); + getCanonicalActionString(set, b); + return b.toString(); + } + + /** + * Get the canonical action string representation for the given action set, appending it to the given string builder. + * + * @param set the action set + * @param b the string builder + * @param the action type + */ + public static > void getCanonicalActionString(EnumSet set, StringBuilder b) { + if (set == null || set.isEmpty()) return; + final Iterator iterator = set.iterator(); + if (iterator.hasNext()) { + E e = iterator.next(); + b.append(e.toString()); + while (iterator.hasNext()) { + e = iterator.next(); + b.append(','); + b.append(e.toString()); + } + } + } + + /** + * Get the canonical action string representation for the given action set. + * + * @param type the action {@code enum} type class + * @param set the action set + * @param the action type + * @return the canonical representation + */ + public static > String getCanonicalActionString(Class type, int set) { + if (set == 0) return ""; + final StringBuilder b = new StringBuilder(); + getCanonicalActionString(type, set, b); + return b.toString(); + } + + /** + * Get the canonical action string representation for the given action set, appending it to the given string builder. + * + * @param type the action {@code enum} type class + * @param set the action set + * @param b the string builder + * @param the action type + */ + public static > void getCanonicalActionString(Class type, int set, StringBuilder b) { + if (set == 0) return; + @SuppressWarnings("unchecked") + final E[] constants = (E[]) storedInfo.get(type).constants; + int bit = Integer.lowestOneBit(set); + E e = constants[Integer.numberOfTrailingZeros(bit)]; + b.append(e.toString()); + set &= ~bit; + while (set != 0) { + bit = Integer.lowestOneBit(set); + e = constants[Integer.numberOfTrailingZeros(bit)]; + b.append(',').append(e.toString()); + set &= ~bit; + } + } + + /** + * Get the canonical action string representation for the given action set. + * + * @param type the action {@code enum} type class + * @param set the action set + * @param the action type + * @return the canonical representation + */ + public static > String getCanonicalActionString(Class type, long set) { + if (set == 0) return ""; + final StringBuilder b = new StringBuilder(); + getCanonicalActionString(type, set, b); + return b.toString(); + } + + /** + * Get the canonical action string representation for the given action set, appending it to the given string builder. + * + * @param type the action {@code enum} type class + * @param set the action set + * @param b the string builder + * @param the action type + */ + public static > void getCanonicalActionString(Class type, long set, StringBuilder b) { + if (set == 0) return; + @SuppressWarnings("unchecked") + final E[] constants = (E[]) storedInfo.get(type).constants; + long bit = Long.lowestOneBit(set); + E e = constants[Long.numberOfTrailingZeros(bit)]; + b.append(e.toString()); + set &= ~bit; + while (set != 0) { + bit = Long.lowestOneBit(set); + e = constants[Long.numberOfTrailingZeros(bit)]; + b.append(',').append(e.toString()); + set &= ~bit; + } + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/permission/PermissionUtil.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/permission/PermissionUtil.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/permission/PermissionUtil.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,478 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2016 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.permission; + +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.UndeclaredThrowableException; +import java.security.AllPermission; +import java.security.Permission; +import java.security.PermissionCollection; +import java.security.Permissions; +import java.util.Arrays; +import java.util.Collection; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.function.BiConsumer; +import java.util.function.BiPredicate; +import java.util.function.Consumer; +import java.util.function.IntFunction; +import java.util.function.LongFunction; +import java.util.function.Predicate; +import java.util.function.ToIntFunction; +import java.util.function.ToLongFunction; + +import org.wildfly.common.Assert; + +/** + * General permission utility methods and constants. + * + * @author David M. Lloyd + */ +public final class PermissionUtil { + + private PermissionUtil() { + } + + /** + * A shared {@link AllPermission} instance. + */ + public static final Permission ALL_PERMISSION = new AllPermission(); + + /** + * A read-only permission collection which implies {@link AllPermission}. + */ + public static final PermissionCollection ALL_PERMISSIONS; + + /** + * A permission collection which is empty. + */ + public static final PermissionCollection EMPTY_PERMISSION_COLLECTION; + + /** + * An array with no permissions in it. + */ + public static final Permission[] NO_PERMISSIONS = new Permission[0]; + + static { + Permissions permissions = new Permissions(); + permissions.add(ALL_PERMISSION); + permissions.setReadOnly(); + ALL_PERMISSIONS = permissions; + permissions = new Permissions(); + permissions.setReadOnly(); + EMPTY_PERMISSION_COLLECTION = permissions; + } + + /** + * Parse an actions string, using the given function to map action strings to bits. + * + * @param actionsString the actions string (must not be {@code null}) + * @param function the mapping function (must not be {@code null}) + * @return the union of all the action bits + * @throws IllegalArgumentException if {@code function} throws this exception (indicating an invalid action string) + */ + public static int parseActions(String actionsString, ToIntFunction function) throws IllegalArgumentException { + Assert.checkNotNullParam("actionsString", actionsString); + Assert.checkNotNullParam("function", function); + int actions = 0; + int pos = 0; + int idx = actionsString.indexOf(','); + for (;;) { + String str; + if (idx == -1) { + str = actionsString.substring(pos, actionsString.length()).trim(); + if (! str.isEmpty()) actions |= function.applyAsInt(str); + return actions; + } else { + str = actionsString.substring(pos, idx).trim(); + pos = idx + 1; + if (! str.isEmpty()) actions |= function.applyAsInt(str); + idx = actionsString.indexOf(',', pos); + } + } + } + + /** + * Parse an actions string, using the given function to map action strings to bits. + * + * @param actionsString the actions string (must not be {@code null}) + * @param function the mapping function (must not be {@code null}) + * @return the union of all the action bits + * @throws IllegalArgumentException if {@code function} throws this exception (indicating an invalid action string) + */ + public static long parseActions(String actionsString, ToLongFunction function) throws IllegalArgumentException { + Assert.checkNotNullParam("actionsString", actionsString); + Assert.checkNotNullParam("function", function); + long actions = 0; + int pos = 0; + int idx = actionsString.indexOf(','); + for (;;) { + String str; + if (idx == -1) { + str = actionsString.substring(pos, actionsString.length()).trim(); + if (! str.isEmpty()) actions |= function.applyAsLong(str); + return actions; + } else { + str = actionsString.substring(pos, idx).trim(); + pos = idx + 1; + if (! str.isEmpty()) actions |= function.applyAsLong(str); + idx = actionsString.indexOf(',', pos); + } + } + } + + /** + * Deparse an action bit set, using the given function to map action bits to strings. If the bits are all clear, + * the empty string {@code ""} is returned. + * + * @param actionBits the action bit set + * @param mappingFunction the mapping function (must not be {@code null}) + * @return the actions string (not {@code null}) + */ + public static String toActionsString(int actionBits, IntFunction mappingFunction) { + Assert.checkNotNullParam("mappingFunction", mappingFunction); + final StringBuilder sb = new StringBuilder(); + if (actionBits == 0) return ""; + int lb = Integer.highestOneBit(actionBits); + sb.append(mappingFunction.apply(lb)); + actionBits &= ~lb; + while (actionBits != 0) { + lb = Integer.highestOneBit(actionBits); + sb.append(',').append(mappingFunction.apply(lb)); + actionBits &= ~lb; + } + return sb.toString(); + } + + /** + * Deparse an action bit set, using the given function to map action bits to strings. If the bits are all clear, + * the empty string {@code ""} is returned. + * + * @param actionBits the action bit set + * @param mappingFunction the mapping function (must not be {@code null}) + * @return the actions string (not {@code null}) + */ + public static String toActionsString(long actionBits, LongFunction mappingFunction) { + Assert.checkNotNullParam("mappingFunction", mappingFunction); + final StringBuilder sb = new StringBuilder(); + if (actionBits == 0) return ""; + long lb = Long.highestOneBit(actionBits); + sb.append(mappingFunction.apply(lb)); + actionBits &= ~lb; + while (actionBits != 0) { + lb = Long.highestOneBit(actionBits); + sb.append(',').append(mappingFunction.apply(lb)); + actionBits &= ~lb; + } + return sb.toString(); + } + + /** + * Create an iterable view over a permission collection. + * + * @param pc the permission collection (must not be {@code null}) + * @return the iterable view (not {@code null}) + */ + public static Iterable iterable(PermissionCollection pc) { + return () -> { + final Enumeration elements = pc.elements(); + return new Iterator() { + public boolean hasNext() { + return elements.hasMoreElements(); + } + + public Permission next() { + return elements.nextElement(); + } + }; + }; + } + + /** + * Perform an action for each permission in the given collection. + * + * @param collection the collection (must not be {@code null}) + * @param consumer the consumer to which each permission should be passed (must not be {@code null}) + */ + public static void forEachIn(PermissionCollection collection, Consumer consumer) { + Assert.checkNotNullParam("collection", collection); + Assert.checkNotNullParam("consumer", consumer); + final Enumeration elements = collection.elements(); + while (elements.hasMoreElements()) { + consumer.accept(elements.nextElement()); + } + } + + /** + * Perform an action for each permission in the given collection. + * + * @param collection the collection (must not be {@code null}) + * @param parameter the parameter to pass to the consumer + * @param consumer the consumer to which each permission should be passed (must not be {@code null}) + * @param

the type of the parameter + * @return the {@code parameter} that was passed in + */ + public static

P forEachIn(PermissionCollection collection, BiConsumer consumer, P parameter) { + Assert.checkNotNullParam("collection", collection); + Assert.checkNotNullParam("consumer", consumer); + final Enumeration elements = collection.elements(); + while (elements.hasMoreElements()) { + consumer.accept(parameter, elements.nextElement()); + } + return parameter; + } + + /** + * Run a test for each permission in the given collection. If the predicate returns {@code false} for any element, + * {@code false} is returned; otherwise, {@code true} is returned. + * + * @param collection the collection (must not be {@code null}) + * @param predicate the predicate to apply to each element (must not be {@code null}) + * @return {@code true} if the predicate matched all the permissions in the collection, {@code false} otherwise + */ + public static boolean forEachIn(PermissionCollection collection, Predicate predicate) { + Assert.checkNotNullParam("collection", collection); + Assert.checkNotNullParam("predicate", predicate); + final Enumeration elements = collection.elements(); + while (elements.hasMoreElements()) { + if (! predicate.test(elements.nextElement())) { + return false; + } + } + return true; + } + + /** + * Run a test for each permission in the given collection. If the predicate returns {@code false} for any element, + * {@code false} is returned; otherwise, {@code true} is returned. + * + * @param collection the collection (must not be {@code null}) + * @param parameter the parameter to pass to the consumer + * @param predicate the predicate to apply to each element (must not be {@code null}) + * @param

the type of the parameter + * @return {@code true} if the predicate matched all the permissions in the collection, {@code false} otherwise + */ + public static

boolean forEachIn(PermissionCollection collection, BiPredicate predicate, P parameter) { + Assert.checkNotNullParam("collection", collection); + Assert.checkNotNullParam("predicate", predicate); + final Enumeration elements = collection.elements(); + while (elements.hasMoreElements()) { + if (! predicate.test(parameter, elements.nextElement())) { + return false; + } + } + return true; + } + + /** + * Create a permission collection that is the union of two permission collections. The permission + * collections must be read-only. + * + * @param pc1 the first permission collection (must not be {@code null}) + * @param pc2 the second permission collection (must not be {@code null}) + * @return a new permission collection that is the union of the two collections (not {@code null}) + */ + public static PermissionCollection union(PermissionCollection pc1, PermissionCollection pc2) { + Assert.checkNotNullParam("pc1", pc1); + Assert.checkNotNullParam("pc2", pc2); + if (! pc1.isReadOnly() || ! pc2.isReadOnly()) { + throw ElytronMessages.log.permissionCollectionMustBeReadOnly(); + } + if (pc1.implies(ALL_PERMISSION) || pc2.implies(ALL_PERMISSION)) { + return ALL_PERMISSIONS; + } else { + return new UnionPermissionCollection(pc1, pc2); + } + } + + /** + * Create a permission collection that is the intersection of two permission collections. The permission + * collections must be read-only. + * + * @param pc1 the first permission collection (must not be {@code null}) + * @param pc2 the second permission collection (must not be {@code null}) + * @return a new permission collection that is the intersection of the two collections (not {@code null}) + */ + public static PermissionCollection intersection(PermissionCollection pc1, PermissionCollection pc2) { + Assert.checkNotNullParam("pc1", pc1); + Assert.checkNotNullParam("pc2", pc2); + if (! pc1.isReadOnly() || ! pc2.isReadOnly()) { + throw ElytronMessages.log.permissionCollectionMustBeReadOnly(); + } + if (pc1.implies(ALL_PERMISSION)) { + return pc2; + } else if (pc2.implies(ALL_PERMISSION)) { + return pc1; + } else { + return new IntersectionPermissionCollection(pc1, pc2); + } + } + + /** + * Determine if one collection implies all the permissions in the other collection. + * + * @param collection the collection to check against (must not be {@code null}) + * @param testCollection the collection whose permissions are to be tested (must not be {@code null}) + * @return {@code true} if {@code collection} implies all of the permissions in {@code testCollection}, {@code false} otherwise + */ + public static boolean impliesAll(PermissionCollection collection, PermissionCollection testCollection) { + return forEachIn(collection, PermissionCollection::implies, testCollection); + } + + /** + * Determine if two permission collections are equal, that is, each collection implies all of the permissions in the + * other collection. + * + * @param pc1 the first collection (must not be {@code null}) + * @param pc2 the second collection (must not be {@code null}) + * @return {@code true} if the collections imply one another, {@code false} otherwise + */ + public static boolean equals(PermissionCollection pc1, PermissionCollection pc2) { + return impliesAll(pc1, pc2) && impliesAll(pc2, pc1); + } + + /** + * Add all of the permissions from the source collection to the target collection. + * + * @param target the target collection (must not be {@code null}) + * @param source the source collection (must not be {@code null}) + * @return the target collection (not {@code null}) + */ + public static PermissionCollection addAll(PermissionCollection target, PermissionCollection source) { + return forEachIn(source, PermissionCollection::add, target); + } + + /** + * Add all of the permissions from the source collection to the target collection. + * + * @param target the target collection (must not be {@code null}) + * @param source the source collection (must not be {@code null}) + * @return the target collection (not {@code null}) + */ + public static PermissionCollection addAll(PermissionCollection target, Collection source) { + source.forEach(target::add); + return target; + } + + /** + * Add a permission to a collection, returning the target collection. If the permission is {@code null}, it is + * not added. + * + * @param target the target collection (must not be {@code null}) + * @param source the permission to add + * @return the target collection (not {@code null}) + */ + public static PermissionCollection add(PermissionCollection target, Permission source) { + Assert.checkNotNullParam("target", target); + if (source != null) target.add(source); + return target; + } + + /** + * Instantiate a permission with the given class name, permission name, and actions. + * + * @param classLoader the class loader to search in ({@code null} indicates the system class loader) + * @param className the name of the permission class to instantiate (must not be {@code null}) + * @param name the permission name (may be {@code null} if allowed by the permission class) + * @param actions the permission actions (may be {@code null} if allowed by the permission class) + * @return the permission object (not {@code null}) + * @throws InvalidPermissionClassException if the permission class does not exist or is not valid + * @throws ClassCastException if the class name does not refer to a subclass of {@link Permission} + */ + public static Permission createPermission(final ClassLoader classLoader, final String className, final String name, final String actions) { + Assert.checkNotNullParam("className", className); + final Class permissionClass; + try { + permissionClass = Class.forName(className, true, classLoader).asSubclass(Permission.class); + } catch (ClassNotFoundException e) { + throw ElytronMessages.log.permissionClassMissing(className, e); + } + return createPermission(permissionClass, name, actions); + } + + /** + * Instantiate a permission with the given class, permission name, and actions. + * + * @param permissionClass the permission class to instantiate (must not be {@code null}) + * @param name the permission name (may be {@code null} if allowed by the permission class) + * @param actions the permission actions (may be {@code null} if allowed by the permission class) + * @return the permission object (not {@code null}) + * @throws InvalidPermissionClassException if the permission class does not exist or is not valid + */ + public static Permission createPermission(final Class permissionClass, final String name, final String actions) { + Assert.checkNotNullParam("permissionClass", permissionClass); + Constructor noArgs = null; + Constructor oneArg = null; + Constructor twoArg = null; + for (Constructor raw : permissionClass.getConstructors()) { + @SuppressWarnings("unchecked") + Constructor ctor = (Constructor) raw; + final Class[] parameterTypes = ctor.getParameterTypes(); + if (parameterTypes.length == 2 && parameterTypes[0] == String.class && parameterTypes[1] == String.class) { + twoArg = ctor; + } else if (parameterTypes.length == 1 && parameterTypes[0] == String.class) { + oneArg = ctor; + } else if (parameterTypes.length == 0) { + noArgs = ctor; + } + } + try { + if (twoArg != null) { + return twoArg.newInstance(name, actions); + } else if (oneArg != null) { + return oneArg.newInstance(name); + } else if (noArgs != null) { + return noArgs.newInstance(); + } else { + throw ElytronMessages.log.noPermissionConstructor(permissionClass.getName()); + } + } catch (IllegalAccessException e) { + throw new IllegalAccessError(e.getMessage()); + } catch (InstantiationException e) { + throw ElytronMessages.log.permissionInstantiation(permissionClass.getName(), e); + } catch (InvocationTargetException e) { + try { + throw e.getCause(); + } catch (Error | RuntimeException cause) { + throw cause; + } catch (Throwable cause) { + throw new UndeclaredThrowableException(cause); + } + } + } + + /** + * Get a read-only collection of the given permissions. + * + * @param permissions the permissions to assign + * @return the read-only collection + */ + public static PermissionCollection readOnlyCollectionOf(Permission... permissions) { + final int length = permissions.length; + if (length == 0) { + return EMPTY_PERMISSION_COLLECTION; + } else { + Permissions collection = new Permissions(); + addAll(collection, Arrays.asList(permissions)); + collection.setReadOnly(); + return collection; + } + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/permission/PermissionVerifier.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/permission/PermissionVerifier.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/permission/PermissionVerifier.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,179 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2016 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.permission; + +import java.security.Permission; +import java.security.PermissionCollection; +import java.security.Policy; +import java.security.ProtectionDomain; + +import org.wildfly.common.Assert; + +/** + * An interface for objects that can verify permissions. + * + * @author David M. Lloyd + */ +@FunctionalInterface +public interface PermissionVerifier { + /** + * Determine if the permission is verified by this object. + * + * @param permission the permission to verify (must not be {@code null}) + * @return {@code true} if the permission is implied by this verifier, {@code false} otherwise + */ + boolean implies(Permission permission); + + /** + * Return a new verifier which implies permissions which are implied both by this verifier and by the given verifier. + * + * @param other the other verifier (must not be {@code null}) + * @return the new permission verifier (not {@code null}) + */ + default PermissionVerifier and(PermissionVerifier other) { + Assert.checkNotNullParam("other", other); + return permission -> implies(permission) && other.implies(permission); + } + + /** + * Return a new verifier which implies permissions which are implied either by this verifier or by the given verifier. + * + * @param other the other verifier (must not be {@code null}) + * @return the new permission verifier (not {@code null}) + */ + default PermissionVerifier or(PermissionVerifier other) { + Assert.checkNotNullParam("other", other); + return permission -> implies(permission) || other.implies(permission); + } + + /** + * Return a new verifier which implies permissions which are implied by only one of this verifier or the given verifier. + * + * @param other the other verifier (must not be {@code null}) + * @return the new permission verifier (not {@code null}) + */ + default PermissionVerifier xor(PermissionVerifier other) { + Assert.checkNotNullParam("other", other); + return permission -> implies(permission) ^ other.implies(permission); + } + + /** + * Return a new verifier which implies the opposite of this verifier. + * + * @return the new permission verifier (not {@code null}) + */ + default PermissionVerifier not() { + return permission -> ! implies(permission); + } + + /** + * Return a new verifier which implies permissions which are implied by this verifier but not the given verifier. + * + * @param other the other verifier (must not be {@code null}) + * @return the new permission verifier (not {@code null}) + */ + default PermissionVerifier unless(PermissionVerifier other) { + Assert.checkNotNullParam("other", other); + return permission -> implies(permission) && ! other.implies(permission); + } + + /** + * Check a permission, throwing an exception if the permission is not implied. + * + * @param permission the permission to check (must not be {@code null}) + * @throws SecurityException if the permission is not implied + */ + default void checkPermission(Permission permission) throws SecurityException { + Assert.checkNotNullParam("permission", permission); + if (! implies(permission)) { + throw ElytronMessages.log.permissionCheckFailed(permission, this); + } + } + + /** + * Get a permission verifier for a single permission. + * + * @param permission the permission (must not be {@code null}) + * @return the verifier (not {@code null}) + */ + static PermissionVerifier from(Permission permission) { + Assert.checkNotNullParam("permission", permission); + return permission instanceof PermissionVerifier ? (PermissionVerifier) permission : permission::implies; + } + + /** + * Get a permission verifier for a permission collection. + * + * @param permissionCollection the permission collection (must not be {@code null}) + * @return the verifier (not {@code null}) + */ + static PermissionVerifier from(PermissionCollection permissionCollection) { + Assert.checkNotNullParam("permissionCollection", permissionCollection); + return permissionCollection instanceof PermissionVerifier ? (PermissionVerifier) permissionCollection : permissionCollection::implies; + } + + /** + * Get a permission verifier for a protection domain. + * + * @param protectionDomain the protection domain (must not be {@code null}) + * @return the verifier (not {@code null}) + */ + static PermissionVerifier from(ProtectionDomain protectionDomain) { + Assert.checkNotNullParam("protectionDomain", protectionDomain); + return protectionDomain instanceof PermissionVerifier ? (PermissionVerifier) protectionDomain : protectionDomain::implies; + } + + /** + * Get a permission verifier for a policy's view of a protection domain. + * + * @param policy the policy (must not be {@code null}) + * @param protectionDomain the protection domain (must not be {@code null}) + * @return the verifier (not {@code null}) + */ + static PermissionVerifier from(Policy policy, ProtectionDomain protectionDomain) { + Assert.checkNotNullParam("policy", policy); + Assert.checkNotNullParam("protectionDomain", protectionDomain); + return permission -> policy.implies(protectionDomain, permission); + } + + /** + * Convert this verifier a permission collection which implies everything this verifier implies. If this instance + * is already a {@code PermissionCollection} instance, then this instance may be cast and returned. Otherwise, + * this method may return a new, read-only collection, which cannot be iterated. + * + * @return the permission collection (not {@code null}) + */ + default PermissionCollection toPermissionCollection() { + if (this instanceof PermissionCollection) { + return (PermissionCollection) this; + } else { + return new PermissionVerifierPermissionCollection(this); + } + } + + /** + * A verifier which implies no permissions. + */ + PermissionVerifier NONE = permission -> false; + + /** + * A verifier which implies all permissions. + */ + PermissionVerifier ALL = permission -> true; +} Index: 3rdParty_sources/elytron/org/wildfly/security/permission/PermissionVerifierPermissionCollection.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/permission/PermissionVerifierPermissionCollection.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/permission/PermissionVerifierPermissionCollection.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,48 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2016 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.permission; + +import java.security.Permission; +import java.security.PermissionCollection; +import java.util.Enumeration; + +import org.wildfly.common.Assert; + +final class PermissionVerifierPermissionCollection extends PermissionCollection { + private static final long serialVersionUID = 5119756048547471645L; + + private final PermissionVerifier verifier; + + PermissionVerifierPermissionCollection(final PermissionVerifier verifier) { + this.verifier = verifier; + setReadOnly(); + } + + public void add(final Permission permission) { + throw ElytronMessages.log.readOnlyPermissionCollection(); + } + + public boolean implies(final Permission permission) { + return verifier.implies(permission); + } + + public Enumeration elements() { + throw Assert.unsupported(); + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/permission/SecurityMessages.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/permission/SecurityMessages.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/permission/SecurityMessages.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,38 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2016 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.permission; + +import org.jboss.logging.Logger; +import org.jboss.logging.annotations.Message; +import org.jboss.logging.annotations.MessageLogger; + +/** + * @author David M. Lloyd + */ +@MessageLogger(projectCode = "WFSM") +public interface SecurityMessages { + SecurityMessages permission = Logger.getMessageLogger(SecurityMessages.class, "org.wildfly.security.permission"); + + @Message(id = 4, value = "Unexpected character '%s' at offset %d of '%s'") + IllegalArgumentException unexpectedActionCharacter(char ch, int offset, String actionString); + + @Message(id = 5, value = "Invalid action '%s' at offset %d of '%s'") + IllegalArgumentException invalidAction(String action, int offset, String actionString); + +} Index: 3rdParty_sources/elytron/org/wildfly/security/permission/SecurityMessages_$logger.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/permission/SecurityMessages_$logger.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/permission/SecurityMessages_$logger.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,48 @@ +package org.wildfly.security.permission; + +import java.util.Locale; +import java.io.Serializable; +import javax.annotation.Generated; +import java.lang.String; +import org.jboss.logging.Logger; +import java.util.Arrays; +import java.lang.IllegalArgumentException; + +/** + * Warning this class consists of generated code. + */ +@Generated(value = "org.jboss.logging.processor.generator.model.MessageLoggerImplementor", date = "2024-01-15T21:44:14+0100") +public class SecurityMessages_$logger implements SecurityMessages, Serializable { + private static final long serialVersionUID = 1L; + private static final String FQCN = SecurityMessages_$logger.class.getName(); + public SecurityMessages_$logger(final Logger log) { + this.log = log; + } + protected final Logger log; + private static final Locale LOCALE = Locale.ROOT; + protected Locale getLoggingLocale() { + return LOCALE; + } + protected String unexpectedActionCharacter$str() { + return "WFSM000004: Unexpected character '%s' at offset %d of '%s'"; + } + @Override + public final IllegalArgumentException unexpectedActionCharacter(final char ch, final int offset, final String actionString) { + final IllegalArgumentException result = new IllegalArgumentException(String.format(getLoggingLocale(), unexpectedActionCharacter$str(), ch, offset, actionString)); + _copyStackTraceMinusOne(result); + return result; + } + private static void _copyStackTraceMinusOne(final Throwable e) { + final StackTraceElement[] st = e.getStackTrace(); + e.setStackTrace(Arrays.copyOfRange(st, 1, st.length)); + } + protected String invalidAction$str() { + return "WFSM000005: Invalid action '%s' at offset %d of '%s'"; + } + @Override + public final IllegalArgumentException invalidAction(final String action, final int offset, final String actionString) { + final IllegalArgumentException result = new IllegalArgumentException(String.format(getLoggingLocale(), invalidAction$str(), action, offset, actionString)); + _copyStackTraceMinusOne(result); + return result; + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/permission/SerializedPermission.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/permission/SerializedPermission.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/permission/SerializedPermission.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,40 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2016 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.permission; + +import java.io.Serializable; +import java.security.Permission; + +final class SerializedPermission implements Serializable { + private static final long serialVersionUID = 897239118282921196L; + + private final Class c; + private final String n; + private final String a; + + SerializedPermission(final Class permissionClass, final String name, final String action) { + this.c = permissionClass; + this.n = name; + this.a = action; + } + + Object readResolve() { + return PermissionUtil.createPermission(c, n, a); + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/permission/SerializedPermissionCollection.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/permission/SerializedPermissionCollection.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/permission/SerializedPermissionCollection.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,49 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2016 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.permission; + +import java.io.Serializable; +import java.security.Permission; +import java.security.PermissionCollection; +import java.util.ArrayList; + +final class SerializedPermissionCollection implements Serializable { + private static final long serialVersionUID = - 8745428905589938281L; + + private final Permission s; + private final Permission[] p; + private final boolean r; + + SerializedPermissionCollection(final AbstractPermissionCollection collection) { + s = collection.getSourcePermission(); + final ArrayList list = new ArrayList<>(collection.size()); + collection.forEach(list::add); + p = list.toArray(PermissionUtil.NO_PERMISSIONS); + r = collection.isReadOnly(); + } + + Object readResolve() { + final PermissionCollection collection = s.newPermissionCollection(); + for (Permission permission : p) { + collection.add(permission); + } + if (r) collection.setReadOnly(); + return collection; + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/permission/SimpleActionBitsPermissionCollection.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/permission/SimpleActionBitsPermissionCollection.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/permission/SimpleActionBitsPermissionCollection.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,126 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2016 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.permission; + +import java.security.Permission; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.concurrent.atomic.AtomicReference; + +import org.wildfly.security.util.ArrayIterator; + +/** + * A trivially simple permission collection, suitable as a default for most permission types (though probably not as efficient + * as a specialized type in many cases). + * + * @author David M. Lloyd + */ +public final class SimpleActionBitsPermissionCollection extends AbstractPermissionCollection { + + private static final AbstractActionSetPermission[] NO_PERMS = new AbstractActionSetPermission[0]; + + private final AtomicReference[]> permissionsRef = new AtomicReference<>(NO_PERMS); + + /** + * Construct a new instance. + * + * @param sourcePermission the source permission for this collection (must not be {@code null}) + */ + public SimpleActionBitsPermissionCollection(final AbstractActionSetPermission sourcePermission) { + super(sourcePermission); + } + + public int size() { + return permissionsRef.get().length; + } + + @Override + protected void doAdd(final AbstractPermission permission) { + if (permission instanceof AbstractActionSetPermission) { + doAdd((AbstractActionSetPermission) permission); + } + throw ElytronMessages.log.invalidPermissionType(AbstractActionSetPermission.class, permission); + } + + /** + * Adds a permission. + * + * @param permission the non-{@code null} permission + */ + protected void doAdd(final AbstractActionSetPermission permission) { + AbstractActionSetPermission[] oldVal, readVal, newVal; + int count; + final AtomicReference[]> permissionsRef = this.permissionsRef; + do { + readVal = permissionsRef.get(); + count = 0; + do { + oldVal = readVal; + AbstractActionSetPermission merged = permission; + retry: for (;;) { + for (AbstractActionSetPermission test : oldVal) { + if (test.implies(merged)) { + // fail fast + return; + } + if (test.nameEquals(merged)) { + // combine + merged = merged.withActionBits(permission.getActionBits()); + // test again with merged permission + count = 0; + continue retry; + } + if (! merged.implies(test)) { + // prepare to skip any permissions that are obviated by this one + count ++; + } + } + break; + } + // see if it's still what we expect before we commit to the possibly expensive update... + readVal = permissionsRef.get(); + } while (readVal != oldVal); + newVal = new AbstractActionSetPermission[count + 1]; + int i = 0; + for (AbstractActionSetPermission test : oldVal) { + if (! permission.implies(test)) { + newVal[i++] = test; + } + } + newVal[i] = permission; + } while (! permissionsRef.compareAndSet(oldVal, newVal)); + } + + public boolean implies(final Permission permission) { + for (Permission test : permissionsRef.get()) { + if (test.implies(permission)) { + return true; + } + } + return false; + } + + public Iterator iterator() { + return new ArrayIterator(permissionsRef.get()); + } + + public Enumeration elements() { + return new ArrayIterator(permissionsRef.get()); + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/permission/SimplePermissionCollection.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/permission/SimplePermissionCollection.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/permission/SimplePermissionCollection.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,101 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2016 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.permission; + +import java.security.Permission; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.concurrent.atomic.AtomicReference; + +import org.wildfly.security.util.ArrayIterator; + +/** + * A trivially simple permission collection, suitable as a default for most permission types (though probably not as efficient + * as a specialized type in many cases). + * + * @author David M. Lloyd + */ +public final class SimplePermissionCollection extends AbstractPermissionCollection { + + private static final long serialVersionUID = - 9157630531211570679L; + + private final AtomicReference permissionsRef = new AtomicReference<>(PermissionUtil.NO_PERMISSIONS); + + /** + * Construct a new instance. + * + * @param sourcePermission the source permission for this collection (must not be {@code null}) + */ + public SimplePermissionCollection(final AbstractPermission sourcePermission) { + super(sourcePermission); + } + + public int size() { + return permissionsRef.get().length; + } + + protected void doAdd(final AbstractPermission permission) { + Permission[] oldVal, readVal, newVal; + int count; + final AtomicReference permissionsRef = this.permissionsRef; + do { + readVal = permissionsRef.get(); + do { + count = 0; + oldVal = readVal; + for (Permission test : oldVal) { + if (test.implies(permission)) { + return; + } + if (! permission.implies(test)) { + // prepare to skip any permissions that are obviated by this one + count ++; + } + } + // see if it's still what we expect before we commit to the possibly expensive update... + readVal = permissionsRef.get(); + } while (readVal != oldVal); + newVal = new Permission[count + 1]; + int i = 0; + for (Permission test : oldVal) { + if (! permission.implies(test)) { + newVal[i++] = test; + } + } + newVal[i] = permission; + } while (! permissionsRef.compareAndSet(oldVal, newVal)); + } + + public boolean implies(final Permission permission) { + for (Permission test : permissionsRef.get()) { + if (test.implies(permission)) { + return true; + } + } + return false; + } + + public Iterator iterator() { + return new ArrayIterator(permissionsRef.get()); + } + + public Enumeration elements() { + return new ArrayIterator(permissionsRef.get()); + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/permission/UnionPermissionCollection.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/permission/UnionPermissionCollection.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/permission/UnionPermissionCollection.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,70 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2016 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.permission; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.security.Permission; +import java.security.PermissionCollection; +import java.util.Enumeration; + +final class UnionPermissionCollection extends PermissionCollection implements PermissionVerifier { + private static final long serialVersionUID = 6731525842957764833L; + + private final PermissionCollection pc1; + private final PermissionCollection pc2; + + UnionPermissionCollection(final PermissionCollection pc1, final PermissionCollection pc2) { + this.pc1 = pc1; + this.pc2 = pc2; + setReadOnly(); + } + + public void add(final Permission permission) { + throw ElytronMessages.log.readOnlyPermissionCollection(); + } + + public boolean implies(final Permission permission) { + return pc1.implies(permission) || pc2.implies(permission); + } + + public Enumeration elements() { + final Enumeration e1 = pc1.elements(); + final Enumeration e2 = pc2.elements(); + return new Enumeration() { + public boolean hasMoreElements() { + return e1.hasMoreElements() || e2.hasMoreElements(); + } + + public Permission nextElement() { + return e1.hasMoreElements() ? e1.nextElement() : e2.nextElement(); + } + }; + } + + private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { + ois.defaultReadObject(); + if (pc1 == null) { + throw ElytronMessages.log.invalidObjectNull("pc1"); + } + if (pc2 == null) { + throw ElytronMessages.log.invalidObjectNull("pc2"); + } + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/ssl/SSLConnection.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/ssl/SSLConnection.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/ssl/SSLConnection.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,179 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2017 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.ssl; + +import static org.wildfly.security.ssl.TLSServerEndPointChannelBinding.*; + +import java.security.NoSuchAlgorithmException; +import java.security.cert.Certificate; +import java.security.cert.CertificateEncodingException; +import java.security.cert.X509Certificate; + +import javax.net.ssl.SSLEngine; +import javax.net.ssl.SSLPeerUnverifiedException; +import javax.net.ssl.SSLSession; +import javax.net.ssl.SSLSocket; + +import org.wildfly.common.Assert; +import org.wildfly.security.auth.callback.ChannelBindingCallback; + +/** + * An SSL connection of some sort. + * + * @author David M. Lloyd + */ +public abstract class SSLConnection { + + SSLConnection() { + } + + /** + * Get the SSL session associated with this connection. + * + * @return the SSL session associated with this connection, or {@code null} if there is none + */ + public abstract SSLSession getSession(); + + /** + * Get the client-mode flag for this connection. + * + * @return the client-mode flag for this connection + */ + public abstract boolean isClientMode(); + + /** + * Get the channel binding of the given type from this connection. If the data is not present or the type is not + * supported, {@code null} is returned. + * + * @return the channel binding of the given type from this connection, or {@code null} if it is not supported + */ + public byte[] getChannelBinding(String bindingType) { + // in JDK 10 and later (presumably), this method will be made abstract and the concrete impls will delegate directly to JSSE + final boolean clientMode = isClientMode(); + switch (bindingType) { + case TLS_SERVER_ENDPOINT: { + final X509Certificate serverCert; + final SSLSession session = getSession(); + if (session == null) { + return null; + } + if (clientMode) { + Certificate[] peerCertificates; + try { + peerCertificates = session.getPeerCertificates(); + } catch (SSLPeerUnverifiedException e) { + peerCertificates = null; + } + serverCert = peerCertificates != null && peerCertificates.length > 0 ? (X509Certificate) peerCertificates[0] : null; + } else { + final Certificate[] localCertificates = session.getLocalCertificates(); + serverCert = localCertificates != null && localCertificates.length > 0 ? (X509Certificate) localCertificates[0] : null; + } + try { + return getChannelBindingData(serverCert); + } catch (NoSuchAlgorithmException | CertificateEncodingException e) { + return null; + } + } + default: { + return null; + } + } + } + + /** + * Populate the given channel binding callback with any channel binding data that might be present on this + * connection. If no channel binding seems to be supported, then the callback will be left unpopulated. + * + * @param callback the binding callback to populate (must not be {@code null}) + */ + public void handleChannelBindingCallback(final ChannelBindingCallback callback) { + Assert.checkNotNullParam("callback", callback); + byte[] bindingData = getChannelBinding("tls-unique"); + if (bindingData != null) { + callback.setBindingType("tls-unique"); + callback.setBindingData(bindingData); + } else { + bindingData = getChannelBinding(TLS_SERVER_ENDPOINT); + if (bindingData != null) { + callback.setBindingType(TLS_SERVER_ENDPOINT); + callback.setBindingData(bindingData); + } + } + } + + /** + * Create a {@code SSLConnection} for the given SSL engine. + * + * @param engine the SSL engine (must not be {@code null}) + * @return the SSL connection (not {@code null}) + */ + public static SSLConnection forEngine(SSLEngine engine) { + Assert.checkNotNullParam("engine", engine); + return new SSLConnection() { + public SSLSession getSession() { + return engine.getSession(); + } + + public boolean isClientMode() { + return engine.getUseClientMode(); + } + }; + } + + /** + * Create a {@code SSLConnection} for the given SSL socket. + * + * @param socket the SSL socket (must not be {@code null}) + * @return the SSL connection (not {@code null}) + */ + public static SSLConnection forSocket(SSLSocket socket) { + Assert.checkNotNullParam("socket", socket); + return new SSLConnection() { + public SSLSession getSession() { + return socket.getSession(); + } + + public boolean isClientMode() { + return socket.getUseClientMode(); + } + }; + } + + /** + * Create a {@code SSLConnection} for the given SSL socket. Since no connection information will be + * available in this case, not all channel binding modes will be supported. + * + * @param session the SSL session (must not be {@code null}) + * @param clientMode {@code true} if the session is client-side, {@code false} if it is server-side + * @return the SSL connection (not {@code null}) + */ + public static SSLConnection forSession(SSLSession session, boolean clientMode) { + Assert.checkNotNullParam("session", session); + return new SSLConnection() { + public SSLSession getSession() { + return session; + } + + public boolean isClientMode() { + return clientMode; + } + }; + } +} Index: 3rdParty_sources/elytron/org/wildfly/security/ssl/TLSServerEndPointChannelBinding.java =================================================================== diff -u --- 3rdParty_sources/elytron/org/wildfly/security/ssl/TLSServerEndPointChannelBinding.java (revision 0) +++ 3rdParty_sources/elytron/org/wildfly/security/ssl/TLSServerEndPointChannelBinding.java (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -0,0 +1,110 @@ +/* + * JBoss, Home of Professional Open Source. + * Copyright 2017 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.ssl; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.security.cert.CertificateEncodingException; +import java.security.cert.X509Certificate; + +import javax.security.auth.callback.UnsupportedCallbackException; + +import org.wildfly.security.asn1.ASN1; +import org.wildfly.security.auth.callback.CallbackUtil; +import org.wildfly.security.auth.callback.ChannelBindingCallback; + +/** + * Utilities for handling the "tls-server-end-point" channel binding strategy used by various types + * of authentication mechanisms. + * + * @author David M. Lloyd + */ +public final class TLSServerEndPointChannelBinding { + public static final String TLS_SERVER_ENDPOINT = "tls-server-end-point"; + + private TLSServerEndPointChannelBinding() {} + + /** + * Get the digest algorithm that would be used for a given signature algorithm OID. + * + * @param sigAlgOID the signature algorithm OID (must not be {@code null}) + * @return the digest algorithm, or {@code null} if the OID is not recognized + */ + public static String getDigestAlgorithm(final String sigAlgOID) { + switch (sigAlgOID) { + case ASN1.OID_MD2: + case ASN1.OID_MD2_WITH_RSA: + case ASN1.OID_MD4_WITH_RSA: + case ASN1.OID_MD5: + case ASN1.OID_MD5_WITH_RSA: + case ASN1.OID_SHA1_WITH_DSA: + case ASN1.OID_SHA1_WITH_RSA: + case ASN1.OID_SHA1_WITH_ECDSA: + case ASN1.OID_SHA1: + case ASN1.OID_SHA224_WITH_ECDSA: + case ASN1.OID_SHA256_WITH_RSA: + case ASN1.OID_SHA256_WITH_ECDSA: + return "SHA-256"; + case ASN1.OID_SHA384_WITH_ECDSA: + case ASN1.OID_SHA384_WITH_RSA: + return "SHA-384"; + case ASN1.OID_SHA512_WITH_ECDSA: + case ASN1.OID_SHA512_WITH_RSA: + return "SHA-512"; + default: { + return null; + } + } + } + + /** + * Convenience method to handle a channel binding callback. + * + * @param channelBindingCallback the callback (must not be {@code null}) + * @param serverCerts the server certificate chain + * @throws UnsupportedCallbackException if the server certificates are not present or unsupported and the callback is not optional + */ + public static void handleChannelBindingCallback(ChannelBindingCallback channelBindingCallback, X509Certificate[] serverCerts) throws UnsupportedCallbackException { + if (serverCerts != null && serverCerts.length > 0) { + // tls-server-end-point + try { + final byte[] bindingData = getChannelBindingData(serverCerts[0]); + if (bindingData != null) { + channelBindingCallback.setBindingData(bindingData); + channelBindingCallback.setBindingType(TLS_SERVER_ENDPOINT); + return; + } + } catch (CertificateEncodingException | NoSuchAlgorithmException e) { + // fail silently + } + } + CallbackUtil.unsupported(channelBindingCallback); + } + + static byte[] getChannelBindingData(X509Certificate serverCert) throws NoSuchAlgorithmException, CertificateEncodingException { + if (serverCert == null) { + return null; + } + final String digestAlgorithm = TLSServerEndPointChannelBinding.getDigestAlgorithm(serverCert.getSigAlgOID()); + if (digestAlgorithm == null) { + return null; + } + return MessageDigest.getInstance(digestAlgorithm).digest(serverCert.getEncoded()); + } +} Index: lams_central/.classpath =================================================================== diff -u -r5661c79b661c18221c0eb5e32fe7ec54110e5a65 -r5e9e3c6915701ed7a340d47ff6d32c12ccb9e800 --- lams_central/.classpath (.../.classpath) (revision 5661c79b661c18221c0eb5e32fe7ec54110e5a65) +++ lams_central/.classpath (.../.classpath) (revision 5e9e3c6915701ed7a340d47ff6d32c12ccb9e800) @@ -38,9 +38,9 @@ - - - - + + + + \ No newline at end of file