org.wildfly.security.auth.server.IdentityCredentials Maven / Gradle / Ivy
Go to download
This artifact provides a single jar that contains all classes required to use remote EJB and JMS, including
all dependencies. It is intended for use by those not using maven, maven users should just import the EJB and
JMS BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up
with different versions on classes on the class path).
The newest version!
/*
* 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 extends Credential> credentialType) {
return contains(credentialType, null);
}
@Override
public final SupportLevel getCredentialAcquireSupport(final Class extends Credential> credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec) {
return contains(credentialType, algorithmName, parameterSpec) ? SupportLevel.SUPPORTED : SupportLevel.UNSUPPORTED;
}
@Override
public final SupportLevel getCredentialAcquireSupport(final Class extends Credential> credentialType, final String algorithmName) {
return contains(credentialType, algorithmName) ? SupportLevel.SUPPORTED : SupportLevel.UNSUPPORTED;
}
@Override
public final SupportLevel getCredentialAcquireSupport(final Class extends Credential> 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 extends Credential> 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 extends Credential> 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 extends Credential> 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 extends Credential> 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 extends Credential> 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 super Credential> 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 super C> 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 extends Evidence> 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 extends Credential> 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 extends Credential> credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec) {
Assert.checkNotNullParam("credentialType", credentialType);
return this;
}
public IdentityCredentials without(final Predicate super Credential> predicate) {
Assert.checkNotNullParam("predicate", predicate);
return this;
}
public Spliterator spliterator() {
return Spliterators.emptySpliterator();
}
public void forEach(final Consumer super Credential> 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 extends Credential> 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 extends Credential> credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec) {
Assert.checkNotNullParam("credentialType", credentialType);
return credential.matches(credentialType, algorithmName, parameterSpec) ? NONE : this;
}
public IdentityCredentials without(final Predicate super Credential> predicate) {
Assert.checkNotNullParam("predicate", predicate);
return predicate.test(credential) ? NONE : this;
}
public void forEach(final Consumer super Credential> 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 extends Credential> 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 extends Credential> credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec) {
Assert.checkNotNullParam("credentialType", credentialType);
return credential1.matches(credentialType, algorithmName, parameterSpec) ? NONE : this;
}
public IdentityCredentials without(final Predicate super Credential> 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 super Credential> 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 extends Credential> 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 super Credential> 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 super Credential> 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 extends Credential> clazz;
private final String algorithm;
private final AlgorithmParameterSpec parameterSpec;
private final int hashCode;
Key(final Class extends Credential> 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 extends Credential> getClazz() {
return clazz;
}
String getAlgorithm() {
return algorithm;
}
AlgorithmParameterSpec getParameterSpec() {
return parameterSpec;
}
}
}