1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * See LICENSE.txt included in this distribution for the specific 9 * language governing permissions and limitations under the License. 10 * 11 * When distributing Covered Code, include this CDDL HEADER in each 12 * file and include the License file at LICENSE.txt. 13 * If applicable, add the following below this CDDL HEADER, with the 14 * fields enclosed by brackets "[]" replaced with your own identifying 15 * information: Portions Copyright [yyyy] [name of copyright owner] 16 * 17 * CDDL HEADER END 18 */ 19 20 /* 21 * Copyright (c) 2016, 2019 Oracle and/or its affiliates. All rights reserved. 22 */ 23 package opengrok.auth.plugin; 24 25 import java.util.Map; 26 import java.util.Map.Entry; 27 import java.util.Set; 28 import java.util.TreeMap; 29 import java.util.logging.Level; 30 import java.util.logging.Logger; 31 import java.util.regex.PatternSyntaxException; 32 import javax.servlet.http.HttpServletRequest; 33 import opengrok.auth.entity.LdapUser; 34 import opengrok.auth.plugin.entity.User; 35 import opengrok.auth.plugin.ldap.LdapException; 36 import opengrok.auth.plugin.util.FilterUtil; 37 import org.opengrok.indexer.authorization.AuthorizationException; 38 import org.opengrok.indexer.configuration.Group; 39 import org.opengrok.indexer.configuration.Project; 40 41 import static opengrok.auth.plugin.util.FilterUtil.expandUserFilter; 42 import static opengrok.auth.plugin.util.FilterUtil.replace; 43 44 /** 45 * Authorization plug-in to check if given user matches configured LDAP filter. 46 * 47 * @author Krystof Tulinger 48 */ 49 public class LdapFilterPlugin extends AbstractLdapPlugin { 50 51 private static final Logger LOGGER = Logger.getLogger(LdapFilterPlugin.class.getName()); 52 53 protected static final String FILTER_PARAM = "filter"; 54 protected static final String TRANSFORMS_PARAM = "transforms"; 55 private static final String SESSION_ALLOWED_PREFIX = "opengrok-filter-plugin-allowed"; 56 private static final String INSTANCE = "instance"; 57 private String sessionAllowed = SESSION_ALLOWED_PREFIX; 58 59 /** 60 * List of configuration names. 61 * <ul> 62 * <li><code>filter</code> is LDAP filter used for searching (mandatory)</li> 63 * <li><code>instance</code> is number of <code>LdapUserInstance</code> plugin to use (optional)</li> 64 * <li><code>transforms</code> are comma separated string transforms, where each transform is name:value pair, 65 * allowed values: <code>toLowerCase</code>, <code>toUpperCase</code></li> 66 * </ul> 67 */ 68 private String ldapFilter; 69 private Integer ldapUserInstance; 70 private Map<String, String> transforms; 71 LdapFilterPlugin()72 public LdapFilterPlugin() { 73 sessionAllowed += "-" + nextId++; 74 } 75 76 @Override load(Map<String, Object> parameters)77 public void load(Map<String, Object> parameters) { 78 super.load(parameters); 79 80 if ((ldapFilter = (String) parameters.get(FILTER_PARAM)) == null) { 81 throw new NullPointerException("Missing param [" + FILTER_PARAM + "] in the setup"); 82 } 83 84 String instance = (String) parameters.get(INSTANCE); 85 if (instance != null) { 86 ldapUserInstance = Integer.parseInt(instance); 87 } 88 89 String transformsString = (String) parameters.get(TRANSFORMS_PARAM); 90 if (transformsString != null) { 91 loadTransforms(transformsString); 92 } 93 94 LOGGER.log(Level.FINE, "LdapFilter plugin loaded with filter={0}, instance={1}, transforms={2}", 95 new Object[]{ldapFilter, ldapUserInstance, transforms}); 96 } 97 loadTransforms(String transformsString)98 void loadTransforms(String transformsString) throws NullPointerException { 99 transforms = new TreeMap<>(); 100 String[] transformsArray = transformsString.split(","); 101 for (String elem: transformsArray) { 102 String[] tran = elem.split(":"); 103 transforms.put(tran[0], tran[1]); 104 } 105 FilterUtil.checkTransforms(transforms); 106 } 107 108 @Override sessionExists(HttpServletRequest req)109 protected boolean sessionExists(HttpServletRequest req) { 110 return super.sessionExists(req) 111 && req.getSession().getAttribute(sessionAllowed) != null; 112 } 113 getSessionAttr()114 private String getSessionAttr() { 115 return (LdapUserPlugin.SESSION_ATTR + (ldapUserInstance != null ? ldapUserInstance.toString() : "")); 116 } 117 118 @Override fillSession(HttpServletRequest req, User user)119 public void fillSession(HttpServletRequest req, User user) { 120 LdapUser ldapUser; 121 122 updateSession(req, false); 123 124 if ((ldapUser = (LdapUser) req.getSession().getAttribute(getSessionAttr())) == null) { 125 LOGGER.log(Level.FINER, "failed to get LDAP attribute " + LdapUserPlugin.SESSION_ATTR); 126 return; 127 } 128 129 String expandedFilter = expandFilter(ldapFilter, ldapUser, user); 130 LOGGER.log(Level.FINER, "expanded filter for user {0} and LDAP user {1} into ''{2}''", 131 new Object[]{user, ldapUser, expandedFilter}); 132 try { 133 if ((getLdapProvider().lookupLdapContent(null, expandedFilter)) == null) { 134 LOGGER.log(Level.FINER, "failed to get content for user from LDAP server"); 135 return; 136 } 137 } catch (LdapException ex) { 138 throw new AuthorizationException(ex); 139 } 140 141 updateSession(req, true); 142 } 143 144 /** 145 * Expand {@code LdapUser} / {@code User} object attribute values into the filter. 146 * 147 * @see opengrok.auth.plugin.util.FilterUtil 148 * 149 * Use \% for printing the '%' character. 150 * 151 * @param filter basic filter containing the special values 152 * @param ldapUser user from LDAP 153 * @param user user from the request 154 * @return the filter with replacements 155 */ expandFilter(String filter, LdapUser ldapUser, User user)156 String expandFilter(String filter, LdapUser ldapUser, User user) { 157 158 filter = expandUserFilter(user, filter, transforms); 159 160 for (Entry<String, Set<String>> entry : ldapUser.getAttributes().entrySet()) { 161 if (entry.getValue().size() == 1) { 162 try { 163 filter = replace(filter, entry.getKey(), 164 entry.getValue().iterator().next(), transforms); 165 } catch (PatternSyntaxException ex) { 166 LOGGER.log(Level.WARNING, "The pattern for expanding is not valid", ex); 167 } 168 } 169 } 170 171 filter = filter.replaceAll("\\\\%", "%"); 172 173 return filter; 174 } 175 176 /** 177 * Add a new allowed value into the session. 178 * 179 * @param req the request 180 * @param allowed the new value 181 */ updateSession(HttpServletRequest req, boolean allowed)182 protected void updateSession(HttpServletRequest req, boolean allowed) { 183 req.getSession().setAttribute(sessionAllowed, allowed); 184 } 185 186 @Override checkEntity(HttpServletRequest request, Project project)187 public boolean checkEntity(HttpServletRequest request, Project project) { 188 return ((Boolean) request.getSession().getAttribute(sessionAllowed)); 189 } 190 191 @Override checkEntity(HttpServletRequest request, Group group)192 public boolean checkEntity(HttpServletRequest request, Group group) { 193 return ((Boolean) request.getSession().getAttribute(sessionAllowed)); 194 } 195 } 196