/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* See LICENSE.txt included in this distribution for the specific
* language governing permissions and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at LICENSE.txt.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright (c) 2016, 2022, Oracle and/or its affiliates. All rights reserved.
*/
package opengrok.auth.plugin;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Stream;
import jakarta.servlet.http.HttpServletRequest;
import opengrok.auth.entity.LdapUser;
import opengrok.auth.plugin.entity.User;
import opengrok.auth.plugin.ldap.AbstractLdapProvider;
import opengrok.auth.plugin.ldap.LdapException;
import org.opengrok.indexer.authorization.AuthorizationException;
import org.opengrok.indexer.configuration.Group;
import org.opengrok.indexer.configuration.Project;
/**
* Authorization plug-in to check user's LDAP attribute against whitelist.
*
* This plugin heavily relies on the presence of the {@code LdapUserPlugin} in the stack above it,
* since it is using the Distinguished Name of the {@code LdapUser} to perform the LDAP lookup.
*
* @author Krystof Tulinger
*/
public class LdapAttrPlugin extends AbstractLdapPlugin {
private static final Logger LOGGER = Logger.getLogger(LdapAttrPlugin.class.getName());
/**
* List of configuration names.
*
* attribute
is LDAP attribute to check (mandatory)
* file
whitelist file (mandatory)
* instance
is number of LdapUserInstance
plugin to use (optional)
*
*/
static final String ATTR_PARAM = "attribute";
static final String FILE_PARAM = "file";
static final String INSTANCE_PARAM = "instance";
private static final String SESSION_ALLOWED_PREFIX = "opengrok-ldap-attr-plugin-allowed";
private String sessionAllowed = SESSION_ALLOWED_PREFIX;
private String ldapAttr;
private final Set whitelist = new TreeSet<>();
private Integer ldapUserInstance;
private String filePath;
public LdapAttrPlugin() {
sessionAllowed += "-" + nextId++;
}
// for testing
void load(Map parameters, AbstractLdapProvider provider) {
super.load(provider);
init(parameters);
}
@Override
public void load(Map parameters) {
super.load(parameters);
init(parameters);
}
private void init(Map parameters) {
if ((ldapAttr = (String) parameters.get(ATTR_PARAM)) == null) {
throw new NullPointerException("Missing param [" + ATTR_PARAM + "] in the setup");
}
if ((filePath = (String) parameters.get(FILE_PARAM)) == null) {
throw new NullPointerException("Missing param [" + FILE_PARAM + "] in the setup");
}
String instance = (String) parameters.get(INSTANCE_PARAM);
if (instance != null) {
ldapUserInstance = Integer.parseInt(instance);
}
try (Stream stream = Files.lines(Paths.get(filePath))) {
stream.forEach(whitelist::add);
} catch (IOException e) {
throw new IllegalArgumentException(String.format("Unable to read the file \"%s\"", filePath), e);
}
LOGGER.log(Level.FINE, "LdapAttrPlugin plugin loaded with attr={0}, whitelist={1} ({2} entries), " +
"instance={3}", new Object[]{ldapAttr, filePath, whitelist.size(), ldapUserInstance});
}
@Override
protected boolean sessionExists(HttpServletRequest req) {
return super.sessionExists(req)
&& req.getSession().getAttribute(sessionAllowed) != null;
}
String getSessionAllowedAttrName() {
return sessionAllowed;
}
@Override
public void fillSession(HttpServletRequest req, User user) {
updateSession(req, false);
LdapUser ldapUser = (LdapUser) req.getSession().getAttribute(LdapUserPlugin.getSessionAttrName(ldapUserInstance));
if (ldapUser == null) {
LOGGER.log(Level.WARNING, "cannot get {0} attribute from {1}",
new Object[]{LdapUserPlugin.SESSION_ATTR, user});
return;
}
// Check attributes cached in LDAP user object first, then query LDAP server
// (and if found, cache the result in the LDAP user object).
Set attributeValues = ldapUser.getAttribute(ldapAttr);
if (attributeValues == null) {
Map> records = null;
AbstractLdapProvider ldapProvider = getLdapProvider();
try {
String dn = ldapUser.getDn();
if (dn != null) {
LOGGER.log(Level.FINEST, "searching with dn={0} on {1}",
new Object[]{dn, ldapProvider});
AbstractLdapProvider.LdapSearchResult