xref: /OpenGrok/plugins/src/main/java/opengrok/auth/plugin/LdapAttrPlugin.java (revision b28a55383a9dd002a5369a73f8ba9c943d6dc214)
1*b28a5538SAdam Hornacek /*
2*b28a5538SAdam Hornacek  * CDDL HEADER START
3*b28a5538SAdam Hornacek  *
4*b28a5538SAdam Hornacek  * The contents of this file are subject to the terms of the
5*b28a5538SAdam Hornacek  * Common Development and Distribution License (the "License").
6*b28a5538SAdam Hornacek  * You may not use this file except in compliance with the License.
7*b28a5538SAdam Hornacek  *
8*b28a5538SAdam Hornacek  * See LICENSE.txt included in this distribution for the specific
9*b28a5538SAdam Hornacek  * language governing permissions and limitations under the License.
10*b28a5538SAdam Hornacek  *
11*b28a5538SAdam Hornacek  * When distributing Covered Code, include this CDDL HEADER in each
12*b28a5538SAdam Hornacek  * file and include the License file at LICENSE.txt.
13*b28a5538SAdam Hornacek  * If applicable, add the following below this CDDL HEADER, with the
14*b28a5538SAdam Hornacek  * fields enclosed by brackets "[]" replaced with your own identifying
15*b28a5538SAdam Hornacek  * information: Portions Copyright [yyyy] [name of copyright owner]
16*b28a5538SAdam Hornacek  *
17*b28a5538SAdam Hornacek  * CDDL HEADER END
18*b28a5538SAdam Hornacek  */
19*b28a5538SAdam Hornacek 
20*b28a5538SAdam Hornacek /*
21*b28a5538SAdam Hornacek  * Copyright (c) 2016, 2018 Oracle and/or its affiliates. All rights reserved.
22*b28a5538SAdam Hornacek  */
23*b28a5538SAdam Hornacek package opengrok.auth.plugin;
24*b28a5538SAdam Hornacek 
25*b28a5538SAdam Hornacek import java.io.IOException;
26*b28a5538SAdam Hornacek import java.nio.file.Files;
27*b28a5538SAdam Hornacek import java.nio.file.Paths;
28*b28a5538SAdam Hornacek import java.util.Map;
29*b28a5538SAdam Hornacek import java.util.Set;
30*b28a5538SAdam Hornacek import java.util.TreeSet;
31*b28a5538SAdam Hornacek import java.util.logging.Level;
32*b28a5538SAdam Hornacek import java.util.logging.Logger;
33*b28a5538SAdam Hornacek import java.util.stream.Stream;
34*b28a5538SAdam Hornacek import javax.servlet.http.HttpServletRequest;
35*b28a5538SAdam Hornacek import opengrok.auth.entity.LdapUser;
36*b28a5538SAdam Hornacek import opengrok.auth.plugin.entity.User;
37*b28a5538SAdam Hornacek import opengrok.auth.plugin.ldap.LdapException;
38*b28a5538SAdam Hornacek import org.opengrok.indexer.authorization.AuthorizationException;
39*b28a5538SAdam Hornacek import org.opengrok.indexer.configuration.Group;
40*b28a5538SAdam Hornacek import org.opengrok.indexer.configuration.Project;
41*b28a5538SAdam Hornacek 
42*b28a5538SAdam Hornacek /**
43*b28a5538SAdam Hornacek  * Authorization plug-in to check user's LDAP attribute against whitelist.
44*b28a5538SAdam Hornacek  *
45*b28a5538SAdam Hornacek  * @author Krystof Tulinger
46*b28a5538SAdam Hornacek  */
47*b28a5538SAdam Hornacek public class LdapAttrPlugin extends AbstractLdapPlugin {
48*b28a5538SAdam Hornacek 
49*b28a5538SAdam Hornacek     private static final Logger LOGGER = Logger.getLogger(LdapAttrPlugin.class.getName());
50*b28a5538SAdam Hornacek 
51*b28a5538SAdam Hornacek 
52*b28a5538SAdam Hornacek     protected static final String ATTR_PARAM = "attribute"; // LDAP attribute name to check
53*b28a5538SAdam Hornacek     protected static final String FILE_PARAM = "file";
54*b28a5538SAdam Hornacek 
55*b28a5538SAdam Hornacek     private static final String SESSION_ALLOWED_PREFIX = "opengrok-attr-plugin-allowed";
56*b28a5538SAdam Hornacek     private String sessionAllowed = SESSION_ALLOWED_PREFIX;
57*b28a5538SAdam Hornacek 
58*b28a5538SAdam Hornacek     private String ldapAttr;
59*b28a5538SAdam Hornacek     private final Set<String> whitelist = new TreeSet<>();
60*b28a5538SAdam Hornacek 
LdapAttrPlugin()61*b28a5538SAdam Hornacek     public LdapAttrPlugin() {
62*b28a5538SAdam Hornacek         sessionAllowed += "-" + nextId++;
63*b28a5538SAdam Hornacek     }
64*b28a5538SAdam Hornacek 
65*b28a5538SAdam Hornacek     @Override
load(Map<String, Object> parameters)66*b28a5538SAdam Hornacek     public void load(Map<String, Object> parameters) {
67*b28a5538SAdam Hornacek         super.load(parameters);
68*b28a5538SAdam Hornacek         String filePath;
69*b28a5538SAdam Hornacek 
70*b28a5538SAdam Hornacek         if ((ldapAttr = (String) parameters.get(ATTR_PARAM)) == null) {
71*b28a5538SAdam Hornacek             throw new NullPointerException("Missing param [" + ATTR_PARAM + "] in the setup");
72*b28a5538SAdam Hornacek         }
73*b28a5538SAdam Hornacek 
74*b28a5538SAdam Hornacek         if ((filePath = (String) parameters.get(FILE_PARAM)) == null) {
75*b28a5538SAdam Hornacek             throw new NullPointerException("Missing param [" + FILE_PARAM + "] in the setup");
76*b28a5538SAdam Hornacek         }
77*b28a5538SAdam Hornacek 
78*b28a5538SAdam Hornacek         try (Stream<String> stream = Files.lines(Paths.get(filePath))) {
79*b28a5538SAdam Hornacek             stream.forEach(whitelist::add);
80*b28a5538SAdam Hornacek         } catch (IOException e) {
81*b28a5538SAdam Hornacek             throw new IllegalArgumentException(String.format("Unable to read the file \"%s\"", filePath), e);
82*b28a5538SAdam Hornacek         }
83*b28a5538SAdam Hornacek     }
84*b28a5538SAdam Hornacek 
85*b28a5538SAdam Hornacek     @Override
sessionExists(HttpServletRequest req)86*b28a5538SAdam Hornacek     protected boolean sessionExists(HttpServletRequest req) {
87*b28a5538SAdam Hornacek         return super.sessionExists(req)
88*b28a5538SAdam Hornacek                 && req.getSession().getAttribute(sessionAllowed) != null;
89*b28a5538SAdam Hornacek     }
90*b28a5538SAdam Hornacek 
91*b28a5538SAdam Hornacek     @SuppressWarnings("unchecked")
92*b28a5538SAdam Hornacek     @Override
fillSession(HttpServletRequest req, User user)93*b28a5538SAdam Hornacek     public void fillSession(HttpServletRequest req, User user) {
94*b28a5538SAdam Hornacek         Boolean sessionAllowed = false;
95*b28a5538SAdam Hornacek         LdapUser ldapUser;
96*b28a5538SAdam Hornacek         Map<String, Set<String>> records;
97*b28a5538SAdam Hornacek         Set<String> attributeValues;
98*b28a5538SAdam Hornacek 
99*b28a5538SAdam Hornacek         updateSession(req, sessionAllowed);
100*b28a5538SAdam Hornacek 
101*b28a5538SAdam Hornacek         if ((ldapUser = (LdapUser) req.getSession().getAttribute(LdapUserPlugin.SESSION_ATTR)) == null) {
102*b28a5538SAdam Hornacek             LOGGER.log(Level.WARNING, "cannot get {0} attribute", LdapUserPlugin.SESSION_ATTR);
103*b28a5538SAdam Hornacek             return;
104*b28a5538SAdam Hornacek         }
105*b28a5538SAdam Hornacek 
106*b28a5538SAdam Hornacek         // Check attributes cached in LDAP user object first, then query LDAP server
107*b28a5538SAdam Hornacek         // (and if found, cache the result in the LDAP user object).
108*b28a5538SAdam Hornacek         attributeValues = ldapUser.getAttribute(ldapAttr);
109*b28a5538SAdam Hornacek         if (attributeValues != null) {
110*b28a5538SAdam Hornacek             sessionAllowed = attributeValues.stream().anyMatch((t) -> whitelist.contains(t));
111*b28a5538SAdam Hornacek         } else {
112*b28a5538SAdam Hornacek             try {
113*b28a5538SAdam Hornacek                 if ((records = getLdapProvider().lookupLdapContent(user, new String[]{ldapAttr})) == null) {
114*b28a5538SAdam Hornacek                     LOGGER.log(Level.WARNING, "cannot lookup attributes {0} for user {1}",
115*b28a5538SAdam Hornacek                         new Object[]{ldapAttr, user});
116*b28a5538SAdam Hornacek                     return;
117*b28a5538SAdam Hornacek                 }
118*b28a5538SAdam Hornacek             } catch (LdapException ex) {
119*b28a5538SAdam Hornacek                 throw new AuthorizationException(ex);
120*b28a5538SAdam Hornacek             }
121*b28a5538SAdam Hornacek 
122*b28a5538SAdam Hornacek             if (records.isEmpty() || (attributeValues = records.get(ldapAttr)) == null) {
123*b28a5538SAdam Hornacek                 LOGGER.log(Level.WARNING, "empty records or attribute values {0} for user {1}",
124*b28a5538SAdam Hornacek                         new Object[]{ldapAttr, user});
125*b28a5538SAdam Hornacek                 return;
126*b28a5538SAdam Hornacek             }
127*b28a5538SAdam Hornacek 
128*b28a5538SAdam Hornacek             ldapUser.setAttribute(ldapAttr, attributeValues);
129*b28a5538SAdam Hornacek             sessionAllowed = attributeValues.stream().anyMatch((t) -> whitelist.contains(t));
130*b28a5538SAdam Hornacek         }
131*b28a5538SAdam Hornacek 
132*b28a5538SAdam Hornacek         updateSession(req, sessionAllowed);
133*b28a5538SAdam Hornacek     }
134*b28a5538SAdam Hornacek 
135*b28a5538SAdam Hornacek     /**
136*b28a5538SAdam Hornacek      * Add a new allowed value into the session.
137*b28a5538SAdam Hornacek      *
138*b28a5538SAdam Hornacek      * @param req the request
139*b28a5538SAdam Hornacek      * @param allowed the new value
140*b28a5538SAdam Hornacek      */
updateSession(HttpServletRequest req, boolean allowed)141*b28a5538SAdam Hornacek     protected void updateSession(HttpServletRequest req, boolean allowed) {
142*b28a5538SAdam Hornacek         req.getSession().setAttribute(sessionAllowed, allowed);
143*b28a5538SAdam Hornacek     }
144*b28a5538SAdam Hornacek 
145*b28a5538SAdam Hornacek     @Override
checkEntity(HttpServletRequest request, Project project)146*b28a5538SAdam Hornacek     public boolean checkEntity(HttpServletRequest request, Project project) {
147*b28a5538SAdam Hornacek         return ((Boolean) request.getSession().getAttribute(sessionAllowed));
148*b28a5538SAdam Hornacek     }
149*b28a5538SAdam Hornacek 
150*b28a5538SAdam Hornacek     @Override
checkEntity(HttpServletRequest request, Group group)151*b28a5538SAdam Hornacek     public boolean checkEntity(HttpServletRequest request, Group group) {
152*b28a5538SAdam Hornacek         return ((Boolean) request.getSession().getAttribute(sessionAllowed));
153*b28a5538SAdam Hornacek     }
154*b28a5538SAdam Hornacek }
155