org.ballerinalang.auth.ldap.util.LdapUtils.java Source code

Java tutorial

Introduction

Here is the source code for org.ballerinalang.auth.ldap.util.LdapUtils.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.util;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.ballerinalang.auth.ldap.CommonLdapConfiguration;
import org.ballerinalang.auth.ldap.UserStoreException;
import org.ballerinalang.util.exceptions.BallerinaException;

import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.naming.CompositeName;
import javax.naming.InvalidNameException;
import javax.naming.Name;
import javax.naming.NamingEnumeration;
import javax.naming.NamingException;
import javax.naming.directory.DirContext;
import javax.naming.directory.SearchControls;
import javax.naming.directory.SearchResult;

/**
 * Utility class for LDAP related common operations.
 *
 * @since 0.983.0
 */
public class LdapUtils {

    private static final Log LOG = LogFactory.getLog(LdapUtils.class);
    private static final Pattern systemVariableIdentifierPattern = Pattern.compile("\\$\\{([^}]*)}");
    private static final ThreadLocal<String> socketFactoryName = new ThreadLocal<>();

    /**
     * Checks whether a given string is null or empty after the trim.
     *
     * @param str String which needs to be check for null or empty
     * @return true if the string is null or empty else false
     */
    public static boolean isNullOrEmptyAfterTrim(String str) {
        return str == null || str.trim().isEmpty();
    }

    /**
     * This is to search user and retrieve ldap name directly from ldap.
     *
     * @param userName          Given username
     * @param ldapConfiguration LDAP user store configurations
     * @param dirContext  Directory naming context
     * @return Associated name for the given username
     * @throws UserStoreException if there is any exception occurs during the process
     * @throws NamingException if there is any exception occurs during the process
     */
    public static String getNameInSpaceForUsernameFromLDAP(String userName,
            CommonLdapConfiguration ldapConfiguration, DirContext dirContext)
            throws UserStoreException, NamingException {
        String userSearchFilter = ldapConfiguration.getUserNameSearchFilter();
        userSearchFilter = userSearchFilter.replace("?", LdapUtils.escapeSpecialCharactersForFilter(userName));
        String searchBase = ldapConfiguration.getUserSearchBase();
        return LdapUtils.getNameInSpaceForUserName(userName, searchBase, userSearchFilter, dirContext);
    }

    /**
     * Searches the corresponding name for a given username from LDAP.
     *
     * @param userName         Given username
     * @param searchBase       LDAP search base
     * @param searchFilter     LDAP search filter
     * @param dirContext Directory naming context
     * @return Associated name for the given username
     * @throws UserStoreException if there is any exception occurs during the process
     * @throws NamingException if there is any exception occurs during the process
     */
    public static String getNameInSpaceForUserName(String userName, String searchBase, String searchFilter,
            DirContext dirContext) throws UserStoreException, NamingException {

        if (userName == null) {
            throw new UserStoreException("userName value is null.");
        }
        String userDN = null;
        NamingEnumeration<SearchResult> answer = null;
        try {
            SearchControls searchCtls = new SearchControls();
            searchCtls.setSearchScope(SearchControls.SUBTREE_SCOPE);
            String[] searchBases = searchBase.split("#");
            for (String base : searchBases) {
                answer = dirContext.search(escapeDNForSearch(base), searchFilter, searchCtls);
                if (!(answer.hasMore())) {
                    continue;
                }
                SearchResult userObj = answer.next();
                if (userObj != null) {
                    //no need to decode since , if decoded the whole string, can't be encoded again
                    //eg CN=Hello\,Ok=test\,test, OU=Industry
                    userDN = userObj.getNameInNamespace();
                    break;
                }
            }
            if (LOG.isDebugEnabled()) {
                LOG.debug("Name in space for " + userName + " is " + userDN);
            }
        } finally {
            LdapUtils.closeNamingEnumeration(answer);
        }
        return userDN;
    }

    /**
     * This method performs the additional level escaping for ldap search. In ldap search / and " characters
     * have to be escaped again.
     *
     * @param dn DN
     * @return composite name
     * @throws InvalidNameException failed to build composite name
     */
    public static Name escapeDNForSearch(String dn) throws InvalidNameException {
        return new CompositeName().add(dn);
    }

    /**
     * Closes the directory context.
     *
     * @param dirContext directory context needs be closed
     * @throws NamingException if a naming exception is encountered
     */
    public static void closeContext(DirContext dirContext) throws NamingException {
        if (dirContext != null) {
            dirContext.close();
        }
    }

    /**
     * Closes the used NamingEnumerations to free up resources.
     *
     * @param namingEnumeration enumeration needs to be closed
     * @throws NamingException if a naming exception is encountered
     */
    public static void closeNamingEnumeration(NamingEnumeration<?> namingEnumeration) throws NamingException {
        if (namingEnumeration != null) {
            namingEnumeration.close();
        }
    }

    /**
     * Escaping ldap search filter special characters in a string.
     *
     * @param dnPartial String to replace special characters of
     * @return String by replacing the special characters
     */
    public static String escapeSpecialCharactersForFilter(String dnPartial) {
        dnPartial = dnPartial.replace("\\*", "*");
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < dnPartial.length(); i++) {
            char currentChar = dnPartial.charAt(i);
            switch (currentChar) {
            case '\\':
                sb.append("\\5c");
                break;
            case '*':
                sb.append("\\2a");
                break;
            case '(':
                sb.append("\\28");
                break;
            case ')':
                sb.append("\\29");
                break;
            case '\u0000':
                sb.append("\\00");
                break;
            default:
                sb.append(currentChar);
            }
        }
        return sb.toString();
    }

    /**
     * Check whether provided url is ldaps or not.
     *
     * @param url LDAP server url
     * @return {@code true} if the url is an ldaps: url.
     */
    public static boolean isLdapsUrl(String url) {
        return (null != url) && (url.length() > 7) && url.substring(0, 8).equalsIgnoreCase("ldaps://");
    }

    /**
     * Replace system property holders in the property values.
     * e.g. Replace ${ballerina.home} with value of the ballerina.home system property.
     *
     * @param value string value to substitute
     * @return String substituted string
     */
    public static String substituteVariables(String value) {
        Matcher matcher = systemVariableIdentifierPattern.matcher(value);
        boolean found = matcher.find();
        if (!found) {
            return value;
        }
        StringBuffer sb = new StringBuffer();
        do {
            String sysPropKey = matcher.group(1);
            String sysPropValue = getSystemVariableValue(sysPropKey, null);
            if (sysPropValue == null || sysPropValue.length() == 0) {
                throw new RuntimeException("System property " + sysPropKey + " is not specified");
            }
            // Due to reported bug under CARBON-14746
            sysPropValue = sysPropValue.replace("\\", "\\\\");
            matcher.appendReplacement(sb, sysPropValue);
        } while (matcher.find());
        matcher.appendTail(sb);
        return sb.toString();
    }

    /**
     * A utility which allows reading variables from the environment or System properties.
     * If the variable in available in the environment as well as a System property, the System property takes
     * precedence.
     *
     * @param variableName System/environment variable name
     * @param defaultValue default value to be returned if the specified system variable is not specified.
     * @return value of the system/environment variable
     */
    private static String getSystemVariableValue(String variableName, String defaultValue) {
        String value;
        if (System.getProperty(variableName) != null) {
            value = System.getProperty(variableName);
        } else if (System.getenv(variableName) != null) {
            value = System.getenv(variableName);
        } else {
            value = defaultValue;
        }
        return value;
    }

    /**
     * Takes instance id of the service from the thread local.
     *
     * @return service instance id.
     */
    public static String getInstanceIdFromThreadLocal() {
        String result = socketFactoryName.get();
        if (result == null) {
            throw new BallerinaException("Cannot infer the ssl context related to the service");
        }
        return result;
    }

    public static void setServiceName(String serviceName) {
        socketFactoryName.set(serviceName);
    }

    public static void removeServiceName() {
        socketFactoryName.remove();
    }
}