| /* |
| * 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.ldap; |
| |
| import static org.apache.openmeetings.util.OpenmeetingsVariables.CONFIG_DEFAULT_GROUP_ID; |
| import static org.apache.openmeetings.util.OpenmeetingsVariables.webAppRootKey; |
| |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.IOException; |
| import java.io.InputStreamReader; |
| import java.io.Reader; |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.Properties; |
| |
| 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.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.exception.LdapInvalidDnException; |
| 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.api.ldap.model.name.Rdn; |
| 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.OrganisationDao; |
| import org.apache.openmeetings.db.dao.user.StateDao; |
| import org.apache.openmeetings.db.dao.user.UserDao; |
| import org.apache.openmeetings.db.entity.server.LdapConfig; |
| import org.apache.openmeetings.db.entity.user.Organisation; |
| import org.apache.openmeetings.db.entity.user.Organisation_Users; |
| 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); |
| // Config constants |
| private static final String CONFIGKEY_LDAP_HOST = "ldap_conn_host"; |
| private static final String CONFIGKEY_LDAP_PORT = "ldap_conn_port"; |
| private static final String CONFIGKEY_LDAP_SECURE = "ldap_conn_secure"; |
| private static final String CONFIGKEY_LDAP_ADMIN_DN = "ldap_admin_dn"; |
| private static final String CONFIGKEY_LDAP_ADMIN_PASSWD = "ldap_passwd"; |
| private static final String CONFIGKEY_LDAP_AUTH_TYPE = "ldap_auth_type"; |
| private static final String CONFIGKEY_LDAP_PROV_TYPE = "ldap_provisionning"; |
| |
| private static final String CONFIGKEY_LDAP_SYNC_PASSWD_OM = "ldap_sync_password_to_om"; // 'true' or 'false' |
| private static final String CONFIGKEY_LDAP_USE_LOWER_CASE = "ldap_use_lower_case"; |
| private static final String CONFIGKEY_LDAP_TIMEZONE_NAME = "ldap_user_timezone"; |
| private static final String CONFIGKEY_LDAP_SEARCH_BASE = "ldap_search_base"; |
| private static final String CONFIGKEY_LDAP_SEARCH_QUERY = "ldap_search_query"; |
| private static final String CONFIGKEY_LDAP_SEARCH_SCOPE = "ldap_search_scope"; |
| private static final String CONFIGKEY_LDAP_USERDN_FORMAT = "ldap_userdn_format"; |
| private static final String CONFIGKEY_LDAP_USE_ADMIN_4ATTRS = "ldap_use_admin_to_get_attrs"; |
| private static final String CONFIGKEY_LDAP_DEREF_MODE = "ldap_deref_mode"; |
| private static final String CONFIGKEY_LDAP_GROUP_MODE = "ldap_group_mode"; |
| private static final String CONFIGKEY_LDAP_GROUP_QUERY = "ldap_group_query"; |
| |
| // LDAP custom attribute mapping keys |
| 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"; |
| private 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_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 StateDao stateDao; |
| @Autowired |
| private LdapConfigDao ldapConfigDao; |
| @Autowired |
| private UserDao userDao; |
| @Autowired |
| private OrganisationDao orgDao; |
| @Autowired |
| private TimezoneUtil timezoneUtil; |
| |
| private Dn getUserDn(Properties config, String user) throws LdapInvalidDnException { |
| return new Dn(String.format(config.getProperty(CONFIGKEY_LDAP_USERDN_FORMAT, "%s"), user)); |
| } |
| |
| private void bindAdmin(LdapConnection conn, String admin, String pass) throws LdapException { |
| if (!Strings.isEmpty(admin)) { |
| conn.bind(admin, pass); |
| } else { |
| conn.bind(); |
| } |
| } |
| |
| private String 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.getString(); |
| } |
| /** |
| * 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; |
| } |
| |
| Properties config = new Properties(); |
| Reader r = null; |
| try { |
| LdapConfig ldapConfig = ldapConfigDao.get(domainId); |
| r = new InputStreamReader(new FileInputStream(new File(OmFileHelper.getConfDir(), ldapConfig.getConfigFileName())), "UTF-8"); |
| config.load(r); |
| } catch (Exception e) { |
| log.error("Error on LdapLogin : Configurationdata couldn't be retrieved!"); |
| return null; |
| } finally { |
| if (r != null) { |
| try { |
| r.close(); |
| } catch (IOException e) { |
| log.error("Error while closing ldap config file"); |
| return null; |
| } |
| } |
| } |
| if (config.isEmpty()) { |
| log.error("Error on LdapLogin : Configurationdata couldnt be retrieved!"); |
| return null; |
| } |
| |
| String ldap_use_lower_case = config.getProperty(CONFIGKEY_LDAP_USE_LOWER_CASE, "false"); |
| if ("true".equals(ldap_use_lower_case)) { |
| login = login.toLowerCase(); |
| } |
| |
| String ldap_auth_type = config.getProperty(CONFIGKEY_LDAP_AUTH_TYPE, ""); |
| AuthType type = AuthType.SIMPLEBIND; |
| try { |
| type = AuthType.valueOf(ldap_auth_type); |
| } catch (Exception e) { |
| log.error(String.format("ConfigKey in Ldap Config contains invalid auth type : '%s' -> Defaulting to %s", ldap_auth_type, type)); |
| } |
| |
| String ldap_prov_type = config.getProperty(CONFIGKEY_LDAP_PROV_TYPE, ""); |
| Provisionning prov = Provisionning.AUTOCREATE; |
| try { |
| prov = Provisionning.valueOf(ldap_prov_type); |
| } catch (Exception e) { |
| log.error(String.format("ConfigKey in Ldap Config contains invalid provisionning type : '%s' -> Defaulting to %s", ldap_prov_type, prov)); |
| } |
| |
| String ldap_deref_mode = config.getProperty(CONFIGKEY_LDAP_DEREF_MODE, ""); |
| AliasDerefMode derefMode = AliasDerefMode.DEREF_ALWAYS; |
| try { |
| derefMode = AliasDerefMode.getDerefMode(ldap_deref_mode); |
| } catch (Exception e) { |
| log.error(String.format("ConfigKey in Ldap Config contains invalid deref mode : '%s' -> Defaulting to %s", ldap_deref_mode, derefMode)); |
| } |
| |
| if (AuthType.NONE == type && Provisionning.NONE == prov) { |
| log.error("Both AuthType and Provisionning are NONE!"); |
| return null; |
| } |
| boolean useAdminForAttrs = true; |
| try { |
| useAdminForAttrs = "true".equals(config.getProperty(CONFIGKEY_LDAP_USE_ADMIN_4ATTRS, "")); |
| } catch (Exception e) { |
| //no-op |
| } |
| GroupMode groupMode = GroupMode.NONE; |
| try { |
| groupMode = GroupMode.valueOf(config.getProperty(CONFIGKEY_LDAP_GROUP_MODE, "NONE")); |
| } catch (Exception e) { |
| //no-op |
| } |
| if (AuthType.NONE == type && !useAdminForAttrs) { |
| log.error("Unable to get Attributes, please change Auth type and/or Use Admin to get attributes"); |
| return null; |
| } |
| |
| // Connection URL |
| String ldap_host = config.getProperty(CONFIGKEY_LDAP_HOST); |
| int ldap_port = Integer.parseInt(config.getProperty(CONFIGKEY_LDAP_PORT, "389")); |
| boolean ldap_secure = "true".equals(config.getProperty(CONFIGKEY_LDAP_SECURE, "false")); |
| |
| // Username for LDAP SERVER himself |
| String ldap_admin_dn = config.getProperty(CONFIGKEY_LDAP_ADMIN_DN); |
| |
| // Password for LDAP SERVER himself |
| String ldap_admin_passwd = config.getProperty(CONFIGKEY_LDAP_ADMIN_PASSWD); |
| |
| User u = null; |
| LdapConnection conn = null; |
| try { |
| boolean authenticated = true; |
| conn = new LdapNetworkConnection(ldap_host, ldap_port, ldap_secure); |
| Dn userDn = null; |
| Entry entry = null; |
| switch (type) { |
| case SEARCHANDBIND: |
| { |
| bindAdmin(conn, ldap_admin_dn, ldap_admin_passwd); |
| Dn baseDn = new Dn(config.getProperty(CONFIGKEY_LDAP_SEARCH_BASE, "")); |
| String searchQ = String.format(config.getProperty(CONFIGKEY_LDAP_SEARCH_QUERY, "%s"), login); |
| SearchScope scope = SearchScope.valueOf(config.getProperty(CONFIGKEY_LDAP_SEARCH_SCOPE, SearchScope.ONELEVEL.name())); |
| |
| EntryCursor cursor = new EntryCursorImpl(conn.search( |
| new SearchRequestImpl() |
| .setBase(baseDn) |
| .setFilter(searchQ) |
| .setScope(scope) |
| .addAttributes("*") |
| .setDerefAliases(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 (useAdminForAttrs) { |
| entry = e; |
| } |
| } catch (CursorLdapReferralException cle) { |
| log.warn("Referral LDAP entry found, ignore it"); |
| } |
| } |
| cursor.close(); |
| if (userDn == null) { |
| log.error("NONE users found in LDAP"); |
| throw new OmException(-11L); |
| } |
| conn.bind(userDn, passwd); |
| } |
| break; |
| case SIMPLEBIND: |
| { |
| userDn = getUserDn(config, login); |
| 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 != prov) { |
| log.error("User not found in OM DB and Provisionning.AUTOCREATE was not set"); |
| throw new OmException(-11L); |
| } |
| if (authenticated && entry == null) { |
| if (useAdminForAttrs) { |
| bindAdmin(conn, ldap_admin_dn, ldap_admin_passwd); |
| } |
| entry = conn.lookup(userDn); |
| } |
| switch (prov) { |
| case AUTOUPDATE: |
| case AUTOCREATE: |
| 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); |
| u.getOrganisation_users().add(new Organisation_Users(orgDao.get(cfgDao.getConfValue(CONFIG_DEFAULT_GROUP_ID, Long.class, "-1")))); |
| u.setLogin(login); |
| u.setShowContactDataToContacts(true); |
| } |
| if ("true".equals(config.getProperty(CONFIGKEY_LDAP_SYNC_PASSWD_OM, ""))) { |
| u.updatePassword(cfgDao, passwd); |
| } |
| u.setLastname(getAttr(config, entry, CONFIGKEY_LDAP_KEY_LASTNAME, LDAP_KEY_LASTNAME)); |
| u.setFirstname(getAttr(config, entry, CONFIGKEY_LDAP_KEY_FIRSTNAME, LDAP_KEY_FIRSTNAME)); |
| u.getAdresses().setEmail(getAttr(config, entry, CONFIGKEY_LDAP_KEY_MAIL, LDAP_KEY_MAIL)); |
| u.getAdresses().setStreet(getAttr(config, entry, CONFIGKEY_LDAP_KEY_STREET, LDAP_KEY_STREET)); |
| u.getAdresses().setAdditionalname(getAttr(config, entry, CONFIGKEY_LDAP_KEY_ADDITIONAL_NAME, LDAP_KEY_ADDITIONAL_NAME)); |
| u.getAdresses().setFax(getAttr(config, entry, CONFIGKEY_LDAP_KEY_FAX, LDAP_KEY_FAX)); |
| u.getAdresses().setZip(getAttr(config, entry, CONFIGKEY_LDAP_KEY_ZIP, LDAP_KEY_ZIP)); |
| u.getAdresses().setStates(stateDao.getStateByName(getAttr(config, entry, CONFIGKEY_LDAP_KEY_COUNTRY, LDAP_KEY_COUNTRY))); |
| u.getAdresses().setTown(getAttr(config, entry, CONFIGKEY_LDAP_KEY_TOWN, LDAP_KEY_TOWN)); |
| u.getAdresses().setPhone(getAttr(config, entry, CONFIGKEY_LDAP_KEY_PHONE, LDAP_KEY_PHONE)); |
| String tz = getAttr(config, entry, CONFIGKEY_LDAP_TIMEZONE_NAME, LDAP_KEY_TIMEZONE); |
| if (tz == null) { |
| tz = config.getProperty(CONFIGKEY_LDAP_TIMEZONE_NAME, null); |
| } |
| u.setTimeZoneId(timezoneUtil.getTimeZone(tz).getID()); |
| String picture = getAttr(config, entry, CONFIGKEY_LDAP_KEY_PICTURE_URI, LDAP_KEY_PICTURE_URI); |
| if (picture == null) { |
| picture = config.getProperty(CONFIGKEY_LDAP_KEY_PICTURE_URI, null); |
| } |
| u.setPictureuri(picture); |
| |
| u = userDao.update(u, null); |
| |
| List<Dn> groups = new ArrayList<>(); |
| if (GroupMode.ATTRIBUTE == groupMode) { |
| Attribute a = entry.get(config.getProperty(CONFIGKEY_LDAP_KEY_GROUP, LDAP_KEY_GROUP)); |
| String attr = a == null ? "" : a.getString(); |
| for (String g : attr.split("|")) { |
| groups.add(new Dn(g)); |
| } |
| } else if (GroupMode.QUERY == groupMode) { |
| Dn baseDn = new Dn(config.getProperty(CONFIGKEY_LDAP_SEARCH_BASE, "")); |
| String searchQ = String.format(config.getProperty(CONFIGKEY_LDAP_GROUP_QUERY, "%s"), login); |
| |
| 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"); |
| } |
| } |
| cursor.close(); |
| } |
| for (Dn g : groups) { |
| Rdn namer = g.getRdn(); |
| String name = namer.getValue().toString(); |
| if (!Strings.isEmpty(name)) { |
| Organisation o = orgDao.get(name); |
| boolean found = false; |
| if (o == null) { |
| o = new Organisation(); |
| o.setName(name); |
| o = orgDao.update(o, u.getUser_id()); |
| } else { |
| for (Organisation_Users ou : u.getOrganisation_users()) { |
| if (ou.getOrganisation().getName().equals(name)) { |
| found = true; |
| break; |
| } |
| } |
| } |
| if (!found) { |
| u.getOrganisation_users().add(new Organisation_Users(o)); |
| userDao.update(u, u.getUser_id()); |
| log.debug("Going to add user to group:: " + name); |
| } |
| } |
| } |
| 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); |
| } finally { |
| if (conn != null) { |
| try { |
| conn.unBind(); |
| conn.close(); |
| } catch (Exception e) { |
| log.error("Unexpected exception.", e); |
| throw new OmException(e); |
| } |
| } |
| } |
| return u; |
| } |
| } |