blob: 3b377c04dc8b7aa8dc3258b8fd467215a7ec0633 [file] [log] [blame]
/*
* 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_DEFAUT_LANG_KEY;
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.Date;
import java.util.Properties;
import java.util.Set;
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.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.Address;
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";
// 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_PICTURE_URI = "ldap_user_picture_uri";
// 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";
public enum AuthType {
NONE
, SEARCHANDBIND
, SIMPLEBIND
}
public enum Provisionning {
NONE
, AUTOUPDATE
, AUTOCREATE
}
@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 user, String passwd, Long domainId) throws OmException {
log.debug("LdapLoginmanagement.doLdapLogin");
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)) {
user = user.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
}
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"), user);
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, user);
conn.bind(userDn, passwd);
}
break;
case NONE:
default:
authenticated = false;
break;
}
u = authenticated ? userDao.getByName(user, Type.ldap) : userDao.login(user, 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);
} else if (u != null && !domainId.equals(u.getDomainId())) {
log.error("User found in OM DB, but domains are differ");
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) {
Set<Right> rights = UserDao.getDefaultRights();
rights.remove(Right.Login);
u = new User();
u.setType(Type.ldap);
u.setRights(rights);
u.setDomainId(domainId);
u.getOrganisation_users().add(new Organisation_Users(orgDao.get(cfgDao.getConfValue("default_domain_id", Long.class, "-1"))));
u.setLogin(user);
u.setAge(new Date());
u.setShowContactDataToContacts(true);
u.setAdresses(new Address());
u.setLanguage_id(cfgDao.getConfValue(CONFIG_DEFAUT_LANG_KEY, Long.class, "1"));
u.setSalutations_id(1L);
}
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_PICTURE_URI, LDAP_KEY_PICTURE_URI);
if (picture == null) {
picture = config.getProperty(CONFIGKEY_LDAP_PICTURE_URI, null);
}
u.setPictureuri(picture);
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);
} finally {
if (conn != null) {
try {
conn.unBind();
conn.close();
} catch (Exception e) {
log.error("Unexpected exception.", e);
throw new OmException(e);
}
}
}
return u;
}
}