Package org.geoserver.security.ldap

Source Code of org.geoserver.security.ldap.LDAPRoleService

/* (c) 2014 Open Source Geospatial Foundation - all rights reserved
* (c) 2013 OpenPlans
* This code is licensed under the GPL 2.0 license, available at the root
* application directory.
*/
package org.geoserver.security.ldap;

import java.io.IOException;
import java.util.Collections;
import java.util.HashSet;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.naming.directory.DirContext;

import org.geoserver.security.GeoServerRoleService;
import org.geoserver.security.GeoServerRoleStore;
import org.geoserver.security.config.SecurityNamedServiceConfig;
import org.geoserver.security.event.RoleLoadedListener;
import org.geoserver.security.impl.AbstractGeoServerSecurityService;
import org.geoserver.security.impl.GeoServerRole;
import org.geotools.util.logging.Logging;
import org.springframework.ldap.core.AuthenticatedLdapEntryContextCallback;
import org.springframework.ldap.core.DirContextOperations;
import org.springframework.ldap.core.DistinguishedName;
import org.springframework.ldap.core.LdapEntryIdentification;
import org.springframework.ldap.core.support.LdapContextSource;
import org.springframework.security.ldap.SpringSecurityLdapTemplate;

/**
* LDAP implementation of {@link GeoServerRoleService}
*
* @author "Mauro Bartolomeoli - mauro.bartolomeoli@geo-solutions.it"
*
*/
public class LDAPRoleService extends AbstractGeoServerSecurityService implements GeoServerRoleService {

    private static final SortedSet<GeoServerRole> emptySet = Collections
            .unmodifiableSortedSet(new TreeSet<GeoServerRole>());
   
    private static final SortedSet<String> emptyStringSet = Collections
            .unmodifiableSortedSet(new TreeSet<String>());
   
    private static final Map<String, String> emptyMap = Collections.emptyMap();
   
    static Logger LOGGER = Logging.getLogger("org.geoserver.security.ldap");
    protected Set<RoleLoadedListener> listeners =
        Collections.synchronizedSet(new HashSet<RoleLoadedListener>());

    LdapContextSource ldapContext;
    SpringSecurityLdapTemplate template;
   
    // search base for ldap groups that are to be mapped to GeoServer roles
    String groupSearchBase;
    String user, password;
    /**
     * Standard filter for getting all roles bounded to a user
     */
    String groupSearchFilter = "member={0}";
    // attribute of a group containing the membership info
    String groupMembershipAttribute = "member";
    // regex to extract username from membership info
    Pattern userMembershipPattern = Pattern.compile("^(.*)$");
   
    // attribute of a user containing the username (used if userFilter is defined)
    String userNameAttribute = "uid";
    // regex to extract the username from the user info
    Pattern userNamePattern = Pattern.compile("^(.*)$");
   
    String userFilter = null;
    boolean lookupUserForDn = false;
    /**
     * Standard filter for getting all roles
     */
    String allGroupsSearchFilter = "cn=*";
    /**
     * The ID of the attribute which contains the role name for a group
     */
    private String groupRoleAttribute = "cn";
   
    private String rolePrefix = "ROLE_";
    private boolean convertToUpperCase = true;
   
    private String adminGroup;
    private String groupAdminGroup;
   
    Pattern lookForMembershipAttribute = Pattern.compile(
            "^\\(*([a-z]+)=(.*?)\\{([01])\\}(.*?)\\)*$", Pattern.CASE_INSENSITIVE);

    @Override
    public void initializeFromConfig(SecurityNamedServiceConfig config)
            throws IOException {
        super.initializeFromConfig(config);
        LDAPRoleServiceConfig ldapConfig = (LDAPRoleServiceConfig) config;
   
        ldapContext = LDAPUtils.createLdapContext(ldapConfig);
   
        if (ldapConfig.isBindBeforeGroupSearch()) {
            // authenticate before LDAP searches
            user = ldapConfig.getUser();
            password = ldapConfig.getPassword();
            template = new BindingLdapTemplate(ldapContext);
        } else {
            template = new SpringSecurityLdapTemplate(ldapContext);
        }
   
        this.groupSearchBase = ldapConfig.getGroupSearchBase();
        if (isNotEmpty(ldapConfig.getGroupSearchFilter())) {
            this.groupSearchFilter = ldapConfig.getGroupSearchFilter();
            Matcher m = lookForMembershipAttribute.matcher(groupSearchFilter);
            if (m.matches()) {
                groupMembershipAttribute = m.group(1);
                lookupUserForDn = m.group(3).equals("1");
                userMembershipPattern = Pattern.compile("^"
                        + Pattern.quote(m.group(2)) + "(.*)"
                        + Pattern.quote(m.group(4)) + "$");
            }
        }
        if (isNotEmpty(ldapConfig.getAllGroupsSearchFilter())) {
            this.allGroupsSearchFilter = ldapConfig.getAllGroupsSearchFilter();
        }
        if (isNotEmpty(ldapConfig.getAdminGroup())) {
            this.adminGroup = ldapConfig.getAdminGroup();
        }
        if (isNotEmpty(ldapConfig.getGroupAdminGroup())) {
            this.groupAdminGroup = ldapConfig.getGroupAdminGroup();
        }
        if (isNotEmpty(ldapConfig.getUserFilter())) {
            this.userFilter = ldapConfig.getUserFilter();
            Matcher m = lookForMembershipAttribute.matcher(userFilter);
            if (m.matches()) {
                userNameAttribute = m.group(1);
                userNamePattern = Pattern.compile("^"
                        + Pattern.quote(m.group(2)) + "(.*)"
                        + Pattern.quote(m.group(4)) + "$");
            }
        }
    }

    private boolean isNotEmpty(String property) {
        return property != null
                && !property.isEmpty();
    }

    /**
     * Read only store.
     */
    @Override
    public boolean canCreateStore() {
        return false;
    }

    /**
     * Read only store.
     */
    @Override
    public GeoServerRoleStore createStore() throws IOException {
        return null;
    }

    /**
     * @see org.geoserver.security.GeoServerRoleService#registerRoleLoadedListener(RoleLoadedListener)
     */
    public void registerRoleLoadedListener(RoleLoadedListener listener) {
        listeners.add(listener);
    }


    /**
     * @see org.geoserver.security.GeoServerRoleService#unregisterRoleLoadedListener(RoleLoadedListener)
     */
    public void unregisterRoleLoadedListener(RoleLoadedListener listener) {
        listeners.remove(listener);
    }
   
    /**
     * Roles to group association is not supported
     */
    @Override
    public SortedSet<String> getGroupNamesForRole(GeoServerRole role)
            throws IOException {
        return emptyStringSet;
    }

    @Override
    public SortedSet<String> getUserNamesForRole(final GeoServerRole role)
            throws IOException {
        final SortedSet<String> users = new TreeSet<String>();
       
        authenticateIfNeeded(new AuthenticatedLdapEntryContextCallback() {
           
            @Override
            public void executeWithContext(DirContext ctx,
                    LdapEntryIdentification ldapEntryIdentification) {
                fillUsersForRole(ctx, users, role);
            }
        });

        return Collections.unmodifiableSortedSet(users);
    }

    @Override
    public SortedSet<GeoServerRole> getRolesForUser(final String username)
            throws IOException {
        final SortedSet<GeoServerRole> roles = new TreeSet<GeoServerRole>();
        final Set<String> userDn = new HashSet<String>();
        userDn.add(username);
        if (lookupUserForDn && isNotEmpty(userFilter)) {
            authenticateIfNeeded(new AuthenticatedLdapEntryContextCallback() {
   
                @Override
                public void executeWithContext(DirContext ctx,
                        LdapEntryIdentification ldapEntryIdentification) {
                    try {
                        String dn = LDAPUtils
                                .getLdapTemplateInContext(ctx, template)
                                .searchForSingleEntry("", userFilter,
                                        new String[] { username }).getDn()
                                .toString();
                        userDn.clear();
                        userDn.add(dn);
                    } catch (Exception e) {
                        // not found, let's use username instead
                    }
                }
            });
        }
        authenticateIfNeeded(new AuthenticatedLdapEntryContextCallback() {
   
            @Override
            public void executeWithContext(DirContext ctx,
                    LdapEntryIdentification ldapEntryIdentification) {
                fillRolesForUser(ctx, username, userDn.iterator().next(), roles);
            }
        });
   
        return Collections.unmodifiableSortedSet(roles);
    }

    /**
     * Roles to group association is not supported
     */
    @Override
    public SortedSet<GeoServerRole> getRolesForGroup(String groupname)
            throws IOException {
       
        return emptySet;
    }

    @Override
    public SortedSet<GeoServerRole> getRoles() throws IOException {
        final SortedSet<GeoServerRole> roles = new TreeSet<GeoServerRole>();
       
        authenticateIfNeeded(new AuthenticatedLdapEntryContextCallback() {
           
            @Override
            public void executeWithContext(DirContext ctx,
                    LdapEntryIdentification ldapEntryIdentification) {
                fillAllRoles(ctx, roles);
            }
        });

        return Collections.unmodifiableSortedSet(roles);
    }

    /**
     * Execute authentication, if configured to do so, and then
     * call the given callback on authenticated context, or simply
     * call the given callback if no authentication is needed.
     *
     * @param callback
     */
    private void authenticateIfNeeded(AuthenticatedLdapEntryContextCallback callback) {
        if (user != null && password != null) {
            template.authenticate(DistinguishedName.EMPTY_PATH, user, password,
                    callback);
        } else {
            callback.executeWithContext(null, null);
        }
   
    }

    private void fillAllRoles(DirContext ctx, SortedSet<GeoServerRole> roles) {
        Set<String> roleNames = LDAPUtils.getLdapTemplateInContext(ctx, template)
                .searchForSingleAttributeValues(groupSearchBase,
                        allGroupsSearchFilter, new String[] {}, groupRoleAttribute);
        addRolesToSet(roles, roleNames);
    }
   
    private void fillUsersForRole(DirContext ctx, SortedSet<String> users,
            GeoServerRole role) {
        DirContextOperations roleObj = LDAPUtils.getLdapTemplateInContext(ctx,
                template).searchForSingleEntry(groupSearchBase, "cn={0}",
                new String[] { role.toString() });
        if (roleObj != null) {
            Object[] usernames = roleObj
                    .getObjectAttributes(groupMembershipAttribute);
            if (usernames != null) {
                for (Object username : usernames) {
                    String user = username.toString();
                    Matcher m = userMembershipPattern.matcher(user);
                    if (m.matches()) {
                        user = m.group(1);
                    }
                    if(lookupUserForDn) {
                        user = getUserNameFromMembership(user);
                    }
                    users.add(user);
                }
            }
        }
    }

    private String getUserNameFromMembership(final String user) {
        final Set<String> userName = new HashSet<String>();
        userName.add(user);
        authenticateIfNeeded(new AuthenticatedLdapEntryContextCallback() {
           
            @Override
            public void executeWithContext(DirContext ctx,
                    LdapEntryIdentification ldapEntryIdentification) {
                DirContextOperations obj = (DirContextOperations)LDAPUtils
                        .getLdapTemplateInContext(ctx, template)
                        .lookup(user);
                String name = obj.getObjectAttribute(userNameAttribute).toString();
                Matcher m = userNamePattern.matcher(name);
                if(m.matches()) {
                    name = m.group(1);
                }
                userName.clear();
                userName.add(name);
            }
        });
        return userName.iterator().next();
    }

    private void addRolesToSet(SortedSet<GeoServerRole> roles,
            Set<String> roleNames) {
        for (String roleName : roleNames) {
            try {
                roles.add(createRoleObject(roleName));
            } catch (IOException e) {
                LOGGER.log(Level.SEVERE, "Error adding a new role from LDAP", e);
            }
        }
    }
   
    private void fillRolesForUser(DirContext ctx, String username, String userDn,
            SortedSet<GeoServerRole> roles) {
        Set<String> roleNames = LDAPUtils.getLdapTemplateInContext(ctx, template)
                .searchForSingleAttributeValues(groupSearchBase, groupSearchFilter,
                        new String[] { username, userDn }, groupRoleAttribute);
   
        addRolesToSet(roles, roleNames);
    }

    @Override
    public Map<String, String> getParentMappings() throws IOException {
        return emptyMap;
    }

    @Override
    public GeoServerRole createRoleObject(String role) throws IOException {
        return new GeoServerRole(rolePrefix
                + (convertToUpperCase ? role.toUpperCase() : role));
    }

    @Override
    public GeoServerRole getParentRole(GeoServerRole role) throws IOException {
        return null;
    }

    @Override
    public GeoServerRole getRoleByName(String role) throws IOException {
        if (role.startsWith("ROLE_")) {
            // remove standard role prefix
            role = role.substring(5);
        }
        final String roleName = role;
        final SortedSet<String> roles = new TreeSet<String>();
        authenticateIfNeeded(new AuthenticatedLdapEntryContextCallback() {
   
            @Override
            public void executeWithContext(DirContext ctx,
                    LdapEntryIdentification ldapEntryIdentification) {
                roles.addAll(LDAPUtils.getLdapTemplateInContext(ctx, template)
                        .searchForSingleAttributeValues(groupSearchBase,
                                "cn=" + roleName, new String[] { roleName },
                                groupRoleAttribute));
            }
        });
        if (roles.size() == 1) {
            return createRoleObject(role);
        }
        return null;
    }

    @Override
    public void load() throws IOException {
       
    }

    @Override
    public Properties personalizeRoleParams(String roleName,
            Properties roleParams, String userName, Properties userProps)
            throws IOException {       
        return null;
    }

    @Override
    public GeoServerRole getAdminRole() {
        if(adminGroup == null) {
            return null;
        }
        try {
            return getRoleByName(adminGroup);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public GeoServerRole getGroupAdminRole() {
        if(groupAdminGroup == null) {
            return null;
        }
        try {
            return getRoleByName(groupAdminGroup);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public int getRoleCount() throws IOException {
        return getRoles().size();
    }

}
TOP

Related Classes of org.geoserver.security.ldap.LDAPRoleService

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.