org.apache.openmeetings.core.ldap.LdapLoginManagement.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.openmeetings.core.ldap.LdapLoginManagement.java

Source

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you under the Apache License, Version 2.0 (the
 * "License") +  you may not use this file except in compliance
 * with the License.  You may obtain a copy of the License at
 *
 *   http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */
package org.apache.openmeetings.core.ldap;

import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_DEFAULT_GROUP_ID;
import static org.apache.openmeetings.util.OpenmeetingsVariables.webAppRootKey;

import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

import org.apache.directory.api.ldap.model.cursor.CursorException;
import org.apache.directory.api.ldap.model.cursor.CursorLdapReferralException;
import org.apache.directory.api.ldap.model.cursor.EntryCursor;
import org.apache.directory.api.ldap.model.entry.Attribute;
import org.apache.directory.api.ldap.model.entry.Entry;
import org.apache.directory.api.ldap.model.entry.Value;
import org.apache.directory.api.ldap.model.exception.LdapAuthenticationException;
import org.apache.directory.api.ldap.model.exception.LdapException;
import org.apache.directory.api.ldap.model.exception.LdapInvalidAttributeValueException;
import org.apache.directory.api.ldap.model.message.AliasDerefMode;
import org.apache.directory.api.ldap.model.message.SearchRequestImpl;
import org.apache.directory.api.ldap.model.message.SearchScope;
import org.apache.directory.api.ldap.model.name.Dn;
import org.apache.directory.ldap.client.api.EntryCursorImpl;
import org.apache.directory.ldap.client.api.LdapConnection;
import org.apache.directory.ldap.client.api.LdapNetworkConnection;
import org.apache.openmeetings.db.dao.basic.ConfigurationDao;
import org.apache.openmeetings.db.dao.server.LdapConfigDao;
import org.apache.openmeetings.db.dao.user.GroupDao;
import org.apache.openmeetings.db.dao.user.UserDao;
import org.apache.openmeetings.db.entity.server.LdapConfig;
import org.apache.openmeetings.db.entity.user.Address;
import org.apache.openmeetings.db.entity.user.Group;
import org.apache.openmeetings.db.entity.user.GroupUser;
import org.apache.openmeetings.db.entity.user.User;
import org.apache.openmeetings.db.entity.user.User.Right;
import org.apache.openmeetings.db.entity.user.User.Type;
import org.apache.openmeetings.db.util.TimezoneUtil;
import org.apache.openmeetings.util.OmException;
import org.apache.openmeetings.util.OmFileHelper;
import org.apache.wicket.util.string.Strings;
import org.red5.logging.Red5LoggerFactory;
import org.slf4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;

/**
 * Management of optional LDAP Login
 * 
 * @author o.becherer
 * 
 */
public class LdapLoginManagement {
    private static final Logger log = Red5LoggerFactory.getLogger(LdapLoginManagement.class, webAppRootKey);
    // LDAP custom attribute mapping keys
    private static final String CONFIGKEY_LDAP_KEY_LOGIN = "ldap_user_attr_login";
    private static final String CONFIGKEY_LDAP_KEY_LASTNAME = "ldap_user_attr_lastname";
    private static final String CONFIGKEY_LDAP_KEY_FIRSTNAME = "ldap_user_attr_firstname";
    private static final String CONFIGKEY_LDAP_KEY_MAIL = "ldap_user_attr_mail";
    private static final String CONFIGKEY_LDAP_KEY_STREET = "ldap_user_attr_street";
    private static final String CONFIGKEY_LDAP_KEY_ADDITIONAL_NAME = "ldap_user_attr_additionalname";
    private static final String CONFIGKEY_LDAP_KEY_FAX = "ldap_user_attr_fax";
    private static final String CONFIGKEY_LDAP_KEY_ZIP = "ldap_user_attr_zip";
    private static final String CONFIGKEY_LDAP_KEY_COUNTRY = "ldap_user_attr_country";
    private static final String CONFIGKEY_LDAP_KEY_TOWN = "ldap_user_attr_town";
    private static final String CONFIGKEY_LDAP_KEY_PHONE = "ldap_user_attr_phone";
    static final String CONFIGKEY_LDAP_KEY_PICTURE_URI = "ldap_user_picture_uri";
    private static final String CONFIGKEY_LDAP_KEY_GROUP = "ldap_group_attr";

    // LDAP default attributes mapping
    private static final String LDAP_KEY_LOGIN = "uid";
    private static final String LDAP_KEY_LASTNAME = "sn";
    private static final String LDAP_KEY_FIRSTNAME = "givenName";
    private static final String LDAP_KEY_MAIL = "mail";
    private static final String LDAP_KEY_STREET = "streetAddress";
    private static final String LDAP_KEY_ADDITIONAL_NAME = "description";
    private static final String LDAP_KEY_FAX = "facsimileTelephoneNumber";
    private static final String LDAP_KEY_ZIP = "postalCode";
    private static final String LDAP_KEY_COUNTRY = "co";
    private static final String LDAP_KEY_TOWN = "l";
    private static final String LDAP_KEY_PHONE = "telephoneNumber";
    private static final String LDAP_KEY_TIMEZONE = "timezone";
    private static final String LDAP_KEY_PICTURE_URI = "pictureUri";
    private static final String LDAP_KEY_GROUP = "memberOf";

    public enum AuthType {
        NONE, SEARCHANDBIND, SIMPLEBIND
    }

    public enum Provisionning {
        NONE, AUTOUPDATE, AUTOCREATE
    }

    public enum GroupMode {
        NONE, ATTRIBUTE, QUERY
    }

    @Autowired
    private ConfigurationDao cfgDao;
    @Autowired
    private LdapConfigDao ldapConfigDao;
    @Autowired
    private UserDao userDao;
    @Autowired
    private GroupDao groupDao;
    @Autowired
    private TimezoneUtil timezoneUtil;

    private static void bindAdmin(LdapConnection conn, LdapOptions options) throws LdapException {
        if (!Strings.isEmpty(options.adminDn)) {
            conn.bind(options.adminDn, options.adminPasswd);
        } else {
            conn.bind();
        }
    }

    private static Attribute getAttr(Properties config, Entry entry, String aliasCode, String defaultAlias)
            throws LdapInvalidAttributeValueException {
        String alias = config.getProperty(aliasCode, "");
        Attribute a = entry.get(Strings.isEmpty(alias) ? defaultAlias : alias);
        return a == null ? null : a;
    }

    private static String getStringAttr(Properties config, Entry entry, String aliasCode, String defaultAlias)
            throws LdapInvalidAttributeValueException {
        Attribute a = getAttr(config, entry, aliasCode, defaultAlias);
        return a == null ? null : a.getString();
    }

    private static String getLogin(Properties config, Entry entry) throws LdapInvalidAttributeValueException {
        return getStringAttr(config, entry, CONFIGKEY_LDAP_KEY_LOGIN, LDAP_KEY_LOGIN);
    }

    /**
     * Ldap Login
     * 
     * Connection Data is retrieved from ConfigurationFile
     * 
     */
    public User login(String login, String passwd, Long domainId) throws OmException {
        log.debug("LdapLoginmanagement.doLdapLogin");
        if (!userDao.validLogin(login)) {
            log.error("Invalid login provided");
            return null;
        }

        User u = null;
        try (LdapWorker w = new LdapWorker(domainId)) {
            if (w.options.useLowerCase) {
                login = login.toLowerCase();
            }

            boolean authenticated = true;
            Dn userDn = null;
            Entry entry = null;
            switch (w.options.type) {
            case SEARCHANDBIND: {
                bindAdmin(w.conn, w.options);
                Dn baseDn = new Dn(w.options.searchBase);
                String searchQ = String.format(w.options.searchQuery, login);

                try (EntryCursor cursor = new EntryCursorImpl(w.conn
                        .search(new SearchRequestImpl().setBase(baseDn).setFilter(searchQ).setScope(w.options.scope)
                                .addAttributes("*").setDerefAliases(w.options.derefMode)))) {
                    while (cursor.next()) {
                        try {
                            Entry e = cursor.get();
                            if (userDn != null) {
                                log.error("more than 1 user found in LDAP");
                                throw new OmException(-1L);
                            }
                            userDn = e.getDn();
                            if (w.options.useAdminForAttrs) {
                                entry = e;
                            }
                        } catch (CursorLdapReferralException cle) {
                            log.warn("Referral LDAP entry found, ignore it");
                        }
                    }
                }
                if (userDn == null) {
                    log.error("NONE users found in LDAP");
                    throw new OmException(-11L);
                }
                w.conn.bind(userDn, passwd);
            }
                break;
            case SIMPLEBIND: {
                userDn = new Dn(String.format(w.options.userDn, login));
                w.conn.bind(userDn, passwd);
            }
                break;
            case NONE:
            default:
                authenticated = false;
                break;
            }
            u = authenticated ? userDao.getByLogin(login, Type.ldap, domainId) : userDao.login(login, passwd);
            if (u == null && Provisionning.AUTOCREATE != w.options.prov) {
                log.error("User not found in OM DB and Provisionning.AUTOCREATE was not set");
                throw new OmException(-11L);
            }
            if (authenticated && entry == null) {
                if (w.options.useAdminForAttrs) {
                    bindAdmin(w.conn, w.options);
                }
                entry = w.conn.lookup(userDn);
            }
            switch (w.options.prov) {
            case AUTOUPDATE:
            case AUTOCREATE:
                u = w.getUser(entry, u);
                if (w.options.syncPasswd) {
                    u.updatePassword(cfgDao, passwd);
                }
                u = userDao.update(u, null);
                break;
            case NONE:
            default:
                break;
            }
        } catch (LdapAuthenticationException ae) {
            log.error("Not authenticated.", ae);
            throw new OmException(-11L);
        } catch (OmException e) {
            throw e;
        } catch (Exception e) {
            log.error("Unexpected exception.", e);
            throw new OmException(e);
        }
        return u;
    }

    public void importUsers(Long domainId, boolean print) throws OmException {
        try (LdapWorker w = new LdapWorker(domainId)) {
            bindAdmin(w.conn, w.options);
            Dn baseDn = new Dn(w.options.searchBase);

            try (EntryCursor cursor = new EntryCursorImpl(
                    w.conn.search(new SearchRequestImpl().setBase(baseDn).setFilter(w.options.importQuery)
                            .setScope(w.options.scope).addAttributes("*").setDerefAliases(w.options.derefMode)))) {
                while (cursor.next()) {
                    try {
                        Entry e = cursor.get();
                        User u = userDao.getByLogin(getLogin(w.config, e), Type.ldap, domainId);
                        u = w.getUser(e, u);
                        if (print) {
                            log.info("Going to import user: {}", u);
                        } else {
                            userDao.update(u, null);
                            log.info("User {}, was imported", u);
                        }
                    } catch (CursorLdapReferralException cle) {
                        log.warn("Referral LDAP entry found, ignore it");
                    }
                }
            }
        } catch (LdapAuthenticationException ae) {
            log.error("Not authenticated.", ae);
            throw new OmException(-11L);
        } catch (OmException e) {
            throw e;
        } catch (Exception e) {
            log.error("Unexpected exception.", e);
            throw new OmException(e);
        }
    }

    private class LdapWorker implements Closeable {
        LdapConnection conn = null;
        Properties config = new Properties();
        LdapOptions options = null;
        Long domainId = null;

        public LdapWorker(Long domainId) throws Exception {
            this.domainId = domainId;
            LdapConfig ldapConfig = ldapConfigDao.get(domainId);
            try (InputStream is = new FileInputStream(
                    new File(OmFileHelper.getConfDir(), ldapConfig.getConfigFileName()));
                    Reader r = new InputStreamReader(is, StandardCharsets.UTF_8)) {
                config.load(r);
                if (config.isEmpty()) {
                    throw new RuntimeException("Error on LdapLogin : Configurationdata couldnt be retrieved!");
                }
                options = new LdapOptions(config);
            } catch (Exception e) {
                log.error("Error on LdapLogin : Configurationdata couldn't be retrieved!");
                throw e;
            }

            conn = new LdapNetworkConnection(options.host, options.port, options.secure);
        }

        public User getUser(Entry entry, User u) throws LdapException, CursorException, OmException, IOException {
            if (entry == null) {
                log.error("LDAP entry is null, search or lookup by Dn failed");
                throw new OmException(-11L);
            }
            if (u == null) {
                u = userDao.getNewUserInstance(null);
                u.setType(Type.ldap);
                u.getRights().remove(Right.Login);
                u.setDomainId(domainId);
                Group g = groupDao.get(cfgDao.getConfValue(CONFIG_DEFAULT_GROUP_ID, Long.class, "-1"));
                if (g != null) {
                    u.getGroupUsers().add(new GroupUser(g, u));
                }
                u.setLogin(getLogin(config, entry));
                u.setShowContactDataToContacts(true);
                u.setAddress(new Address());
            }
            u.setLastname(getStringAttr(config, entry, CONFIGKEY_LDAP_KEY_LASTNAME, LDAP_KEY_LASTNAME));
            u.setFirstname(getStringAttr(config, entry, CONFIGKEY_LDAP_KEY_FIRSTNAME, LDAP_KEY_FIRSTNAME));
            u.getAddress().setEmail(getStringAttr(config, entry, CONFIGKEY_LDAP_KEY_MAIL, LDAP_KEY_MAIL));
            u.getAddress().setStreet(getStringAttr(config, entry, CONFIGKEY_LDAP_KEY_STREET, LDAP_KEY_STREET));
            u.getAddress().setAdditionalname(
                    getStringAttr(config, entry, CONFIGKEY_LDAP_KEY_ADDITIONAL_NAME, LDAP_KEY_ADDITIONAL_NAME));
            u.getAddress().setFax(getStringAttr(config, entry, CONFIGKEY_LDAP_KEY_FAX, LDAP_KEY_FAX));
            u.getAddress().setZip(getStringAttr(config, entry, CONFIGKEY_LDAP_KEY_ZIP, LDAP_KEY_ZIP));
            u.getAddress().setCountry(getStringAttr(config, entry, CONFIGKEY_LDAP_KEY_COUNTRY, LDAP_KEY_COUNTRY));
            u.getAddress().setTown(getStringAttr(config, entry, CONFIGKEY_LDAP_KEY_TOWN, LDAP_KEY_TOWN));
            u.getAddress().setPhone(getStringAttr(config, entry, CONFIGKEY_LDAP_KEY_PHONE, LDAP_KEY_PHONE));
            String tz = getStringAttr(config, entry, LdapOptions.CONFIGKEY_LDAP_TIMEZONE_NAME, LDAP_KEY_TIMEZONE);
            if (tz == null) {
                tz = options.tz;
            }
            u.setTimeZoneId(timezoneUtil.getTimeZone(tz).getID());
            String picture = getStringAttr(config, entry, CONFIGKEY_LDAP_KEY_PICTURE_URI, LDAP_KEY_PICTURE_URI);
            if (picture == null) {
                picture = options.pictureUri;
            }
            u.setPictureuri(picture);

            List<Dn> groups = new ArrayList<>();
            if (GroupMode.ATTRIBUTE == options.groupMode) {
                Attribute attr = getAttr(config, entry, CONFIGKEY_LDAP_KEY_GROUP, LDAP_KEY_GROUP);
                if (attr != null) {
                    for (Value<?> v : attr) {
                        groups.add(new Dn(v.getString()));
                    }
                }
            } else if (GroupMode.QUERY == options.groupMode) {
                Dn baseDn = new Dn(options.searchBase);
                String searchQ = String.format(options.groupQuery, u.getLogin());

                try (EntryCursor cursor = new EntryCursorImpl(conn.search(
                        new SearchRequestImpl().setBase(baseDn).setFilter(searchQ).setScope(SearchScope.SUBTREE)
                                .addAttributes("*").setDerefAliases(AliasDerefMode.DEREF_ALWAYS)))) {
                    while (cursor.next()) {
                        try {
                            Entry e = cursor.get();
                            groups.add(e.getDn());
                        } catch (CursorLdapReferralException cle) {
                            log.warn("Referral LDAP entry found, ignore it");
                        }
                    }
                }
            }
            for (Dn g : groups) {
                String name = g.getRdn().getValue();
                if (!Strings.isEmpty(name)) {
                    Group o = groupDao.get(name);
                    boolean found = false;
                    if (o == null) {
                        o = new Group();
                        o.setName(name);
                        o = groupDao.update(o, u.getId());
                    } else {
                        for (GroupUser ou : u.getGroupUsers()) {
                            if (ou.getGroup().getName().equals(name)) {
                                found = true;
                                break;
                            }
                        }
                    }
                    if (!found) {
                        u.getGroupUsers().add(new GroupUser(o, u));
                        log.debug("Going to add user to group:: " + name);
                    }
                }
            }
            return u;
        }

        @Override
        public void close() throws IOException {
            if (conn != null) {
                conn.close();
            }
        }
    }
}