xref: /OpenGrok/plugins/src/main/java/opengrok/auth/plugin/LdapUserPlugin.java (revision e5567996b7e5d77d9bf3f2fc6e9a0ea2651f44f8)
1b28a5538SAdam Hornacek /*
2b28a5538SAdam Hornacek  * CDDL HEADER START
3b28a5538SAdam Hornacek  *
4b28a5538SAdam Hornacek  * The contents of this file are subject to the terms of the
5b28a5538SAdam Hornacek  * Common Development and Distribution License (the "License").
6b28a5538SAdam Hornacek  * You may not use this file except in compliance with the License.
7b28a5538SAdam Hornacek  *
8b28a5538SAdam Hornacek  * See LICENSE.txt included in this distribution for the specific
9b28a5538SAdam Hornacek  * language governing permissions and limitations under the License.
10b28a5538SAdam Hornacek  *
11b28a5538SAdam Hornacek  * When distributing Covered Code, include this CDDL HEADER in each
12b28a5538SAdam Hornacek  * file and include the License file at LICENSE.txt.
13b28a5538SAdam Hornacek  * If applicable, add the following below this CDDL HEADER, with the
14b28a5538SAdam Hornacek  * fields enclosed by brackets "[]" replaced with your own identifying
15b28a5538SAdam Hornacek  * information: Portions Copyright [yyyy] [name of copyright owner]
16b28a5538SAdam Hornacek  *
17b28a5538SAdam Hornacek  * CDDL HEADER END
18b28a5538SAdam Hornacek  */
19b28a5538SAdam Hornacek 
20b28a5538SAdam Hornacek /*
2153c33ae5SVladimir Kotal  * Copyright (c) 2016, 2019 Oracle and/or its affiliates. All rights reserved.
22b28a5538SAdam Hornacek  */
23b28a5538SAdam Hornacek package opengrok.auth.plugin;
24b28a5538SAdam Hornacek 
2553c33ae5SVladimir Kotal import java.util.Arrays;
26b28a5538SAdam Hornacek import java.util.HashMap;
2753c33ae5SVladimir Kotal import java.util.HashSet;
28b28a5538SAdam Hornacek import java.util.Map;
29b28a5538SAdam Hornacek import java.util.Set;
30b28a5538SAdam Hornacek import java.util.logging.Level;
31b28a5538SAdam Hornacek import java.util.logging.Logger;
32b28a5538SAdam Hornacek import javax.servlet.http.HttpServletRequest;
33b28a5538SAdam Hornacek import opengrok.auth.entity.LdapUser;
34b28a5538SAdam Hornacek import opengrok.auth.plugin.entity.User;
3517deb9edSVladimir Kotal import opengrok.auth.plugin.ldap.AbstractLdapProvider;
36b28a5538SAdam Hornacek import opengrok.auth.plugin.ldap.LdapException;
37b28a5538SAdam Hornacek import org.opengrok.indexer.authorization.AuthorizationException;
38b28a5538SAdam Hornacek import org.opengrok.indexer.configuration.Group;
39b28a5538SAdam Hornacek import org.opengrok.indexer.configuration.Project;
40b28a5538SAdam Hornacek 
41f305899cSVladimir Kotal import static opengrok.auth.plugin.util.FilterUtil.expandUserFilter;
42f305899cSVladimir Kotal 
43b28a5538SAdam Hornacek /**
44b28a5538SAdam Hornacek  * Authorization plug-in to extract user's LDAP attributes.
45b28a5538SAdam Hornacek  * The attributes can be then used by the other LDAP plugins down the stack.
46b28a5538SAdam Hornacek  *
47b28a5538SAdam Hornacek  * @author Krystof Tulinger
48b28a5538SAdam Hornacek  */
49b28a5538SAdam Hornacek public class LdapUserPlugin extends AbstractLdapPlugin {
50b28a5538SAdam Hornacek 
51b28a5538SAdam Hornacek     private static final Logger LOGGER = Logger.getLogger(LdapUserPlugin.class.getName());
52b28a5538SAdam Hornacek 
53bf4d2fd4SVladimir Kotal     static final String SESSION_ATTR = "opengrok-ldap-plugin-user";
54b28a5538SAdam Hornacek 
5553c33ae5SVladimir Kotal     /**
560e7ff20bSVladimir Kotal      * List of configuration names.
5753c33ae5SVladimir Kotal      * <ul>
5853c33ae5SVladimir Kotal      * <li><code>filter</code> is LDAP filter used for searching (optional)</li>
5902df4614SVladimir Kotal      * <li><code>useDN</code> boolean value indicating if User.username should be treated as Distinguished Name (optional, default is false)</li>
6053c33ae5SVladimir Kotal      * <li><code>attributes</code> is comma separated list of LDAP attributes to be produced (mandatory)</li>
6166cf937cSVladimir Kotal      * <li><code>instance</code> integer that can be used to identify instance of this plugin by other LDAP plugins (optional, default empty)</li>
6253c33ae5SVladimir Kotal      * </ul>
6353c33ae5SVladimir Kotal      */
64bf4d2fd4SVladimir Kotal     static final String LDAP_FILTER = "filter";
65bf4d2fd4SVladimir Kotal     static final String ATTRIBUTES = "attributes";
66bf4d2fd4SVladimir Kotal     static final String USE_DN = "useDN";
67bf4d2fd4SVladimir Kotal     static final String INSTANCE = "instance";
68b28a5538SAdam Hornacek 
6953c33ae5SVladimir Kotal     private String ldapFilter;
7053c33ae5SVladimir Kotal     private Boolean useDN;
7153c33ae5SVladimir Kotal     private Set<String> attributes;
7266cf937cSVladimir Kotal     private Integer instance;
73b28a5538SAdam Hornacek 
743c16dad8SVladimir Kotal     // for testing
load(Map<String, Object> parameters, AbstractLdapProvider provider)753c16dad8SVladimir Kotal     void load(Map<String, Object> parameters, AbstractLdapProvider provider) {
763c16dad8SVladimir Kotal         super.load(provider);
773c16dad8SVladimir Kotal 
783c16dad8SVladimir Kotal         init(parameters);
793c16dad8SVladimir Kotal     }
803c16dad8SVladimir Kotal 
81b28a5538SAdam Hornacek     @Override
load(Map<String, Object> parameters)82b28a5538SAdam Hornacek     public void load(Map<String, Object> parameters) {
83b28a5538SAdam Hornacek         super.load(parameters);
84b28a5538SAdam Hornacek 
853c16dad8SVladimir Kotal         init(parameters);
863c16dad8SVladimir Kotal     }
873c16dad8SVladimir Kotal 
init(Map<String, Object> parameters)883c16dad8SVladimir Kotal     private void init(Map<String, Object> parameters) {
89b28a5538SAdam Hornacek         String attributesVal;
90b28a5538SAdam Hornacek         if ((attributesVal = (String) parameters.get(ATTRIBUTES)) == null) {
9153c33ae5SVladimir Kotal             throw new NullPointerException("Missing configuration parameter [" + ATTRIBUTES +
92b28a5538SAdam Hornacek                     "] in the setup");
93b28a5538SAdam Hornacek         }
9453c33ae5SVladimir Kotal         attributes = new HashSet<>(Arrays.asList(attributesVal.split(",")));
95b28a5538SAdam Hornacek 
9653c33ae5SVladimir Kotal         ldapFilter = (String) parameters.get(LDAP_FILTER);
9753c33ae5SVladimir Kotal 
9853c33ae5SVladimir Kotal         if ((useDN = (Boolean) parameters.get(USE_DN)) == null) {
9953c33ae5SVladimir Kotal             useDN = false;
10053c33ae5SVladimir Kotal         }
10153c33ae5SVladimir Kotal 
102e173e36dSVladimir Kotal         String instance_param = (String) parameters.get(INSTANCE);
103e173e36dSVladimir Kotal         if (instance_param != null) {
104e173e36dSVladimir Kotal             instance = Integer.parseInt(instance_param);
105e173e36dSVladimir Kotal         }
10666cf937cSVladimir Kotal 
10753c33ae5SVladimir Kotal         LOGGER.log(Level.FINE, "LdapUser plugin loaded with filter={0}, " +
10866cf937cSVladimir Kotal                         "attributes={1}, useDN={2}, instance={3}",
10966cf937cSVladimir Kotal                 new Object[]{ldapFilter, attributes, useDN, instance});
110b28a5538SAdam Hornacek     }
111b28a5538SAdam Hornacek 
112b28a5538SAdam Hornacek     /**
113b28a5538SAdam Hornacek      * Check if the session exists and contains all necessary fields required by
114b28a5538SAdam Hornacek      * this plug-in.
115b28a5538SAdam Hornacek      *
116b28a5538SAdam Hornacek      * @param req the HTTP request
117b28a5538SAdam Hornacek      * @return true if it does; false otherwise
118b28a5538SAdam Hornacek      */
119b28a5538SAdam Hornacek     @Override
sessionExists(HttpServletRequest req)120b28a5538SAdam Hornacek     protected boolean sessionExists(HttpServletRequest req) {
121b28a5538SAdam Hornacek         return super.sessionExists(req)
122d7630e3aSVladimir Kotal                 && req.getSession().getAttribute(getSessionAttrName()) != null;
123b28a5538SAdam Hornacek     }
124b28a5538SAdam Hornacek 
12553c33ae5SVladimir Kotal     /**
126f305899cSVladimir Kotal      * Expand {@code User} object attribute values into the filter.
12753c33ae5SVladimir Kotal      *
128f305899cSVladimir Kotal      * @see opengrok.auth.plugin.util.FilterUtil
12953c33ae5SVladimir Kotal      *
13053c33ae5SVladimir Kotal      * Use \% for printing the '%' character.
13153c33ae5SVladimir Kotal      *
132750d880bSVladimir Kotal      * @param user User object from the request (created by {@code UserPlugin})
13353c33ae5SVladimir Kotal      * @return replaced result
13453c33ae5SVladimir Kotal      */
expandFilter(User user)135bf4d2fd4SVladimir Kotal     String expandFilter(User user) {
13653c33ae5SVladimir Kotal         String filter = ldapFilter;
137b28a5538SAdam Hornacek 
138f305899cSVladimir Kotal         filter = expandUserFilter(user, filter);
139b28a5538SAdam Hornacek 
14053c33ae5SVladimir Kotal         filter = filter.replaceAll("\\\\%", "%");
14153c33ae5SVladimir Kotal 
14253c33ae5SVladimir Kotal         return filter;
143b28a5538SAdam Hornacek     }
144b28a5538SAdam Hornacek 
145b28a5538SAdam Hornacek     @Override
fillSession(HttpServletRequest req, User user)146b28a5538SAdam Hornacek     public void fillSession(HttpServletRequest req, User user) {
147b28a5538SAdam Hornacek         Map<String, Set<String>> records;
148b28a5538SAdam Hornacek 
149b28a5538SAdam Hornacek         updateSession(req, null);
150b28a5538SAdam Hornacek 
151b28a5538SAdam Hornacek         if (getLdapProvider() == null) {
15253c33ae5SVladimir Kotal             LOGGER.log(Level.WARNING, "cannot get LDAP provider");
153b28a5538SAdam Hornacek             return;
154b28a5538SAdam Hornacek         }
155b28a5538SAdam Hornacek 
15653c33ae5SVladimir Kotal         String expandedFilter = null;
15702df4614SVladimir Kotal         String dn = null;
15802df4614SVladimir Kotal         if (useDN) {
15902df4614SVladimir Kotal             dn = user.getUsername();
16002df4614SVladimir Kotal         }
16153c33ae5SVladimir Kotal         if (ldapFilter != null) {
16253c33ae5SVladimir Kotal             expandedFilter = expandFilter(user);
16353c33ae5SVladimir Kotal         }
164b28a5538SAdam Hornacek         try {
16517deb9edSVladimir Kotal             AbstractLdapProvider.LdapSearchResult<Map<String, Set<String>>> res;
16602df4614SVladimir Kotal             if ((res = getLdapProvider().lookupLdapContent(dn, expandedFilter,
167b91b7b82SVladimir Kotal                     attributes.toArray(new String[0]))) == null) {
168afd2f2f7SVladimir Kotal                 LOGGER.log(Level.WARNING, "failed to get LDAP attributes ''{2}'' for user {0} " +
169*e5567996SVladimir Kotal                                 "with filter ''{1}'' from LDAP provider {3}",
170*e5567996SVladimir Kotal                         new Object[]{user, expandedFilter, attributes, getLdapProvider()});
171b28a5538SAdam Hornacek                 return;
172b28a5538SAdam Hornacek             }
17317deb9edSVladimir Kotal 
17417deb9edSVladimir Kotal             records = res.getAttrs();
17502df4614SVladimir Kotal             if (!useDN) {
17617deb9edSVladimir Kotal                 dn = res.getDN();
17702df4614SVladimir Kotal             }
178b28a5538SAdam Hornacek         } catch (LdapException ex) {
179b28a5538SAdam Hornacek             throw new AuthorizationException(ex);
180b28a5538SAdam Hornacek         }
181b28a5538SAdam Hornacek 
182b28a5538SAdam Hornacek         if (records.isEmpty()) {
183b28a5538SAdam Hornacek             LOGGER.log(Level.WARNING, "LDAP records for user {0} are empty",
184b28a5538SAdam Hornacek                     user);
185b28a5538SAdam Hornacek             return;
186b28a5538SAdam Hornacek         }
187b28a5538SAdam Hornacek 
188b28a5538SAdam Hornacek         for (String attrName : attributes) {
18902df4614SVladimir Kotal             if (!records.containsKey(attrName) || records.get(attrName) == null || records.get(attrName).isEmpty()) {
19053c33ae5SVladimir Kotal                 LOGGER.log(Level.WARNING, "''{0}'' record for user {1} is not present or empty (LDAP provider: {2})",
19153c33ae5SVladimir Kotal                         new Object[]{attrName, user, getLdapProvider()});
192b28a5538SAdam Hornacek             }
193b28a5538SAdam Hornacek         }
194b28a5538SAdam Hornacek 
195b28a5538SAdam Hornacek         Map<String, Set<String>> attrSet = new HashMap<>();
196b28a5538SAdam Hornacek         for (String attrName : attributes) {
197b28a5538SAdam Hornacek             attrSet.put(attrName, records.get(attrName));
198b28a5538SAdam Hornacek         }
199b28a5538SAdam Hornacek 
20002df4614SVladimir Kotal         LOGGER.log(Level.FINEST, "DN for user {0}: {1}", new Object[]{user, dn});
20102df4614SVladimir Kotal         updateSession(req, new LdapUser(dn, attrSet));
202b28a5538SAdam Hornacek     }
203b28a5538SAdam Hornacek 
204b28a5538SAdam Hornacek     /**
205b28a5538SAdam Hornacek      * Add a new user value into the session.
206b28a5538SAdam Hornacek      *
207b28a5538SAdam Hornacek      * @param req the request
208b28a5538SAdam Hornacek      * @param user the new value for user
209b28a5538SAdam Hornacek      */
updateSession(HttpServletRequest req, LdapUser user)210d7630e3aSVladimir Kotal     void updateSession(HttpServletRequest req, LdapUser user) {
211d7630e3aSVladimir Kotal         req.getSession().setAttribute(getSessionAttrName(), user);
21266cf937cSVladimir Kotal     }
21366cf937cSVladimir Kotal 
getSessionAttrName(Integer instance)214d7630e3aSVladimir Kotal     static String getSessionAttrName(Integer instance) {
21566cf937cSVladimir Kotal         return (SESSION_ATTR + (instance != null ? instance.toString() : ""));
216b28a5538SAdam Hornacek     }
217b28a5538SAdam Hornacek 
getSessionAttrName()218d7630e3aSVladimir Kotal     private String getSessionAttrName() {
219d7630e3aSVladimir Kotal         return getSessionAttrName(instance);
220d7630e3aSVladimir Kotal     }
221d7630e3aSVladimir Kotal 
222b28a5538SAdam Hornacek     @Override
checkEntity(HttpServletRequest request, Project project)223b28a5538SAdam Hornacek     public boolean checkEntity(HttpServletRequest request, Project project) {
224d7630e3aSVladimir Kotal         return request.getSession().getAttribute(getSessionAttrName()) != null;
225b28a5538SAdam Hornacek     }
226b28a5538SAdam Hornacek 
227b28a5538SAdam Hornacek     @Override
checkEntity(HttpServletRequest request, Group group)228b28a5538SAdam Hornacek     public boolean checkEntity(HttpServletRequest request, Group group) {
229d7630e3aSVladimir Kotal         return request.getSession().getAttribute(getSessionAttrName()) != null;
230b28a5538SAdam Hornacek     }
231b28a5538SAdam Hornacek }
232