Package org.cfcc.services

Source Code of org.cfcc.services.Ldap

/*
* Copyright 2007, 2008, 2009 Jakim Friant
*
* This file is part of ResetPassword.
*
* ResetPassword 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.
*
* ResetPassword 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 ResetPassword.  If not, see <http://www.gnu.org/licenses/>.
*/
package org.cfcc.services;

import org.cfcc.PasswordConstraintException;
import java.io.UnsupportedEncodingException;
import java.util.Hashtable;
import java.util.ArrayList;
import java.util.ListIterator;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.naming.*;
import javax.naming.directory.*;
import org.acegisecurity.providers.ldap.LdapShaPasswordEncoder;
import org.cfcc.CryptoException;
import org.cfcc.NoPersonIdException;
import org.cfcc.LdapChangeException;
import org.cfcc.SystemUnavailableException;
import org.cfcc.utils.BlindSSLSocketFactory;
import org.cfcc.utils.Crypto;
import org.cfcc.utils.PwGen;

/**
* Class for connecting to the LDAP server and performing common operations.
*
* @author Jakim Friant
*/
public class Ldap {

    /** the URL to the ldap database, for example: ldap://ldap.example.com/ */
    String url = null;
    /** set to true to use TLS/SSL (independent of a port specified in the
     * LDAP URL string) */
    boolean use_ssl = false;
    /** the bind DN with write access to the LDAP database */
    String bind_dn = null;
    /** holds the password for the bind DN */
    String bind_pw = null;
    /** the search base for the directory, for example: "dc=example,dc=com" */
    String search_base = null;
    /** the field to use for username searches (usally uid or cn), defaults to "uid" */
    String username_field = "uid";
    /** the last DN that was used in a search or modify operation */
    String last_dn = null;
    /** Email Domain for converting to a Posix Account (do not include an '@' symbol)*/
    public static final String EMAIL_DOMAIN = "mail.cfcc.edu";
    /** An option to bypass the SSL certificate validation (use only if absolutely necessary) */
    private boolean ssl_validate_cert = true;
    /** This encoding is used to convert the password bytes to a string */
    private static final String LDAP_ENCODING = "US-ASCII";
  private static final String NORMAL_ACCOUNT = "512";

    /**
     * Class constructor with no defaults, the calling code will specify
     * everything.
     *
     * @param arg_url the ldap URL
     * @param arg_use_ssl set to true to use SSL
     * @param arg_bind_dn the DN used to bind to LDAP
     * @param arg_bind_pw password to connect to the LDAP service
     * @param arg_search_base the base DN of the tree, for example:
     * "dc=example,dc=com"
     * @param arg_username_field the LDAP field that holds the username
     */
    public Ldap(String arg_url,
    boolean arg_use_ssl,
            String arg_bind_dn,
            String arg_bind_pw,
            String arg_search_base,
            String arg_username_field) {
        url = arg_url;
        use_ssl = arg_use_ssl;
        bind_dn = arg_bind_dn;
        bind_pw = arg_bind_pw;
        search_base = arg_search_base;
        username_field = arg_username_field;
    }

    /**
     * Class constructor that uses the OpenLDAP default "uid" for the username.
     *
     * @param arg_url the ldap URL
     * @param arg_use_ssl set to true to use SSL
     * @param arg_bind_dn the DN used to bind to LDAP
     * @param arg_bind_pw password to connect to the LDAP service
     * @param arg_search_base the base DN of the tree, for example:
     * "dc=example,dc=com"
     */
    public Ldap(String arg_url,
            boolean arg_use_ssl,
            String arg_bind_dn,
            String arg_bind_pw,
            String arg_search_base) {
        url = arg_url;
        use_ssl = arg_use_ssl;
        bind_dn = arg_bind_dn;
        bind_pw = arg_bind_pw;
        search_base = arg_search_base;
    }

    /**
     * Class constructor with no defaults that uses a string flag to specify
     * SSL instead of a boolean parameter.  This is here to make it conveient
     * to set the parameter in a servlet.
     *
     * @param arg_url the ldap URL
     * @param arg_ssl set string to "true" to use SSL
     * @param arg_bind_dn the DN used to bind to LDAP
     * @param arg_bind_pw password to connect to the LDAP service
     * @param arg_search_base the base DN of the tree
     * @param arg_username_field the field used in the filter for the username (ex: uid)
     */
    public Ldap(String arg_url,
            String arg_ssl,
            String arg_bind_dn,
            String arg_bind_pw,
            String arg_search_base,
            String arg_username_field) {
        url = arg_url;
        use_ssl = arg_ssl.equals("true");
        bind_dn = arg_bind_dn;
        bind_pw = arg_bind_pw;
        search_base = arg_search_base;
        username_field = arg_username_field;
    }

    /**
     * Class constructor that has "uid" as the default username field and that
     * uses a string flag to specify SSL instead of a boolean parameter.  This
     * is here to make it conveient to set the parameter in a servlet.
     *
     * @param arg_url the ldap URL
     * @param arg_ssl set string to "true" to use SSL
     * @param arg_bind_dn the DN used to bind to LDAP
     * @param arg_bind_pw password to connect to the LDAP service
     * @param arg_search_base the base DN of the tree
     */
    public Ldap(String arg_url,
            String arg_ssl,
            String arg_bind_dn,
            String arg_bind_pw,
            String arg_search_base) {
        url = arg_url;
        use_ssl = arg_ssl.equals("true");
        bind_dn = arg_bind_dn;
        bind_pw = arg_bind_pw;
        search_base = arg_search_base;
    }

    /**
     * Class constructor that lets the caller request an unverified SSL
     * connection.  Has no defaults and also uses a string flag to specify
     * SSL instead of a boolean parameter.  This is here to make it conveient
     * to set the parameter from the servlet config file.
     *
     * @param arg_url the ldap URL
     * @param arg_ssl set string to "true" to use SSL
     * @param arg_ssl_validate_cert set to "false" to bypass certificate validation
     * @param arg_bind_dn the DN used to bind to LDAP
     * @param arg_bind_pw password to connect to the LDAP service
     * @param arg_search_base the base DN of the tree
     * @param arg_username_field the field used in the filter for the username (ex: uid)
     */
    public Ldap(String arg_url,
            String arg_ssl,
            String arg_ssl_validate_cert,
            String arg_bind_dn,
            String arg_bind_pw,
            String arg_search_base,
            String arg_username_field) {
        url = arg_url;
        use_ssl = arg_ssl.equals("true");
        ssl_validate_cert = arg_ssl_validate_cert.equals("true");
        bind_dn = arg_bind_dn;
        bind_pw = arg_bind_pw;
        search_base = arg_search_base;
        username_field = arg_username_field;
    }

    /**
     * Set up the LDAP login context and return a DirContext object.  Uses the
     * connection information specified when the object was created.
     *
     * @return a DirContext instance that is connected to a LDAP
     * service/database.
     * @throws NamingException If the connection cannot be made
     */
    public DirContext getContext() throws NamingException {
        Hashtable<String, String> env = new Hashtable<String, String>(11);
        env.put(Context.INITIAL_CONTEXT_FACTORY, "com.sun.jndi.ldap.LdapCtxFactory");
        env.put(Context.PROVIDER_URL, url);
        if (use_ssl == true) {
            env.put(Context.SECURITY_PROTOCOL, "ssl");
            if (ssl_validate_cert == false) {
                env.put("java.naming.ldap.factory.socket", BlindSSLSocketFactory.class.getName());
            }
        }
        env.put(Context.SECURITY_AUTHENTICATION, "simple");
        env.put(Context.SECURITY_PRINCIPAL, bind_dn);
        if (bind_pw.startsWith(Crypto.prefix())) {
            Crypto c = new Crypto();
            try {
                bind_pw = c.decrypt(bind_pw);
            } catch (CryptoException ex) {
                throw new NamingException(ex.toString());
            }
        }
        env.put(Context.SECURITY_CREDENTIALS, bind_pw);

        DirContext ctx = new InitialDirContext(env);
        return ctx;
    }

    /**
     * Runs a search against the LDAP tree and returns the results.
     *
     * @param filter string with any valid LDAP search filter
     * @return a NamingEnumeration with the records found in the search
     * @throws NamingException if an error is encountered when connecting or
     * searching
     */
    public NamingEnumeration<?> search(String filter) throws NamingException {
        NamingEnumeration<?> answer = null;
        DirContext ctx = null;
        // set up search criteria
        SearchControls ctls = new SearchControls();
        ctls.setReturningObjFlag(true);
        ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);

        ctx = getContext();
        answer = ctx.search(search_base, filter, ctls);
        ctx.close();
        return answer;
    }

    /**
     * Alternate function that runs a search against the LDAP tree and returns
     * a limited set of results.
     *
     * @param filter string with any valid LDAP search filter
     * @param limit the maximum number of entries to return from the search
     * @return a NamingEnumeration with the records found in the search
     * @throws NamingException if an error is encountered when connecting or
     * searching
     */
    public NamingEnumeration<?> search(String filter, long limit)
            throws NamingException {
        NamingEnumeration<?> answer = null;
        DirContext ctx = null;
        // set up search criteria
        SearchControls ctls = new SearchControls();
        ctls.setReturningObjFlag(true);
        ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
        ctls.setCountLimit(limit);

        ctx = getContext();
        answer = ctx.search(search_base, filter, ctls);
        ctx.close();
        return answer;
    }

    /**
     * Alternate function that runs a search against a subtree in the LDAP
     * tree and returns a limited set of results.
     *
     * @param filter string with any valid LDAP search filter
     * @param base string with an alternate search base
     * @param limit the maximum number of entries to return from the search
     * @return a NamingEnumeration with the records found in the search
     * @throws NamingException if an error is encountered when connecting or
     * searching
     */
    public NamingEnumeration<?> search(String filter, String base, long limit)
            throws NamingException {
        NamingEnumeration<?> answer = null;
        DirContext ctx = null;
        // set up search criteria
        SearchControls ctls = new SearchControls();
        ctls.setReturningObjFlag(true);
        ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);
        ctls.setCountLimit(limit);

        ctx = getContext();
        answer = ctx.search(base, filter, ctls);
        ctx.close();
        return answer;
    }

    /**
     * Alternate function that runs a search against a subtree in the LDAP
     * tree and returns unlimited results.
     *
     * @return a NamingEnumeration with the records found in the search
     * @param filter string with any valid LDAP search filter
     * @param base string with an alternate search base
     * @throws NamingException if an error is encountered when connecting or
     * searching
     */
    public NamingEnumeration<?> search(String filter, String base)
            throws NamingException {
        NamingEnumeration<?> answer = null;
        DirContext ctx = null;
        // set up search criteria
        SearchControls ctls = new SearchControls();
        ctls.setReturningObjFlag(true);
        ctls.setSearchScope(SearchControls.SUBTREE_SCOPE);

        ctx = getContext();
        answer = ctx.search(base, filter, ctls);
        ctx.close();
        return answer;
    }

    /**
     * Use to set the username field after class initialization
     * @param field_name a string with the name of the field to use
     */
    public void setUsernameField(String field_name) {
        username_field = field_name;
    }

    /**
     * Returns the value of the username field.
     * @return a string with the username
     */
    public String getUsernameField() {
        return username_field;
    }

    /**
     * Searches for the username in the LDAP database to find the PERSON ID.
     * Calling this function sets the last_dn member.
     *
     * This is the basic function that the next two functions call to do the actual
     * work of finding and returning the number.
     * @param username the system name for the user, for example: jmdoe25
     * @return a String with the employee number
     * @throws NoPersonIdException If the LDAP record is not found or the
     * employeeNumber field is blank.
     */
    public String getEmployeeNumber(String username) throws NoPersonIdException {
        String empl_num = "";
        String filter = "(" + username_field + "=" + username + ")";
        try {
            NamingEnumeration<?> answer = search(filter);
            if (answer != null) {
                if (answer.hasMore()) {
                    SearchResult sr = (SearchResult) answer.next();
                    last_dn = sr.getName();
                    Attributes attrs = sr.getAttributes();
                    empl_num = (String) attrs.get("employeeNumber").get();
                }
                if (empl_num.equals("")) {
                    throw new NoPersonIdException("Person ID blank", username);
                }
            } else {
                throw new NoPersonIdException("Username not found", username);
            }
        } catch (NamingException e) {
            throw new NoPersonIdException("Error getting employee number.", username, e);
        } catch (NullPointerException e) {
            throw new NoPersonIdException("Null Pointer Exception", username);
        }
        return empl_num;
    }

    /**
     * Returns the employee number for the last DN used.  Requires that the
     * last_dn class member be set before this function is called.
     *
     * @return a string with the employee number
     * @throws NoPersonIdException If an error occured when searching for the
     * UID or retrieving the ID.
     */
    public String getEmployeeNumber() throws NoPersonIdException {
        String empl_num = "";
        if (last_dn != null) {
            if (last_dn.length() > 0) {
                String uid = last_dn.substring(last_dn.indexOf("=") + 1,
                        last_dn.indexOf(","));
                empl_num = getEmployeeNumber(uid);
            }
        }
        return empl_num;
    }

    /**
     * Searches the directory for a matching employee number (person ID) and
     * returns the full DN.
     * @return a string with the full DN
     * @param person_id The Colleague person ID that is stored in the LDAP employeeNumber field
     * @throws NamingException if the search fails
     */
    public String getDnByEmployeeNumber(String person_id) throws NamingException {
        String dn = "";
        NamingEnumeration<?> enumr = search("(employeeNumber=" + person_id + ")");
        if (enumr.hasMore()) {
            SearchResult sr = (SearchResult) enumr.next();
            dn = sr.getName() + "," + search_base;
        } else {
            throw new NamingException("getDnByEmployeeNumber(): No record found for " + person_id);
        }
        return dn;
    }

    /**
     * Searches by UID for an entry and returns the full DN.  While it may be
     * possible to get more than one result in the search (by adding a wild
     * card to the user_id for example) the function will only return the
     * first result.
     *
     * @param user_id the user name to search for
     * @return a string with the full DN
     * @throws NamingException if the search fails
     */
    public String getDN(String user_id) throws NamingException {
        String dn = "";
        NamingEnumeration<?> enumr = search("(" + username_field + "=" + user_id + ")");
        if (enumr.hasMore()) {
            SearchResult sr = (SearchResult) enumr.next();
            dn = sr.getName() + "," + search_base;
        }
        return dn;
    }

    /**
     * Returns the password from the current LDAP record (uses last_dn).
     * @return password - the string stored in the LDAP record
     * @throws UnsupportedEncodingException if it fails when converting the password bytes to a string
     */
    public String getPassword() throws UnsupportedEncodingException {
        String password = "";
        try {
            if (last_dn != null && last_dn.length() > 0) {
                DirContext ctx = getContext();
                String[] wanted_attrs = {"userPassword"};
                Attributes attrs = ctx.getAttributes(getLastDn(), wanted_attrs);
                byte[] passwd_enc = (byte[]) attrs.get("userPassword").get();
                password = new String(passwd_enc, LDAP_ENCODING);
            }
        } catch (NamingException ex) {
            Logger.getLogger(Ldap.class.getName()).log(Level.SEVERE, null, ex);
        }
        return password;
    }

    /**
     * Generates a new password and saves it to the current user's LDAP record.
     * Uses a random generator to create a 8-character alphanumeric password.
     * @return an 8-character string with the password or an
     * empty string if there was an error saving the password
     * @throws org.cfcc.LdapChangeException when there is an error accessing the LDAP tree
     * @throws SystemUnavailableException when the password generator cannot load the crypto module
     */
    public String newPassword() throws LdapChangeException, SystemUnavailableException {
        String new_passwd = PwGen.getPassword(8);
        setPassword(new_passwd, true);
        return new_passwd;
    }

    /**
     * Generates the current timestamp in Unix shadow format.
     * Note to convert to days we are dividing by 1000ms, 60s, 60m, 24h
     * for a total of 86,400,000 milliseconds in a day.
     * @return number of days since the Unix epoch
     */
    public String ldapTimestamp() {
        String ts_out = "";
        int MS_IN_A_DAY = 86400000;
        long uts = System.currentTimeMillis() / MS_IN_A_DAY;
        ts_out = String.valueOf(uts);
        return ts_out;
    }

    /**
     * Replaces the password in the LDAP database for the last record accessed
     * (as specified by the string member last_dn).
     *
     * @param new_passwd a string with the new password to save
     * @param salted is a flag to determine if the SHA1 password should have a random salt
     * @throws org.cfcc.LdapChangeException When there is an error saving the password
     * @throws org.cfcc.SystemUnavailableException When the encryption subroutine fails
     */
    public void setPassword(String new_passwd, boolean salted)
            throws LdapChangeException, SystemUnavailableException {
        try {
            DirContext ctx = getContext();
            String ts = ldapTimestamp();
            byte[] salt = null;

            // set password is a ldap modify operation
            ModificationItem[] mods = new ModificationItem[2];

            LdapShaPasswordEncoder pwd = new LdapShaPasswordEncoder();
            String passwd_hash = pwd.encodePassword(new_passwd, salt);

            Attribute passwd = new BasicAttribute("userPassword", passwd_hash);
            mods[0] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, passwd);

            Attribute slchanged = new BasicAttribute("shadowLastChange", ts);
            mods[1] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, slchanged);

            ctx.modifyAttributes(last_dn + "," + search_base, mods);
            ctx.close();
        } catch (AttributeModificationException e) {
            throw new LdapChangeException(last_dn, "Unable to change password");
        } catch (NamingException e) {
            Logger.getLogger(Ldap.class.getName()).log(Level.SEVERE, "Naming error at LDAP server", e);
            throw new LdapChangeException(last_dn, "Naming error connecting to LDAP server");
        }
    }

    /**
     * Replace the password on an Microsoft Active Directory server via LDAP.
     *
     * <p>Examples: </p>
     * <ul>
     * <li><a href="http://forum.java.sun.com/thread.jspa?threadID=599638&messageID=3200093">User sets own password</a></li>
     * <li><a href="http://forum.java.sun.com/thread.jspa?threadID=592611&tstart=0">Admin resets password</a></li>
     * <li><a href="http://support.microsoft.com/kb/269190">How to change a Windows password through LDAP</a></li>
     * <li><a href="http://support.microsoft.com/kb/247078/EN-US/">How To Enable Secure Socket Layer (SSL) Communication over LDAP...</a></li>
     * <li><a href="http://support.microsoft.com/kb/321051">How to enable LDAP over SSL with a third-party certification authority</a></li>
     * </ul>
     *
     * @param new_password a string with the new password
     * @throws org.cfcc.LdapChangeException thrown if there is any problem setting the password
     * @throws PasswordConstraintException
     */
    public void setUnicodePassword(String new_password)
            throws LdapChangeException, PasswordConstraintException {
        try {
            DirContext ctx = getContext();

            // set password is a ldap modify operation
            ModificationItem[] mods = new ModificationItem[1];

            // Replace the "unicodePwd" attribute with a new value
            // Password must be both Unicode and a quoted string
            String newQuotedPassword = "\"" + new_password + "\"";
            byte[] newUnicodePassword = newQuotedPassword.getBytes("UTF-16LE");

            mods[0] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, new BasicAttribute("unicodePwd", newUnicodePassword));

            /* Debugging only */
            //System.out.println("Setting unicodePwd for " + last_dn + "," + search_base);

            ctx.modifyAttributes(last_dn + "," + search_base, mods);
           
            // Now run a separate modification to clear the userAccountControl
            mods[0] = new ModificationItem(DirContext.REPLACE_ATTRIBUTE, new BasicAttribute("userAccountControl", NORMAL_ACCOUNT));

            ctx.modifyAttributes(last_dn + "," + search_base, mods);
           
            ctx.close();
            /* Catch the following exception raised by AD when the password
             * does not match the constraints.
             *
             * Error updating LDAP for cn=Jakim Friant,ou=Headquarters, setUnicodePassword(): Naming
             * error connecting to Active Directory server [javax.naming.OperationNotSupported
             * Exception: [LDAP: error code 53 - 0000052D: SvcErr: DSID-031A0FBC, problem 5003
             * (WILL_NOT_PERFORM), data 0
             */
        } catch (javax.naming.OperationNotSupportedException ex) {
            throw new PasswordConstraintException(ex);
        } catch (UnsupportedEncodingException ex) {
            throw new LdapChangeException(last_dn, "Error setting password on Active Directory server [" + ex + "]");
        } catch (NamingException ex) {
            // ex.printStackTrace();
            throw new LdapChangeException(last_dn, "Naming error connecting to Active Directory server [" + ex + "]");
        }
    }

    /**
     * Explicitly sets the last_dn member string.  It will strip off the base
     * DN and only save the unique portion of the DN.  For example, if the
     * base DN is "dc=cfcc,dc=edu", then
     * "uid=jdoe25,ou=Students,dc=cfcc,dc=edu" becomes
     * "uid=jdoe25,ou=Students".
     *
     * @param dn a string with a full or partial DN
     * @throws StringIndexOutOfBoundsException if the dn parameter cannot be
     * parsed, such as a blank string
     */
    public void setLastDn(String dn) throws StringIndexOutOfBoundsException {
        try {
            int base_loc = dn.indexOf(search_base) - 1;
            if (base_loc > 0) {
                last_dn = dn.substring(0, base_loc);
            } else {
                last_dn = dn;
            }
        } catch (StringIndexOutOfBoundsException e) {
            System.err.println("ResetPassword - Unable to parse DN: " + dn + ". " + e.getMessage());
            throw e;
        }
    }

    /**
     * Returns the full DN for the last_dn member.
     *
     * @return a string with the FQDN of the last DN used, or an empty string
     * if the last_dn is null.
     */
    public String getLastDn() {
        StringBuffer dn_out = new StringBuffer("");
        if (last_dn != null) {
            dn_out.append(last_dn);
            dn_out.append(",");
            dn_out.append(search_base);
        }
        return dn_out.toString();
    }

    /**
     * Returns just the username portion of the DN.  Used to link the LDAP
     * entry back to the Registry record.
     *
     * @return a string with that contains the username, or an empty string
     */
    public String getLastUsername() {
        String username = "";
        try {
            if (last_dn != null) {
                username = last_dn.substring(last_dn.indexOf('=') + 1,
                        last_dn.indexOf(','));
            }
        } catch (StringIndexOutOfBoundsException e) { /* pass */ }
        return username;
    }

    /**
     * Sets the employee type (or multiple types) based on the Office Codes from
     * Colleague
     * @param office_codes A list of office code strings from Colleague
     * @throws org.cfcc.LdapChangeException If there was an error updating the LDAP record
     */
    public void setEmployeeType(ArrayList<?> office_codes)
            throws LdapChangeException {
        String staff_code;
        try {
            DirContext ctx = getContext();

            Attributes attrs = new BasicAttributes(true);
            Attribute oc = new BasicAttribute("employeeType", office_codes.get(0));
            ListIterator<?> itr = office_codes.listIterator(1);
            while (itr.hasNext()) {
                staff_code = (String) itr.next();
                if (staff_code.matches("[A-Z][A-Z]*")) {
                    oc.add(staff_code);
                }
            }
            attrs.put(oc);
            try {
                ctx.modifyAttributes(last_dn + "," + search_base,
                        DirContext.ADD_ATTRIBUTE,
                        attrs);
            } catch (NamingException e) {
                // If we can't add, maybe we can replace...
                ctx.modifyAttributes(last_dn + "," + search_base,
                        DirContext.REPLACE_ATTRIBUTE,
                        attrs);
            }
            ctx.close();
        } catch (AttributeModificationException e) {
            throw new LdapChangeException(last_dn, "Unable to add employee type");
        } catch (NamingException e) {
            throw new LdapChangeException(last_dn, "Naming exception");
        }
    }

    /**
     * Creates the necessary fields for the entry to function as a Posix/shadow
     * account -- mainly for Linux authentication.
     * @param person_id The Colleague person ID
     * @param last is the last name of the user
     * @param first is the first name of the user
     * @throws LdapChangeException On an error creating or writing the new
     * LDAP record.
     */
    public void convertToPosixAccount(String person_id, String last, String first) throws LdapChangeException {
        try {
            DirContext ctx = getContext();
            Attributes attrs = new BasicAttributes(true);

            // build the full name for later
            String full_name = first + " " + last;

            System.out.println("Ldap.convertToPosixAccount(): " + person_id + ", " + full_name);

            // we have to convert this to an integer and then back to a string
            // so that the beginning zeros are removed
            Integer per_id = new Integer(person_id);

            // Add the proper OU's
            Attribute oc = new BasicAttribute("objectClass");
            oc.add("top");
            oc.add("person");
            oc.add("organizationalPerson");
            oc.add("inetOrgPerson");
            oc.add("posixAccount");
            oc.add("shadowAccount");
            attrs.put(oc);

            // then add the default values
            attrs.put("gidNumber", "1000");
            attrs.put("uidNumber", per_id.toString());
            attrs.put("homeDirectory", "/home/" + getLastUsername());
            attrs.put("loginShell", "/bin/bash");
            attrs.put("gecos", full_name);
            attrs.put("sn", last);
            attrs.put("givenName", first);
            attrs.put("displayName", full_name);
            attrs.put("mail", getLastUsername() + "@" + EMAIL_DOMAIN);
            attrs.put("shadowMin", "0");
            attrs.put("shadowMax", "999999");
            attrs.put("shadowWarning", "7");
            attrs.put("shadowInactive", "-1");
            attrs.put("shadowExpire", "-1");
            attrs.put("shadowFlag", "0");
            try {
                ctx.modifyAttributes(last_dn + "," + search_base,
                        DirContext.ADD_ATTRIBUTE,
                        attrs);
            } catch (NamingException e) {
                // If we can't add, maybe we can replace...
                ctx.modifyAttributes(last_dn + "," + search_base,
                        DirContext.REPLACE_ATTRIBUTE,
                        attrs);
            }
            ctx.close();
        } catch (AttributeModificationException e) {
            throw new LdapChangeException(last_dn, "Unable to update to posixAccount: " + e);
        } catch (NamingException e) {
            throw new LdapChangeException(last_dn, "Naming exception: " + e);
        } catch (NumberFormatException e) {
            throw new LdapChangeException(last_dn, "Person ID is NULL: " + e);
        }
    }

    /**
     * Returns the value of the search base member
     *
     * @return a string with the search base DN
     */
    public String getSearchBase() {
        return search_base;
    }

    /**
     * Runs a search and prints the result.  Mainly used for testing
     * @see #formatResults(NamingEnumeration)
     * @param filter a string with a LDAP search construct
     * @throws javax.naming.NamingException If there is an error with the search.
     */
    public void searchResult(String filter) throws NamingException {
        NamingEnumeration<?> result = search(filter);
        formatResults(result);
    }

    /**
     * Generic method to format and print the NamingEnumeration returned from
     * a search.  Used for testing.
     *
     * @param enumr the NamingEnumeration object returned from a search
     * @see #formatAttributes(Attributes)
     */
    public void formatResults(NamingEnumeration<?> enumr) {
        int count = 0;
        try {
            while (enumr.hasMore()) {
                SearchResult sr = (SearchResult) enumr.next();
                System.out.println("SEARCH RESULT:" + sr.getName());
                formatAttributes(sr.getAttributes());
                System.out.println("====================================================");
                count++;
            }

            System.out.println("Search returned " + count + " results");

        } catch (NamingException e) {
            e.printStackTrace();
        }
    }

    /**
     * Generic method to format and print the Attributes.  Displays all the
     * multiple values of each Attribute in the Attributes.
     *
     * @param attrs an Attribute collection returned by a search result object
     * @see #formatResults(NamingEnumeration)
     */
    public void formatAttributes(Attributes attrs) {
        if (attrs == null) {
            System.out.println("This result has no attributes");
        } else {
            try {
                for (NamingEnumeration<?> enumr = attrs.getAll(); enumr.hasMore();) {
                    Attribute attrib = (Attribute) enumr.next();
                    System.out.println("ATTRIBUTE :" + attrib.getID());
                    for (NamingEnumeration<?> e = attrib.getAll(); e.hasMore();) {
                        System.out.println("\t\t        = " + e.next());
                    }
                }

            } catch (NamingException e) {
                e.printStackTrace();
            }
        }
    }
}
TOP

Related Classes of org.cfcc.services.Ldap

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.