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