net.officefloor.plugin.web.http.security.store.JndiLdapCredentialStore.java Source code

Java tutorial

Introduction

Here is the source code for net.officefloor.plugin.web.http.security.store.JndiLdapCredentialStore.java

Source

/*
 * OfficeFloor - http://www.officefloor.net
 * Copyright (C) 2005-2013 Daniel Sagenschneider
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */
package net.officefloor.plugin.web.http.security.store;

import java.io.IOException;
import java.nio.charset.Charset;
import java.util.HashSet;
import java.util.Set;

import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.Attributes;
import javax.naming.directory.DirContext;
import javax.naming.directory.SearchResult;

import net.officefloor.plugin.socket.server.http.parse.impl.HttpRequestParserImpl;

import org.apache.commons.codec.binary.Base64;

/**
 * {@link CredentialStore} for {@link DirContext}.
 * 
 * @author Daniel Sagenschneider
 */
public class JndiLdapCredentialStore implements CredentialStore {

    /**
     * {@link Charset} for credentials.
     */
    private static final Charset US_ASCII = HttpRequestParserImpl.US_ASCII;

    /**
     * Algorithm.
     */
    private final String algorithm;

    /**
     * Algorithm prefix of credentials to use.
     */
    private final String credentialPrefix;

    /**
     * {@link DirContext}.
     */
    private final DirContext context;

    /**
     * Base dn for searching for entries.
     */
    private final String entrySearchBaseDn;

    /**
     * Base dn for searching for roles.
     */
    private final String rolesSearchBaseDn;

    /**
     * Initiate.
     * 
     * @param algorithm
     *            Algorithm.
     * @param context
     *            {@link DirContext}.
     * @param entrySearchBaseDn
     *            Base dn for searching for entries.
     * @param rolesSearchBaseDn
     *            Base dn for searching for roles.
     */
    public JndiLdapCredentialStore(String algorithm, DirContext context, String entrySearchBaseDn,
            String rolesSearchBaseDn) {
        this.algorithm = (algorithm == null ? null : (algorithm.trim().length() == 0 ? null : algorithm.trim()));
        this.context = context;
        this.entrySearchBaseDn = entrySearchBaseDn;
        this.rolesSearchBaseDn = rolesSearchBaseDn;

        // Create the prefix
        if (this.algorithm == null) {
            this.credentialPrefix = "";
        } else {
            this.credentialPrefix = "{" + this.algorithm.toUpperCase() + "}";
        }
    }

    /*
     * ================== CredentialStore ==========================
     */

    @Override
    public String getAlgorithm() {
        return this.algorithm;
    }

    @Override
    public CredentialEntry retrieveCredentialEntry(String userId, String realm) throws IOException {
        try {
            // Search for the credential entry
            NamingEnumeration<SearchResult> searchResults = this.context.search(this.entrySearchBaseDn,
                    "(&(objectClass=inetOrgPerson)(uid=" + userId + "))", null);
            if (!searchResults.hasMore()) {
                return null; // entry not found
            }
            SearchResult result = searchResults.next();

            // Obtain the attributes
            String entryDn = result.getNameInNamespace();

            // Create and return the credential entry
            return new JndiLdapCredentialEntry(entryDn);

        } catch (NamingException ex) {
            throw new IOException(ex);
        }
    }

    /**
     * JNDI LDAP {@link CredentialEntry}.
     */
    private class JndiLdapCredentialEntry implements CredentialEntry {

        /**
         * Dn for this {@link CredentialEntry}.
         */
        private final String entryDn;

        /**
         * Initiate.
         * 
         * @param entryDn
         *            Dn for this {@link CredentialEntry}.
         */
        public JndiLdapCredentialEntry(String entryDn) {
            this.entryDn = entryDn;
        }

        /*
         * =================== CredentialEntry ========================
         */

        @Override
        public byte[] retrieveCredentials() throws IOException {
            try {
                // Obtain the entry's userPassword attribute
                Attributes entry = JndiLdapCredentialStore.this.context.getAttributes(this.entryDn);
                Attribute userPasswordAttribute = entry.get("userPassword");

                // Iterate over 'userPassword' values to match algorithm
                NamingEnumeration<?> userPasswords = userPasswordAttribute.getAll();
                for (; userPasswords.hasMore();) {

                    // Obtain the userPassword value
                    byte[] userPasswordBytes = (byte[]) userPasswords.next();
                    String userPasswordText = new String(userPasswordBytes, US_ASCII);

                    // Determine if credential for algorithm
                    if (userPasswordText.toUpperCase().startsWith(JndiLdapCredentialStore.this.credentialPrefix)) {

                        // Found credentials, so strip out prefix
                        userPasswordText = userPasswordText
                                .substring(JndiLdapCredentialStore.this.credentialPrefix.length());

                        // Decode credentials
                        byte[] credentials;
                        if (JndiLdapCredentialStore.this.algorithm == null) {
                            // Plain text password
                            credentials = userPasswordText.getBytes(US_ASCII);
                        } else {
                            // Decode credentials (assume always Base64)
                            credentials = Base64.decodeBase64(userPasswordText);
                        }

                        // Return the credentials
                        return credentials;
                    }
                }

                // If here, no credentials
                throw new IOException("No authentication credentials for " + this.entryDn);

            } catch (NamingException ex) {
                throw new IOException(ex);
            }
        }

        @Override
        public Set<String> retrieveRoles() throws IOException {
            try {

                // Search for the groups
                NamingEnumeration<SearchResult> groupResults = JndiLdapCredentialStore.this.context.search(
                        JndiLdapCredentialStore.this.rolesSearchBaseDn,
                        "(&(objectClass=groupOfNames)(member=" + this.entryDn + "))", null);

                // Obtain the set of roles
                Set<String> roles = new HashSet<String>();
                for (; groupResults.hasMore();) {
                    SearchResult group = groupResults.next();

                    // Obtain the role from the group
                    String role = (String) group.getAttributes().get("ou").get();

                    // Add role to listing
                    roles.add(role);
                }

                // Return the roles
                return roles;

            } catch (NamingException ex) {
                throw new IOException(ex);
            }
        }
    }

}