All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.wildfly.security.auth.realm.ldap.OtpCredentialLoader Maven / Gradle / Ivy

Go to download

This artifact provides a single jar that contains all classes required to use remote Jakarta Enterprise Beans and Jakarta Messaging, including all dependencies. It is intended for use by those not using maven, maven users should just import the Jakarta Enterprise Beans and Jakarta Messaging BOM's instead (shaded JAR's cause lots of problems with maven, as it is very easy to inadvertently end up with different versions on classes on the class path).

There is a newer version: 35.0.0.Final
Show newest version
package org.wildfly.security.auth.realm.ldap;

import org.wildfly.common.Assert;
import org.wildfly.common.codec.Base64Alphabet;
import org.wildfly.common.iteration.ByteIterator;
import org.wildfly.common.iteration.CodePointIterator;
import org.wildfly.security.auth.server.RealmUnavailableException;
import org.wildfly.security.auth.SupportLevel;
import org.wildfly.security.credential.Credential;
import org.wildfly.security.credential.PasswordCredential;
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.OneTimePasswordSpec;

import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.BasicAttribute;
import javax.naming.directory.BasicAttributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.NoSuchAttributeException;

import java.security.NoSuchAlgorithmException;
import java.security.Provider;
import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.InvalidKeySpecException;
import java.util.Collection;
import java.util.function.Supplier;

import static org.wildfly.security.auth.realm.ldap.ElytronMessages.log;

/**
 * A {@link CredentialLoader} for loading OTP credentials stored within defined attributes of LDAP entries.
 *
 * @author Jan Kalina
 */
class OtpCredentialLoader implements CredentialPersister {

    private final String algorithmAttributeName;
    private final String hashAttributeName;
    private final String seedAttributeName;
    private final String sequenceAttributeName;

    OtpCredentialLoader(String algorithmAttributeName, String hashAttributeName, String seedAttributeName, String sequenceAttributeName) {
        Assert.checkNotNullParam("algorithmAttributeName", algorithmAttributeName);
        Assert.checkNotNullParam("hashAttributeName", hashAttributeName);
        Assert.checkNotNullParam("seedAttributeName", seedAttributeName);
        Assert.checkNotNullParam("sequenceAttributeName", sequenceAttributeName);
        this.algorithmAttributeName = algorithmAttributeName;
        this.hashAttributeName = hashAttributeName;
        this.seedAttributeName = seedAttributeName;
        this.sequenceAttributeName = sequenceAttributeName;
    }

    @Override
    public SupportLevel getCredentialAcquireSupport(final Class credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec) {
        if (credentialType == PasswordCredential.class) {
            if (algorithmName == null) {
                return SupportLevel.SUPPORTED;
            }
            switch (algorithmName) {
                case OneTimePassword.ALGORITHM_OTP_MD5: return SupportLevel.POSSIBLY_SUPPORTED;
                case OneTimePassword.ALGORITHM_OTP_SHA1: return SupportLevel.POSSIBLY_SUPPORTED;
                case OneTimePassword.ALGORITHM_OTP_SHA_256: return SupportLevel.POSSIBLY_SUPPORTED;
                case OneTimePassword.ALGORITHM_OTP_SHA_384: return SupportLevel.POSSIBLY_SUPPORTED;
                case OneTimePassword.ALGORITHM_OTP_SHA_512: return SupportLevel.POSSIBLY_SUPPORTED;
                default: return SupportLevel.UNSUPPORTED;
            }
        }
        return SupportLevel.UNSUPPORTED;
    }

    @Override
    public ForIdentityLoader forIdentity(DirContext context, String distinguishedName, Attributes attributes) {
        return new ForIdentityLoader(context, distinguishedName, attributes);
    }

    @Override
    public void addRequiredIdentityAttributes(Collection attributes) {
        attributes.add(algorithmAttributeName);
        attributes.add(hashAttributeName);
        attributes.add(seedAttributeName);
        attributes.add(sequenceAttributeName);
    }

    private class ForIdentityLoader implements IdentityCredentialPersister {

        private final DirContext context;
        private final String distinguishedName;
        private final Attributes attributes;

        public ForIdentityLoader(DirContext context, String distinguishedName, Attributes attributes) {
            this.context = context;
            this.distinguishedName = distinguishedName;
            this.attributes = attributes;
        }

        @Override
        public SupportLevel getCredentialAcquireSupport(final Class credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec, final Supplier providers) {
            if (credentialType != PasswordCredential.class) {
                return SupportLevel.UNSUPPORTED;
            }
            Attribute algorithmAttribute = attributes.get(algorithmAttributeName);
            Attribute hashAttribute = attributes.get(hashAttributeName);
            Attribute seedAttribute = attributes.get(seedAttributeName);
            Attribute sequenceAttribute = attributes.get(sequenceAttributeName);

            if (algorithmAttribute != null && hashAttribute != null && seedAttribute != null && sequenceAttribute != null && (algorithmName == null || algorithmAttribute.contains(algorithmName))) {
                return SupportLevel.SUPPORTED;
            }
            return SupportLevel.UNSUPPORTED;
        }

        @Override
        public  C getCredential(final Class credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec, Supplier providers) {
            if (credentialType != PasswordCredential.class) {
                return null;
            }
            try {
                Attribute algorithmAttribute = attributes.get(algorithmAttributeName);
                Attribute hashAttribute = attributes.get(hashAttributeName);
                Attribute seedAttribute = attributes.get(seedAttributeName);
                Attribute sequenceAttribute = attributes.get(sequenceAttributeName);

                if (algorithmAttribute == null || algorithmName != null && ! algorithmAttribute.contains(algorithmName) || hashAttribute == null || seedAttribute == null || sequenceAttribute == null) {
                    return null;
                }

                Object algorithm = algorithmAttribute.get();
                Object hash = hashAttribute.get();
                Object seed = seedAttribute.get();
                Object sequence = sequenceAttribute.get();

                if (algorithm == null || hash == null || seed == null || sequence == null || ! (algorithm instanceof String) || ! (hash instanceof String) || ! (seed instanceof String) || ! (sequence instanceof String)) {
                    return null;
                }

                PasswordFactory passwordFactory = PasswordFactory.getInstance((String) algorithm, providers);
                Password password = passwordFactory.generatePassword(new OneTimePasswordSpec(
                                CodePointIterator.ofString((String) hash)
                                        .base64Decode(Base64Alphabet.STANDARD, false).drain(),
                                (String) seed,
                                Integer.parseInt((String) sequence)));
                if (credentialType.isAssignableFrom(PasswordCredential.class)) {
                    return credentialType.cast(new PasswordCredential(password));
                }

            } catch (NamingException | InvalidKeySpecException | NoSuchAlgorithmException e) {
                if (log.isTraceEnabled()) log.trace("Getting OTP credential of type "
                        + credentialType.getName() + " failed. dn=" + distinguishedName, e);
            }
            return null;
        }

        @Override
        public boolean getCredentialPersistSupport(final Class credentialType, final String algorithmName, final AlgorithmParameterSpec parameterSpec) {
            return OtpCredentialLoader.this.getCredentialAcquireSupport(credentialType, algorithmName, parameterSpec).mayBeSupported();
        }

        @Override
        public void persistCredential(final Credential credential) throws RealmUnavailableException {
            OneTimePassword password = credential.castAndApply(PasswordCredential.class, c -> c.getPassword(OneTimePassword.class));
            try {
                Attributes attributes = new BasicAttributes();
                attributes.put(algorithmAttributeName, password.getAlgorithm());
                attributes.put(hashAttributeName, ByteIterator.ofBytes(password.getHash()).base64Encode().drainToString());
                attributes.put(seedAttributeName, password.getSeed());
                attributes.put(sequenceAttributeName, Integer.toString(password.getSequenceNumber()));

                context.modifyAttributes(distinguishedName, DirContext.REPLACE_ATTRIBUTE, attributes);
            } catch (NamingException e) {
                throw log.ldapRealmCredentialPersistingFailed(credential.toString(), distinguishedName, e);
            }
        }

        @Override public void clearCredentials() throws RealmUnavailableException {
            try {
                Attributes attributes = new BasicAttributes();
                attributes.put(new BasicAttribute(algorithmAttributeName));
                attributes.put(new BasicAttribute(hashAttributeName));
                attributes.put(new BasicAttribute(seedAttributeName));
                attributes.put(new BasicAttribute(sequenceAttributeName));

                context.modifyAttributes(distinguishedName, DirContext.REMOVE_ATTRIBUTE, attributes);
            } catch (NoSuchAttributeException e) {
                // ignore if already clear
            } catch (NamingException e) {
                throw log.ldapRealmCredentialClearingFailed(distinguishedName, e);
            }
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy