org.ballerinalang.auth.ldap.nativeimpl.GetLdapScopesOfUser.java Source code

Java tutorial

Introduction

Here is the source code for org.ballerinalang.auth.ldap.nativeimpl.GetLdapScopesOfUser.java

Source

/*
 * Copyright (c) 2018, WSO2 Inc. (http://www.wso2.org) All Rights Reserved.
 *
 * WSO2 Inc. licenses this file to you under the Apache License,
 * Version 2.0 (the "License"); you may not use this file except
 * in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations
 * under the License.
 */

package org.ballerinalang.auth.ldap.nativeimpl;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.ballerinalang.auth.ldap.CommonLdapConfiguration;
import org.ballerinalang.auth.ldap.LdapConstants;
import org.ballerinalang.auth.ldap.UserStoreException;
import org.ballerinalang.auth.ldap.util.LdapUtils;
import org.ballerinalang.bre.Context;
import org.ballerinalang.bre.bvm.BlockingNativeCallableUnit;
import org.ballerinalang.model.types.BTypes;
import org.ballerinalang.model.types.TypeKind;
import org.ballerinalang.model.values.BMap;
import org.ballerinalang.model.values.BValue;
import org.ballerinalang.model.values.BValueArray;
import org.ballerinalang.natives.annotations.Argument;
import org.ballerinalang.natives.annotations.BallerinaFunction;
import org.ballerinalang.natives.annotations.ReturnType;
import org.ballerinalang.util.exceptions.BallerinaException;

import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.Attribute;
import javax.naming.directory.DirContext;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;
import javax.naming.ldap.LdapName;
import javax.naming.ldap.Rdn;

/**
 * Provides the scopes of a given user.
 *
 * @since 0.983.0
 */
@BallerinaFunction(orgName = "ballerina", packageName = "auth", functionName = "LdapAuthStoreProvider.getScopesOfUser", args = {
        @Argument(name = "username", type = TypeKind.STRING) }, returnType = {
                @ReturnType(type = TypeKind.ARRAY, elementType = TypeKind.STRING) }, isPublic = true)
public class GetLdapScopesOfUser extends BlockingNativeCallableUnit {

    private static final Log LOG = LogFactory.getLog(GetLdapScopesOfUser.class);
    private CommonLdapConfiguration ldapConfiguration;
    private DirContext ldapConnectionContext;

    @Override
    public void execute(Context context) {
        try {
            BMap<String, BValue> authStore = ((BMap<String, BValue>) context.getRefArgument(0));
            LdapUtils.setServiceName((String) authStore.getNativeData(LdapConstants.ENDPOINT_INSTANCE_ID));
            ldapConnectionContext = (DirContext) authStore.getNativeData(LdapConstants.LDAP_CONNECTION_CONTEXT);
            ldapConfiguration = (CommonLdapConfiguration) authStore.getNativeData(LdapConstants.LDAP_CONFIGURATION);
            String userName = context.getStringArgument(0);
            String[] externalRoles = doGetGroupsListOfUser(userName, ldapConfiguration);
            context.setReturnValues(new BValueArray(externalRoles));
        } catch (UserStoreException | NamingException e) {
            context.setReturnValues(new BValueArray(BTypes.typeString));
        } finally {
            LdapUtils.removeServiceName();
        }
    }

    private String[] doGetGroupsListOfUser(String userName, CommonLdapConfiguration ldapAuthConfig)
            throws UserStoreException, NamingException {
        // Get the effective search base
        List<String> searchBase = ldapAuthConfig.getGroupSearchBase();
        return getLDAPGroupsListOfUser(userName, searchBase, ldapAuthConfig);
    }

    private String[] getLDAPGroupsListOfUser(String userName, List<String> searchBase,
            CommonLdapConfiguration ldapAuthConfig) throws UserStoreException, NamingException {
        if (userName == null) {
            throw new BallerinaException("userName value is null.");
        }

        SearchControls searchCtls = new SearchControls();
        searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);
        // Load normal roles with the user
        String searchFilter = ldapAuthConfig.getGroupNameListFilter();
        String roleNameProperty = ldapAuthConfig.getGroupNameAttribute();
        String membershipProperty = ldapAuthConfig.getMembershipAttribute();
        String nameInSpace = this.getNameInSpaceForUserName(userName, ldapConfiguration);

        if (membershipProperty == null || membershipProperty.length() < 1) {
            throw new BallerinaException("membershipAttribute not set in configuration");
        }

        String membershipValue;
        if (nameInSpace != null) {
            LdapName ldn = new LdapName(nameInSpace);
            if (LdapConstants.MEMBER_UID.equals(ldapAuthConfig.getMembershipAttribute())) {
                // membership value of posixGroup is not DN of the user
                List rdns = ldn.getRdns();
                membershipValue = ((Rdn) rdns.get(rdns.size() - 1)).getValue().toString();
            } else {
                membershipValue = escapeLdapNameForFilter(ldn);
            }
        } else {
            return new String[0];
        }

        searchFilter = "(&" + searchFilter + "(" + membershipProperty + "=" + membershipValue + "))";
        String returnedAtts[] = { roleNameProperty };
        searchCtls.setReturningAttributes(returnedAtts);

        if (LOG.isDebugEnabled()) {
            LOG.debug("Reading roles with the membershipProperty Property: " + membershipProperty);
        }

        List<String> list = this.getListOfNames(searchBase, searchFilter, searchCtls, roleNameProperty, false);
        return list.toArray(new String[list.size()]);
    }

    private List<String> getListOfNames(List<String> searchBases, String searchFilter, SearchControls searchCtls,
            String property, boolean appendDn) throws NamingException {
        if (LOG.isDebugEnabled()) {
            LOG.debug("Result for searchBase: " + searchBases + " searchFilter: " + searchFilter + " property:"
                    + property + " appendDN: " + appendDn);
        }

        List<String> names = new ArrayList<String>();
        NamingEnumeration<SearchResult> answer = null;
        try {
            // handle multiple search bases
            for (String searchBase : searchBases) {
                answer = ldapConnectionContext.search(LdapUtils.escapeDNForSearch(searchBase), searchFilter,
                        searchCtls);
                while (answer.hasMoreElements()) {
                    SearchResult searchResult = answer.next();
                    if (searchResult.getAttributes() == null) {
                        continue;
                    }
                    Attribute attr = searchResult.getAttributes().get(property);
                    if (attr == null) {
                        continue;
                    }
                    for (Enumeration vals = attr.getAll(); vals.hasMoreElements();) {
                        String name = (String) vals.nextElement();
                        if (LOG.isDebugEnabled()) {
                            LOG.debug("Found user: " + name);
                        }
                        names.add(name);
                    }
                }

                if (LOG.isDebugEnabled()) {
                    for (String name : names) {
                        LOG.debug("Result  :  " + name);
                    }
                }
            }
        } finally {
            LdapUtils.closeNamingEnumeration(answer);
        }
        return names;
    }

    /**
     * Takes the corresponding name for a given username from LDAP.
     *
     * @param userName Given username
     * @param ldapConfiguration LDAP user store configurations
     * @return Associated name for the given username
     * @throws UserStoreException if there is any exception occurs during the process
     */
    private String getNameInSpaceForUserName(String userName, CommonLdapConfiguration ldapConfiguration)
            throws UserStoreException, NamingException {
        return LdapUtils.getNameInSpaceForUsernameFromLDAP(userName, ldapConfiguration, this.ldapConnectionContext);
    }

    /**
     * This method escapes the special characters in a LdapName according to the ldap filter escaping standards.
     *
     * @param ldn LDAP name
     * @return A String which special characters are escaped
     */
    private String escapeLdapNameForFilter(LdapName ldn) {
        if (ldn == null) {
            if (LOG.isDebugEnabled()) {
                LOG.debug("Received null value to escape special characters. Returning null");
            }
            return null;
        }

        StringBuilder escapedDN = new StringBuilder();
        for (int i = ldn.size() - 1; i > -1; i--) { //escaping the rdns separately and re-constructing the DN
            escapedDN = escapedDN.append(escapeSpecialCharactersForFilterWithStarAsRegex(ldn.get(i)));
            if (i != 0) {
                escapedDN = escapedDN.append(",");
            }
        }
        if (LOG.isDebugEnabled()) {
            LOG.debug("Escaped DN value for filter : " + escapedDN.toString());
        }
        return escapedDN.toString();
    }

    /**
     * Escaping ldap search filter special characters in a string.
     *
     * @param filter LDAP search filter
     * @return A String which special characters are escaped
     */
    private String escapeSpecialCharactersForFilterWithStarAsRegex(String filter) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < filter.length(); i++) {
            char currentChar = filter.charAt(i);
            switch (currentChar) {
            case '\\':
                if (filter.charAt(i + 1) == '*') {
                    sb.append("\\2a");
                    i++;
                    break;
                }
                sb.append("\\5c");
                break;
            case '(':
                sb.append("\\28");
                break;
            case ')':
                sb.append("\\29");
                break;
            case '\u0000':
                sb.append("\\00");
                break;
            default:
                sb.append(currentChar);
            }
        }
        return sb.toString();
    }
}