com.microsoft.tfs.core.util.UserNameUtil.java Source code

Java tutorial

Introduction

Here is the source code for com.microsoft.tfs.core.util.UserNameUtil.java

Source

// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See License.txt in the repository root.

package com.microsoft.tfs.core.util;

import java.text.MessageFormat;
import java.util.Comparator;
import java.util.concurrent.atomic.AtomicReference;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.microsoft.tfs.core.clients.versioncontrol.VersionControlConstants;
import com.microsoft.tfs.core.httpclient.DefaultNTCredentials;
import com.microsoft.tfs.core.httpclient.auth.NTLMScheme;
import com.microsoft.tfs.core.httpclient.auth.NegotiateScheme;
import com.microsoft.tfs.jni.PlatformMiscUtils;
import com.microsoft.tfs.util.Check;
import com.microsoft.tfs.util.Platform;

/**
 * <p>
 * {@link UserNameUtil} contains static utility methods for working with TFS /
 * Windows user names.
 * </p>
 * <p>
 * Equivalent to: Microsoft.TeamFoundation.Common.UserNameUtil
 * </p>
 *
 * @since TEE-SDK-10.1
 * @threadsafety thread-safe
 */
public class UserNameUtil {
    private static final Log log = LogFactory.getLog(UserNameUtil.class);

    public static final char DOMAIN_SEPARATOR = '\\';

    private static Object currentUserLock = new Object();
    private static boolean currentUserInitialized = false;
    private static String currentUserName;
    private static String currentUserDomain;

    /**
     * Obtains the current user name for this process, if available.
     *
     * @return the current user name, or <code>null</code> if not available
     */
    public static String getCurrentUserName() {
        synchronized (currentUserLock) {
            ensureCurrentUserInitialized();
            return currentUserName;
        }
    }

    /**
     * Obtains the current user domain for this process, if available.
     *
     * @return the current user domain, or <code>null</code> if not available
     */
    public static String getCurrentUserDomain() {
        synchronized (currentUserLock) {
            ensureCurrentUserInitialized();
            return currentUserDomain;
        }
    }

    private static void ensureCurrentUserInitialized() {
        synchronized (currentUserLock) {
            if (currentUserInitialized) {
                return;
            }

            String defaultCredentials = null;

            /*
             * Try to look up our current credentials. (Is only likely to
             * succeed against Unix kerberos implementations.
             */
            if (NegotiateScheme.supportsCredentials(DefaultNTCredentials.class)) {
                defaultCredentials = NegotiateScheme.getDefaultCredentials();
            }

            /* Try again with default credentials. (Is unlikely to succeed.) */
            if (defaultCredentials == null && NTLMScheme.supportsCredentials(DefaultNTCredentials.class)) {
                defaultCredentials = NTLMScheme.getDefaultCredentials();
            }

            if (defaultCredentials != null) {
                /* Native credentials are in the form user@DOMAIN. */
                final int separatorIdx = defaultCredentials.indexOf("@"); //$NON-NLS-1$

                if (separatorIdx >= 0) {
                    currentUserName = defaultCredentials.substring(0, separatorIdx);
                    currentUserDomain = defaultCredentials.substring(separatorIdx + 1);
                } else {
                    currentUserName = defaultCredentials;
                }
            } else if (Platform.isCurrentPlatform(Platform.WINDOWS)) {
                final NTUserInfo info = lookUpNTUserInfo();

                if (info != null) {
                    currentUserName = info.getUserName();
                    currentUserDomain = info.getDomain();
                } else {
                    currentUserDomain = PlatformMiscUtils.getInstance().getEnvironmentVariable("USERDOMAIN"); //$NON-NLS-1$
                    currentUserName = PlatformMiscUtils.getInstance().getEnvironmentVariable("USERNAME"); //$NON-NLS-1$
                }
            }

            if (currentUserName == null) {
                currentUserName = System.getProperty("user.name"); //$NON-NLS-1$
            }

            currentUserInitialized = true;
        }
    }

    /**
     * Compares two usernames / domains for equality. The strings are simply
     * compared case-insensitively.
     *
     * @param username1
     *        the first username to compare (must not be <code>null</code>)
     * @param username2
     *        the second username to compare (must not be <code>null</code>)
     * @return <code>true</code> if the two usernames are equal
     */
    public static boolean equals(final String username1, final String username2) {
        Check.notNull(username1, "username1"); //$NON-NLS-1$
        Check.notNull(username2, "username2"); //$NON-NLS-1$

        return username1.equalsIgnoreCase(username2);
    }

    /**
     * Does a compare of two usernames / domains.
     *
     * @param username1
     *        the first username (must not be <code>null</code>)
     * @param username2
     *        the second username (must not be <code>null</code>)
     * @return the compare value
     */
    public static int compare(final String username1, final String username2) {
        final ParsedUserName p1 = parse(username1);
        final ParsedUserName p2 = parse(username2);

        final int val = p1.getName().compareToIgnoreCase(p2.getName());

        if (val != 0) {
            return val;
        }

        if (p1.getDomain() == null && p2.getDomain() == null) {
            return 0;
        }

        if (p1.getDomain() == null) {
            return -1;
        }

        if (p2.getDomain() == null) {
            return 1;
        }

        return p1.getDomain().compareToIgnoreCase(p2.getDomain());
    }

    /**
     * @return a new {@link Comparator} that uses the
     *         {@link #compare(String, String)} method to sort usernames /
     *         domains (never <code>null</code>)
     */
    public static Comparator<String> newUsernameComparator() {
        return new Comparator<String>() {
            @Override
            public int compare(final String username1, final String username2) {
                return UserNameUtil.compare(username1, username2);
            }
        };
    }

    /**
     * Tests whether the given user name contains a domain.
     *
     * @param userName
     *        the user name to test (must not be <code>null</code>)
     * @return <code>true</code> if the specified user name contains a domain
     */
    public static boolean hasDomain(final String userName) {
        final String domain = getDomain(userName);
        return domain != null && domain.length() > 0;
    }

    /**
     * Gets the domain portion of the specified user name.
     *
     * @param userName
     *        the user name to get the domain for (must not be <code>null</code>
     *        )
     * @return the domain portion or <code>null</code> if there is no domain
     *         portion
     */
    public static String getDomain(final String userName) {
        return parse(userName).getDomain();
    }

    /**
     * Gets the name-only (no domain) portion of the specified user name.
     *
     * @param userName
     *        the user name to get the name for (must not be <code>null</code>)
     * @return the name portion (never <code>null</code>)
     */
    public static String getName(final String userName) {
        return parse(userName).getName();
    }

    public static void getIdentityName(final String identityType, final String displayName, final String attribute,
            final String attribute2, final int uniqueUserID, final AtomicReference<String> outResolvableName,
            final AtomicReference<String> outDisplayableName) {
        // TODO: NYI
    }

    /**
     * Tests whether the given user name is complete (contains a domain
     * separator or equals the authenticated user constant).
     *
     * @param userName
     *        the user name to test (must not be <code>null</code> or empty)
     * @return true if the user name is complete, false if it is not.
     */
    public static boolean isComplete(final String userName) {
        Check.notNullOrEmpty(userName, "userName"); //$NON-NLS-1$

        if (userName.indexOf(DOMAIN_SEPARATOR) < 0) {
            return userName.equals(VersionControlConstants.AUTHENTICATED_USER);
        }

        return true;
    }

    /**
     * Completes the specified user name. Completing a user name works the
     * following way:
     * <ol>
     * <li>If the specified user name contains a domain, the specified user name
     * is returned</li>
     * <li>If the specified user name is equal to the "authenticated user"
     * constant (<code>.</code>) and <code>allowAuthenticatedUserConstant</code>
     * is <code>true</code>, the specified user name is returned</li>
     * <li>If a domain can be determined, the specified username is combined
     * with that domain and the result is returned</li>
     * <li>Otherwise, the specified user name is returned</li>
     * </ol>
     * A domain is determined in the following way:
     * <ol>
     * <li>If the specified <code>relative</code> argument is non-
     * <code>null</code>, the domain is either the <code>relative</code>
     * argument or the domain portion of the <code>relative</code> argument</li>
     * <li>If the current user domain can be determined (
     * {@link #getCurrentUserDomain()}), that is used as the domain</li>
     * <li>Otherwise, no domain can be determined</li>
     * </ol>
     *
     * @param userName
     *        the user name to complete (must not be <code>null</code> or empty)
     * @param relative
     *        the relative argument (can be <code>null</code>)
     * @param allowAuthenticatedUserConstant
     *        <code>true</code> to allow usernames that are equal to the
     *        authenticated user constant
     * @return the completed user name (never <code>null</code>)
     */
    public static String makeComplete(final String userName, final String relative,
            final boolean allowAuthenticatedUserConstant) {
        Check.notNullOrEmpty(userName, "userName"); //$NON-NLS-1$

        if (hasDomain(userName)) {
            return userName;
        }

        if (allowAuthenticatedUserConstant && VersionControlConstants.AUTHENTICATED_USER.equals(userName)) {
            return userName;
        }

        String defaultDomain;

        if (relative == null) {
            defaultDomain = getCurrentUserDomain();
        } else if (relative.indexOf(DOMAIN_SEPARATOR) != -1) {
            defaultDomain = getDomain(relative);
        } else {
            defaultDomain = relative;
        }

        if (defaultDomain == null) {
            return userName;
        }

        return defaultDomain + DOMAIN_SEPARATOR + userName;
    }

    /**
     * Formats the specified user account name and domain name. The result is a
     * full NT4 style user spec (<code>DOMAIN\\user</code>).
     *
     * @param username
     *        the user account portion (must not be <code>null</code>)
     * @param domain
     *        the domain portion (may be <code>null</code>)
     * @return the full NT4 style username
     */
    public static String format(final String username, final String domain) {
        Check.notNull(username, "username"); //$NON-NLS-1$

        final StringBuffer buffer = new StringBuffer();

        if (domain != null && domain.length() > 0) {
            buffer.append(domain);
            buffer.append(DOMAIN_SEPARATOR);
        }

        buffer.append(username);

        return buffer.toString();
    }

    /**
     * Removes the domain portion from the specified user name, if present. If
     * no domain portion is present, the specified user name is returned.
     *
     * @param userName
     *        user name to parse (must not be <code>null</code>)
     * @return the specified user name with any domain portion removed
     */
    public static String removeDomain(final String userName) {
        final ParsedUserName parsedUserName = parse(userName);

        return parsedUserName.getName();
    }

    /**
     * Parses the specified user name, returning a {@link ParsedUserName} object
     * that contains the user and domain portions.
     *
     * @param userName
     *        the user name to parse (must not be <code>null</code>)
     * @return the parsed user name (never <code>null</code>)
     */
    public static ParsedUserName parse(final String userName) {
        Check.notNull(userName, "userName"); //$NON-NLS-1$

        final int ix = userName.indexOf(DOMAIN_SEPARATOR);

        if (ix == -1) {
            return new ParsedUserName(null, userName);
        }

        if (ix == userName.length() - 1) {
            throw new IllegalArgumentException(MessageFormat.format("Invalid user name: [{0}]", userName)); //$NON-NLS-1$
        }

        final String domain = userName.substring(0, ix);
        final String name = userName.substring(ix + 1);

        return new ParsedUserName(domain, name);
    }

    /**
     * Represents a parsed user name's two components: domain and account name.
     */
    public static class ParsedUserName {
        private final String domain;
        private final String name;

        /**
         * Creates a new {@link ParsedUserName}.
         *
         * @param domain
         *        the domain (may be <code>null</code>)
         * @param name
         *        the account name (must not be <code>null</code>)
         */
        public ParsedUserName(final String domain, final String name) {
            Check.notNull(name, "name"); //$NON-NLS-1$

            this.domain = domain;
            this.name = name;
        }

        /**
         * @return the domain portion of the parsed user name, or
         *         <code>null</code> if there is no domain portion
         */
        public String getDomain() {
            return domain;
        }

        /**
         * @return the name portion of the parsed user name, or
         *         <code>null</code> if there is no name portion
         */
        public String getName() {
            return name;
        }
    }

    private static NTUserInfo lookUpNTUserInfo() {
        try {
            final Class c = Class.forName("com.sun.security.auth.module.NTSystem"); //$NON-NLS-1$
            final Object instance = c.newInstance();

            final String userName = (String) c.getMethod("getName", (Class[]) null).invoke(instance, //$NON-NLS-1$
                    (Object[]) null);
            final String domain = (String) c.getMethod("getDomain", (Class[]) null).invoke(instance, //$NON-NLS-1$
                    (Object[]) null);
            final String domainSID = (String) c.getMethod("getDomainSID", (Class[]) null).invoke(instance, //$NON-NLS-1$
                    (Object[]) null);
            final String userSID = (String) c.getMethod("getUserSID", (Class[]) null).invoke(instance, //$NON-NLS-1$
                    (Object[]) null);
            final String primaryGroupID = (String) c.getMethod("getPrimaryGroupID", (Class[]) null).invoke( //$NON-NLS-1$
                    instance, (Object[]) null);
            final String[] groupIDs = (String[]) c.getMethod("getGroupIDs", (Class[]) null).invoke(instance, //$NON-NLS-1$
                    (Object[]) null);
            final long impersonationToken = ((Long) c.getMethod("getImpersonationToken", (Class[]) null).invoke( //$NON-NLS-1$
                    instance, (Object[]) null)).longValue();

            return new NTUserInfo(userName, domain, domainSID, userSID, primaryGroupID, groupIDs,
                    impersonationToken);
        } catch (final Throwable t) {
            if (log.isDebugEnabled()) {
                log.debug("Unable to get NT user info", t); //$NON-NLS-1$
            }

            return null;
        }
    }

    private static class NTUserInfo {
        private final String userName;
        private final String domain;

        public NTUserInfo(final String userName, final String domain, final String domainSID, final String userSID,
                final String primaryGroupID, final String[] groupIDs, final long impersonationToken) {
            this.userName = userName;
            this.domain = domain;
        }

        public String getUserName() {
            return userName;
        }

        public String getDomain() {
            return domain;
        }
    }
}