<|start_filename|>src/main/java/com/microsoft/alm/authentication/IAuthentication.java<|end_filename|>
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root.

package com.microsoft.alm.authentication;

import com.microsoft.alm.secret.Credential;

import java.net.URI;
import java.util.concurrent.atomic.AtomicReference;

public interface IAuthentication
{
    void deleteCredentials(final URI targetUri);
    boolean getCredentials(final URI targetUri, final AtomicReference<Credential> credentials);
    boolean setCredentials(final URI targetUri, final Credential credentials);
}


<|start_filename|>src/main/java/com/microsoft/alm/authentication/SecretStore.java<|end_filename|>
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root.

package com.microsoft.alm.authentication;

import com.microsoft.alm.helpers.Debug;
import com.microsoft.alm.helpers.ObjectExtensions;
import com.microsoft.alm.helpers.StringHelper;
import com.microsoft.alm.helpers.Trace;
import com.microsoft.alm.secret.Credential;
import com.microsoft.alm.secret.Secret;
import com.microsoft.alm.secret.Token;

import java.net.URI;
import java.util.concurrent.atomic.AtomicReference;

/**
 * Interface to secure secrets storage which indexes values by target and utilizes the
 * operating system keychain / secrets vault.
 */
public final class SecretStore extends BaseSecureStore implements ICredentialStore, ITokenStore
{
    public SecretStore(final ISecureStore backingStore, final String namespace) { this(backingStore, namespace, null, null, null); }

    /**
     * Creates a new {@link SecretStore} backed by the specified keychain /
     * secrets vault.
     *
     * @param backingStore    The {@link ISecureStore} implementation to use for actually storing secrets.
     * @param namespace       The namespace of the secrets written and read by this store.
     * @param credentialCache (optional) Write-through, read-first cache. Default cache is used if a custom cache is
     *                        not provided.
     * @param tokenCache      (optional) Write-through, read-first cache. Default cache is used if a custom cache is
     *                        not provided.
     * @param getTargetName   The {@link Secret.IUriNameConversion} implementation to use for converting URIs to names.
     */
    public SecretStore(final ISecureStore backingStore, final String namespace, final ICredentialStore credentialCache, final ITokenStore tokenCache, final Secret.IUriNameConversion getTargetName)
    {
        super(backingStore);
        if (StringHelper.isNullOrWhiteSpace(namespace) || StringHelper.indexOfAny(namespace, IllegalCharacters) != -1)
            throw new IllegalArgumentException("The `namespace` parameter is null or invalid.");

        _getTargetName = ObjectExtensions.coalesce(getTargetName, Secret.DefaultUriNameConversion);

        _namespace = namespace;
        _credentialCache = credentialCache != null ? credentialCache : new SecretCache(namespace, _getTargetName);
        _tokenCache = tokenCache != null ? tokenCache : new SecretCache(namespace, _getTargetName);
    }

    private String _namespace;
    private ICredentialStore _credentialCache;
    private ITokenStore _tokenCache;

    private final Secret.IUriNameConversion _getTargetName;

    /**
     * Deletes credentials for target URI from the credential store
     *
     * @param targetUri The URI of the target for which credentials are being deleted
     */
    @Override public void deleteCredentials(final URI targetUri)
    {
        validateTargetUri(targetUri);

        Trace.writeLine("SecretStore::deleteCredentials");

        final String targetName = this.getTargetName(targetUri);

        this.delete(targetName);

        _credentialCache.deleteCredentials(targetUri);
    }

    /**
     * Deletes the {@link Token} for target URI from the token store
     *
     * @param targetUri The URI of the target for which the token is being deleted
     */
    @Override public void deleteToken(final URI targetUri)
    {
        validateTargetUri(targetUri);

        Trace.writeLine("SecretStore::deleteToken");

        final String targetName = this.getTargetName(targetUri);

        this.delete(targetName);
        _tokenCache.deleteToken(targetUri);
    }

    /**
     * Reads credentials for a target URI from the credential store
     *
     * @param targetUri   The URI of the target for which credentials are being read
     * @param credentials The credentials from the store; null if failure
     * @return            True if success; false if failure
     */
    @Override public boolean readCredentials(final URI targetUri, final AtomicReference<Credential> credentials)
    {
        validateTargetUri(targetUri);

        final String targetName = this.getTargetName(targetUri);

        Trace.writeLine("SecretStore::readCredentials");

        if (!_credentialCache.readCredentials(targetUri, credentials))
        {
            credentials.set(this.readCredentials(targetName));
        }

        return credentials.get() != null;
    }

    /**
     * Reads a {@link Token} for a target URI from the token store.
     *
     * @param targetUri The URI of the target for which a token is being read
     * @param token     The {@link Token} from the store; null if failure
     * @return True if success; false if failure
     */
    @Override public boolean readToken(final URI targetUri, final AtomicReference<Token> token)
    {
        validateTargetUri(targetUri);

        Trace.writeLine("SecretStore::readToken");

        token.set(null);

        if (!_tokenCache.readToken(targetUri, token))
        {
            final String targetName = this.getTargetName(targetUri);
            token.set(readToken(targetName));
        }

        return token.get() != null;
    }

    /**
     * Writes credentials for a target URI to the credential store
     *
     * @param targetUri   The URI of the target for which credentials are being stored
     * @param credentials The credentials to be stored
     */
    @Override public void writeCredentials(final URI targetUri, final Credential credentials)
    {
        validateTargetUri(targetUri);
        Credential.validate(credentials);

        Trace.writeLine("SecretStore::writeCredentials");

        final String targetName = this.getTargetName(targetUri);

        this.writeCredential(targetName, credentials);

        _credentialCache.writeCredentials(targetUri, credentials);
    }

    /**
     * Writes a {@link Token} for a target URI to the token store
     *
     * @param targetUri The URI of the target for which a token is being stored
     * @param token     The {@link Token} to be stored
     */
    @Override public void writeToken(final URI targetUri, final Token token)
    {
        validateTargetUri(targetUri);
        Token.validate(token);

        Trace.writeLine("SecretStore::writeToken");

        final String targetName = this.getTargetName(targetUri);

        _tokenCache.writeToken(targetUri, token);

        this.writeToken(targetName, token);
    }

    @Override protected String getTargetName(final URI targetUri)
    {

        Debug.Assert(targetUri != null, "The targetUri parameter is null");

        Trace.writeLine("SecretStore::getTargetName");

        return _getTargetName.convert(targetUri, _namespace);
    }
}


<|start_filename|>src/test/java/com/microsoft/alm/authentication/GlobalTest.java<|end_filename|>
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root.

package com.microsoft.alm.authentication;

import org.junit.Assert;
import org.junit.Test;

public class GlobalTest
{
    @Test
    public void getUserAgent_correctFormat() throws Exception
    {
        final String actual = Global.getUserAgent();

        Assert.assertNotNull(actual);
        Assert.assertTrue(actual.matches("git-credential-manager \\([^;]+; [^;]+; [^;]+\\) [^/]+/.+ git-tools/.+"));
    }
}


<|start_filename|>src/main/java/com/microsoft/alm/authentication/DeviceFlowResponse.java<|end_filename|>
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root.

package com.microsoft.alm.authentication;

import com.microsoft.alm.helpers.PropertyBag;

import java.net.URI;
import java.util.Calendar;

/**
 * Represents the result of requesting device flow authorization from a Device Endpoint.
 *
 * @see "Section 3.1 Client Requests Authorization"
 */
public class DeviceFlowResponse
{
    private final String deviceCode;
    private final String userCode;
    private final URI verificationUri;
    private final int expiresIn;
    private final Calendar expiresAt;
    private final int interval;

    public DeviceFlowResponse(final String deviceCode, final String userCode, final URI verificationUri, final int expiresIn, final int interval)
    {
        this.deviceCode = deviceCode;
        this.userCode = userCode;
        this.verificationUri = verificationUri;
        this.expiresIn = expiresIn;
        this.expiresAt = Calendar.getInstance();
        expiresAt.add(Calendar.SECOND, expiresIn);
        this.interval = interval;
    }

    public static DeviceFlowResponse fromJson(final String jsonText) {
        final PropertyBag bag = PropertyBag.fromJson(jsonText);
        final String deviceCode = (String) bag.get(OAuthParameter.DEVICE_CODE);
        final String userCode = (String) bag.get(OAuthParameter.USER_CODE);
        final String verificationUriString = (String) bag.get(OAuthParameter.VERIFICATION_URI);
        final URI verificationUri = URI.create(verificationUriString);
        final int expiresInSeconds = bag.readOptionalInteger(OAuthParameter.EXPIRES_IN, 600);
        final int intervalInSeconds = bag.readOptionalInteger(OAuthParameter.INTERVAL, 5);

        final DeviceFlowResponse result = new DeviceFlowResponse(deviceCode, userCode, verificationUri, expiresInSeconds, intervalInSeconds);
        return result;
    }

    /**
     * The "Device Verification Code" is defined as
     * "A short-lived token representing an authorization session."
     * in section 2.
     *
     * @return a string that is to be supplied to the token endpoint.
     */
    public String getDeviceCode()
    {
        return deviceCode;
    }

    /**
     * The "End-User Verification Code" is defined as
     * "A short-lived token which the device displays to the end user, is
     * entered by the end-user on the authorization sever, and is thus
     * used to bind the device to the end-user."
     * in section 2.
     *
     * @return a string that the resource owner (end-user)
     *         will type into the user-agent (web browser)
     *         to link their authentication and authorization
     *         with the device.
     */
    public String getUserCode()
    {
        return userCode;
    }

    /**
     * The end-user verification URI on the authorization
     * server.  The URI should be short and easy to remember as end-
     * users will be asked to manually type it into their user-agent.
     *
     * @return the URI that the resource owner (end-user)
     *         will visit with their user-agent (web browser)
     *         to complete the device flow process.
     */
    public URI getVerificationUri()
    {
        return verificationUri;
    }

    /**
     * The duration in seconds of the verification code
     * lifetime.
     *
     * @return the number of seconds the resource owner (end-user)
     *         has to complete the device flow process.
     *
     */
    public int getExpiresIn()
    {
        return expiresIn;
    }

    /**
     * The date and time when the verification code will
     * no longer be valid.
     *
     * @return a Calendar representing the instant when the
     *         resource owner (end-user) will no longer be
     *         able to complete the device flow process.
     *
     */
    public Calendar getExpiresAt()
    {
        return expiresAt;
    }

    /**
     * The minimum amount of time in seconds that the client
     * SHOULD wait between polling requests to the token endpoint.
     *
     * @return the minimum number of seconds the client (application)
     *         waits before polling the token endpoint.
     */
    public int getInterval()
    {
        return interval;
    }
}


<|start_filename|>src/main/java/com/microsoft/alm/authentication/ICredentialStore.java<|end_filename|>
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root.

package com.microsoft.alm.authentication;

import com.microsoft.alm.secret.Credential;

import java.net.URI;
import java.util.concurrent.atomic.AtomicReference;

public interface ICredentialStore
{
    void deleteCredentials(final URI targetUri);
    boolean readCredentials(final URI targetUri, final AtomicReference<Credential> credentials);
    void writeCredentials(final URI targetUri, final Credential credentials);
}


<|start_filename|>src/main/java/com/microsoft/alm/authentication/IGithubAuthority.java<|end_filename|>
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root.

package com.microsoft.alm.authentication;

import com.microsoft.alm.secret.Credential;

import java.net.URI;

interface IGithubAuthority
{
    GithubAuthenticationResult acquireToken(
        final URI targetUri,
        final String username,
        final String password,
        final String authenticationCode,
        final GithubTokenScope scope);

    boolean validateCredentials(final URI targetUri, final Credential credentials);
}


<|start_filename|>src/test/groovy/com/microsoft/alm/authentication/VsoAzureAuthorityTest.groovy<|end_filename|>
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root.

package com.microsoft.alm.authentication;

import groovy.transform.CompileStatic
import org.junit.Test

import static org.junit.Assert.assertEquals

/**
 * A class to test {@see DeviceFlowResponse}.
 */
@CompileStatic
public class VsoAzureAuthorityTest {

    @Test
    public void parseLocationServiceUriFromJson_success() throws Exception {
        final json = """
        {
            "serviceType": "LocationService2",
            "identifier": "852017ac-a960-5000-8464-e3f0aa25b381",
            "displayName": "SPS Location Service",
            "relativeToSetting": "fullyQualified",
            "description": "",
            "serviceOwner": "00000000-0000-0000-0000-000000000000",
            "locationMappings": [{
                "accessMappingMoniker": "HostGuidAccessMapping",
                "location": "https://app.vssps.visualstudio.com/Ca576a7c5-ab56-424e-91ba-66c86e0fff0d/"
            }],
            "toolId": "Framework",
            "parentServiceType": "LocationService2",
            "parentIdentifier": "b6cd35eb-148e-4aad-bbb3-d31576d75958",
            "properties": {}
        }
        """

        final URI locationService = VsoAzureAuthority.parseLocationFromJson(json);
        assertEquals("https://app.vssps.visualstudio.com/Ca576a7c5-ab56-424e-91ba-66c86e0fff0d/", locationService.toString());
    }

    @Test
    public void parseLocationServiceFromAppVsspsJson_success() throws Exception {
        final json = """
        {
            "locationMappings": [{
                "accessMappingMoniker": "HostGuidAccessMapping",
                "location": "https://app.vssps.visualstudio.com/"
            }],
        }
        """

        final URI locationService = VsoAzureAuthority.parseLocationFromJson(json);
        assertEquals("https://app.vssps.visualstudio.com/", locationService.toString());
    }

    @Test
    public void parseLocationServiceFromAppVsspsNoSlashJson_success() throws Exception {
        final json = """
        {
            "locationMappings": [{
                "accessMappingMoniker": "HostGuidAccessMapping",
                "location": "https://app.vssps.visualstudio.com"
            }]
        }
        """

        final URI locationService = VsoAzureAuthority.parseLocationFromJson(json);
        assertEquals("https://app.vssps.visualstudio.com", locationService.toString());
    }
}

<|start_filename|>src/main/java/com/microsoft/alm/authentication/GithubTokenScope.java<|end_filename|>
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root.

package com.microsoft.alm.authentication;

import com.microsoft.alm.helpers.NotImplementedException;
import com.microsoft.alm.helpers.ScopeSet;
import com.microsoft.alm.helpers.StringHelper;
import com.microsoft.alm.secret.TokenScope;

import java.util.Arrays;
import java.util.Iterator;
import java.util.List;

public class GithubTokenScope extends TokenScope
{
    public static final GithubTokenScope None = new GithubTokenScope(StringHelper.Empty);
    /**
     * Create gists
     */
    public static final GithubTokenScope Gist = new GithubTokenScope("gist");
    /**
     * Access notifications
     */
    public static final GithubTokenScope Notifications = new GithubTokenScope("notifications");
    /**
     * Full control of orgs and teams
     */
    public static final GithubTokenScope OrgAdmin = new GithubTokenScope("admin:org");
    /**
     * Read org and team membership
     */
    public static final GithubTokenScope OrgRead = new GithubTokenScope("read:org");
    /**
     * Read and write org and team membership
     */
    public static final GithubTokenScope OrgWrite = new GithubTokenScope("write:org");
    /**
     * Full control of organization hooks
     */
    public static final GithubTokenScope OrgHookAdmin = new GithubTokenScope("admin:org_hook");
    /**
     * Full control of user's public keys
     */
    public static final GithubTokenScope PublicKeyAdmin = new GithubTokenScope("admin:public_key");
    /**
     * Read user's public keys
     */
    public static final GithubTokenScope PublicKeyRead = new GithubTokenScope("read:public_key");
    /**
     * Write user's public keys
     */
    public static final GithubTokenScope PublicKeyWrite = new GithubTokenScope("write:public_key");
    /**
     * Access private repositories
     */
    public static final GithubTokenScope Repo = new GithubTokenScope("repo");
    /**
     * Delete repositories
     */
    public static final GithubTokenScope RepoDelete = new GithubTokenScope("delete_repo");
    /**
     * Access deployment status
     */
    public static final GithubTokenScope RepoDeployment = new GithubTokenScope("repo_deployment");
    /**
     * Access public repositories
     */
    public static final GithubTokenScope RepoPublic = new GithubTokenScope("public_repo");
    /**
     * Access commit status
     */
    public static final GithubTokenScope RepoStatus = new GithubTokenScope("repo:status");
    /**
     * Full control of repository hooks
     */
    public static final GithubTokenScope RepoHookAdmin = new GithubTokenScope("admin:repo_hook");
    /**
     * Read repository hooks
     */
    public static final GithubTokenScope RepoHookRead = new GithubTokenScope("read:repo_hook");
    /**
     * Write repository hooks
     */
    public static final GithubTokenScope RepoHookWrite = new GithubTokenScope("write:repo_hook");
    /**
     * Update all user information
     */
    public static final GithubTokenScope User = new GithubTokenScope("user");
    /**
     * Access user email address (read-only)
     */
    public static final GithubTokenScope UserEmail = new GithubTokenScope("user:email");
    /**
     * Follow and unfollow users
     */
    public static final GithubTokenScope UserFollow = new GithubTokenScope("user:follow");

    private GithubTokenScope(final String value)
    {
        super(value);
    }

    private GithubTokenScope(final String[] values)
    {
        super(values);
    }

    private GithubTokenScope(final ScopeSet set)
    {
        super(set);
    }

    private static final List<GithubTokenScope> values = Arrays.asList
    (
        Gist,
        Notifications,
        OrgAdmin,
        OrgRead,
        OrgWrite,
        OrgHookAdmin,
        PublicKeyAdmin,
        PublicKeyRead,
        PublicKeyWrite,
        Repo,
        RepoDelete,
        RepoDeployment,
        RepoPublic,
        RepoStatus,
        RepoHookAdmin,
        RepoHookRead,
        RepoHookWrite,
        User,
        UserEmail,
        UserFollow
    );

    public static Iterator<GithubTokenScope> enumerateValues()
    {
        return values.iterator();
    }

    public static GithubTokenScope operatorPlus(final GithubTokenScope scope1, final GithubTokenScope scope2)
    {
        throw new NotImplementedException(449506);
    }

    public static GithubTokenScope operatorMinus(final GithubTokenScope scope1, final GithubTokenScope scope2)
    {
        throw new NotImplementedException(449506);
    }

    public static GithubTokenScope operatorOr(final GithubTokenScope scope1, final GithubTokenScope scope2)
    {
        throw new NotImplementedException(449506);
    }

    public static GithubTokenScope operatorAnd(final GithubTokenScope scope1, final GithubTokenScope scope2)
    {
        throw new NotImplementedException(449506);
    }

    public static GithubTokenScope operatorXor(final GithubTokenScope scope1, final GithubTokenScope scope2)
    {
        throw new NotImplementedException(449506);
    }

}


<|start_filename|>src/main/java/com/microsoft/alm/authentication/DeviceFlowImpl.java<|end_filename|>
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root.

package com.microsoft.alm.authentication;

import com.microsoft.alm.helpers.HttpClient;
import com.microsoft.alm.helpers.PropertyBag;
import com.microsoft.alm.helpers.QueryString;
import com.microsoft.alm.helpers.StringContent;
import com.microsoft.alm.helpers.StringHelper;
import com.microsoft.alm.oauth2.useragent.AuthorizationException;
import com.microsoft.alm.secret.TokenPair;

import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URI;
import java.util.Calendar;

public class DeviceFlowImpl implements DeviceFlow
{
    @Override
    public DeviceFlowResponse requestAuthorization(final URI deviceEndpoint, final String clientId, final String scope)
    {
        final QueryString bodyParameters = new QueryString();
        bodyParameters.put(OAuthParameter.RESPONSE_TYPE, OAuthParameter.DEVICE_CODE);
        bodyParameters.put(OAuthParameter.CLIENT_ID, clientId);
        if (!StringHelper.isNullOrEmpty(scope)) {
            bodyParameters.put(OAuthParameter.SCOPE, scope);
        }
        contributeAuthorizationRequestParameters(bodyParameters);
        final StringContent requestBody = StringContent.createUrlEncoded(bodyParameters);

        final HttpClient client = new HttpClient(Global.getUserAgent());
        final String responseText;
        try {
            final HttpURLConnection response = client.post(deviceEndpoint, requestBody);
            final int httpStatus = response.getResponseCode();
            if (httpStatus == HttpURLConnection.HTTP_OK) {
                responseText = HttpClient.readToString(response);
            }
            else {
                final String errorResponseText = HttpClient.readErrorToString(response);
                throw new Error("Device endpoint returned HTTP " + httpStatus + ":\n" + errorResponseText);
            }
        }
        catch (final IOException e) {
            throw new Error(e);
        }

        final DeviceFlowResponse result = buildDeviceFlowResponse(responseText);
        return result;
    }

    /**
     * Allows subclasses to augment the request to the device endpoint with additional parameters.
     *
     * @param bodyParameters the {@link QueryString} to which additional parameters should be added.
     */
    protected void contributeAuthorizationRequestParameters(final QueryString bodyParameters) {
        // do nothing by default
    }

    /**
     * Allows subclasses to construct a subclass of {@link DeviceFlowResponse} with extra metadata, etc.
     *
     * @param responseText the JSON response received from the device endpoint.
     *
     * @return             a {@link DeviceFlowResponse} (or subclass thereof).
     */
    protected DeviceFlowResponse buildDeviceFlowResponse(final String responseText) {
        return DeviceFlowResponse.fromJson(responseText);
    }

    @Override
    public TokenPair requestToken(final URI tokenEndpoint, final String clientId, final DeviceFlowResponse deviceFlowResponse) throws AuthorizationException
    {
        final QueryString bodyParameters = new QueryString();
        bodyParameters.put(OAuthParameter.GRANT_TYPE, OAuthParameter.DEVICE_CODE);
        bodyParameters.put(OAuthParameter.CODE, deviceFlowResponse.getDeviceCode());
        bodyParameters.put(OAuthParameter.CLIENT_ID, clientId);
        contributeTokenRequestParameters(bodyParameters);
        final StringContent requestBody = StringContent.createUrlEncoded(bodyParameters);

        final int intervalSeconds = deviceFlowResponse.getInterval();
        int intervalMilliseconds = intervalSeconds * 1000;
        final HttpClient client = new HttpClient(Global.getUserAgent());
        String responseText = null;
        final Calendar expiresAt = deviceFlowResponse.getExpiresAt();
        while (Calendar.getInstance().compareTo(expiresAt) <= 0) {
            try {
                final HttpURLConnection response = client.post(tokenEndpoint, requestBody);
                final int httpStatus = response.getResponseCode();
                if (httpStatus == HttpURLConnection.HTTP_OK) {
                    responseText = HttpClient.readToString(response);
                    break;
                }
                else {
                    final String errorResponseText = HttpClient.readErrorToString(response);
                    if (httpStatus == HttpURLConnection.HTTP_BAD_REQUEST) {
                        final PropertyBag bag = PropertyBag.fromJson(errorResponseText);
                        final String errorCode = bag.readOptionalString(OAuthParameter.ERROR_CODE, "unknown_error");
                        if (OAuthParameter.ERROR_AUTHORIZATION_PENDING.equals(errorCode)) {
                            try {
                                Thread.sleep(intervalMilliseconds);
                            } catch (final InterruptedException e) {
                                throw new Error(e);
                            }
                            continue;
                        }
                        else if (OAuthParameter.ERROR_SLOW_DOWN.equals(errorCode)) {
                            intervalMilliseconds *= 2;
                            try {
                                Thread.sleep(intervalMilliseconds);
                            } catch (final InterruptedException e) {
                                throw new Error(e);
                            }
                            continue;
                        }
                        final String errorDescription = bag.readOptionalString(OAuthParameter.ERROR_DESCRIPTION, null);
                        final String errorUriString = bag.readOptionalString(OAuthParameter.ERROR_URI, null);
                        final URI errorUri = errorUriString == null ? null : URI.create(errorUriString);
                        throw new AuthorizationException(errorCode, errorDescription, errorUri, null);
                    }
                    else {
                        throw new Error("Token endpoint returned HTTP " + httpStatus + ":\n" + errorResponseText);
                    }
                }
            }
            catch (final IOException e) {
                throw new Error(e);
            }
        }

        if (responseText == null) {
            throw new AuthorizationException("code_expired", "The verification code expired.", null, null);
        }
        final TokenPair tokenPair = buildTokenPair(responseText);
        return tokenPair;
    }

    /**
     * Allows subclasses to augment the request to the token endpoint with additional parameters.
     *
     * @param bodyParameters the {@link QueryString} to which additional parameters should be added.
     */
    protected void contributeTokenRequestParameters(final QueryString bodyParameters) {
        // do nothing by default
    }

    /**
     * Allows subclasses to construct a subclass of {@link TokenPair} with extra metadata, etc.
     *
     * @param responseText the JSON response received from the token endpoint.
     *
     * @return             a {@link TokenPair} (or subclass thereof).
     */
    protected TokenPair buildTokenPair(final String responseText) {
        final TokenPair tokenPair = new TokenPair(responseText);
        return tokenPair;
    }
}


<|start_filename|>src/main/java/com/microsoft/alm/authentication/IVsoAuthority.java<|end_filename|>
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root.

package com.microsoft.alm.authentication;

import com.microsoft.alm.secret.Credential;
import com.microsoft.alm.secret.Token;
import com.microsoft.alm.secret.VsoTokenScope;

import java.net.URI;

interface IVsoAuthority extends IAzureAuthority
{
    Token generatePersonalAccessToken(final URI targetUri, final Token accessToken, final VsoTokenScope tokenScope, final boolean requireCompactToken);
    boolean validateCredentials(final URI targetUri, final Credential credentials);
    boolean validateToken(final URI targetUri, final Token token);
}


<|start_filename|>src/main/java/com/microsoft/alm/authentication/SecretCache.java<|end_filename|>
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root.

package com.microsoft.alm.authentication;

import com.microsoft.alm.helpers.Debug;
import com.microsoft.alm.helpers.ObjectExtensions;
import com.microsoft.alm.helpers.StringHelper;
import com.microsoft.alm.helpers.Trace;
import com.microsoft.alm.secret.Credential;
import com.microsoft.alm.secret.Secret;
import com.microsoft.alm.secret.Token;

import java.net.URI;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.atomic.AtomicReference;

final class SecretCache implements ICredentialStore, ITokenStore
{
    static
    {
        _cache = new TreeMap<String, Secret>(String.CASE_INSENSITIVE_ORDER);
    }

    private static final Map<String, Secret> _cache;

    public SecretCache(final String namespace) { this(namespace,  null); }

    public SecretCache(final String namespace, final Secret.IUriNameConversion getTargetName)
    {
        Debug.Assert(!StringHelper.isNullOrWhiteSpace(namespace), "The namespace parameter is null or invalid");

        _namespace = namespace;
        _getTargetName = ObjectExtensions.coalesce(getTargetName, Secret.DefaultUriNameConversion);
    }

    private final String _namespace;
    private final Secret.IUriNameConversion _getTargetName;

    /**
     * Deletes a credential from the cache.
     *
     * @param targetUri The URI of the target for which credentials are being deleted
     */
    public void deleteCredentials(final URI targetUri)
    {
        BaseSecureStore.validateTargetUri(targetUri);

        Trace.writeLine("SecretCache::deleteCredentials");

        final String targetName = this.getTargetName(targetUri);

        synchronized (_cache)
        {
            if (_cache.containsKey(targetName) && _cache.get(targetName) instanceof Credential)
            {
                _cache.remove(targetName);
            }
        }
    }

    /**
     * Deletes a token from the cache.
     *
     * @param targetUri The key which to find and delete the token with.
     */
    public void deleteToken(final URI targetUri)
    {
        BaseSecureStore.validateTargetUri(targetUri);

        Trace.writeLine("SecretCache::deleteToken");

        final String targetName = this.getTargetName(targetUri);

        synchronized (_cache)
        {
            if (_cache.containsKey(targetName) && _cache.get(targetName) instanceof Token)
            {
                _cache.remove(targetName);
            }
        }
    }

    /**
     * Reads credentials for a target URI from the credential store
     *
     * @param targetUri   The URI of the target for which credentials are being read
     * @param credentials The credentials from the store; null if failure
     * @return            True if success; false if failure
     */
    public boolean readCredentials(final URI targetUri, final AtomicReference<Credential> credentials)
    {
        BaseSecureStore.validateTargetUri(targetUri);

        Trace.writeLine("SecretCache::readCredentials");

        final String targetName = this.getTargetName(targetUri);

        synchronized (_cache)
        {
            if (_cache.containsKey(targetName) && _cache.get(targetName) instanceof Credential)
            {
                credentials.set((Credential) _cache.get(targetName));
            }
            else
            {
                credentials.set(null);
            }
        }

        return credentials.get() != null;
    }

    /**
     * Gets a token from the cache.
     *
     * @param targetUri The key which to find the token.
     * @param token     The token if successful; otherwise null.
     * @return          True if successful; false otherwise.
     */
    public boolean readToken(final URI targetUri, final AtomicReference<Token> token)
    {
        BaseSecureStore.validateTargetUri(targetUri);

        Trace.writeLine("SecretCache::readToken");

        final String targetName = this.getTargetName(targetUri);

        synchronized (_cache)
        {
            if (_cache.containsKey(targetName) && _cache.get(targetName) instanceof Token)
            {
                token.set((Token) _cache.get(targetName));
            }
            else
            {
                token.set(null);
            }
        }

        return token.get() != null;
    }

    /**
     * Writes credentials for a target URI to the credential store
     *
     * @param targetUri   The URI of the target for which credentials are being stored
     * @param credentials The credentials to be stored
     */
    public void writeCredentials(final URI targetUri, final Credential credentials)
    {
        BaseSecureStore.validateTargetUri(targetUri);
        Credential.validate(credentials);

        Trace.writeLine("SecretCache::writeCredentials");

        final String targetName = this.getTargetName(targetUri);

        synchronized (_cache)
        {
            _cache.put(targetName, credentials);
        }
    }

    /**
     * Writes a token to the cache.
     *
     * @param targetUri The key which to index the token by.
     * @param token     The token to write to the cache.
     */
    public void writeToken(final URI targetUri, final Token token)
    {
        BaseSecureStore.validateTargetUri(targetUri);
        Token.validate(token);

        Trace.writeLine("SecretCache::writeToken");

        final String targetName = this.getTargetName(targetUri);

        synchronized (_cache)
        {
            _cache.put(targetName, token);
        }
    }

    /**
     * Formats a TargetName string based on the TargetUri based on the format started by git-credential-winstore
     *
     * @param targetUri uri of the target
     * @return Properly formatted TargetName string
     */
    private String getTargetName(final URI targetUri)
    {
        Debug.Assert(targetUri != null, "The targetUri parameter is null");

        Trace.writeLine("SecretCache::_getTargetName");

        return _getTargetName.convert(targetUri, _namespace);
    }
}


<|start_filename|>src/main/java/com/microsoft/alm/gitcredentialmanager/IComponentFactory.java<|end_filename|>
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root.

package com.microsoft.alm.gitcredentialmanager;

import com.microsoft.alm.authentication.Configuration;
import com.microsoft.alm.authentication.IAuthentication;
import com.microsoft.alm.authentication.ISecureStore;

import java.io.IOException;

public interface IComponentFactory
{
    IAuthentication createAuthentication(final OperationArguments operationArguments, final ISecureStore secureStore);
    Configuration createConfiguration() throws IOException;
    ISecureStore createSecureStore(final OperationArguments operationArguments);
}


<|start_filename|>src/main/java/com/microsoft/alm/authentication/ITokenStore.java<|end_filename|>
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root.

package com.microsoft.alm.authentication;

import com.microsoft.alm.secret.Token;

import java.net.URI;
import java.util.concurrent.atomic.AtomicReference;

public interface ITokenStore
{
    /**
     * Deletes a {@link Token} from the underlying storage.
     *
     * @param targetUri The key identifying which token is being deleted.
     */
    void deleteToken(final URI targetUri);

    /**
     * Reads a {@link Token} from the underlying storage.
     *
     * @param targetUri The key identifying which token to read.
     * @param token A {@link Token} if successful; otherwise null.
     * @return True if successful; otherwise false.
     */
    boolean readToken(final URI targetUri, final AtomicReference<Token> token);

    /**
     * Writes a {@link Token} to the underlying storage.
     *
     * @param targetUri Unique identifier for the token, used when reading back from storage.
     * @param token The {@link Token} to be written.
     */
    void writeToken(final URI targetUri, final Token token);
}


<|start_filename|>src/main/java/com/microsoft/alm/authentication/VsoAadAuthentication.java<|end_filename|>
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root.

package com.microsoft.alm.authentication;

import com.microsoft.alm.helpers.Action;
import com.microsoft.alm.helpers.Guid;
import com.microsoft.alm.helpers.NotImplementedException;
import com.microsoft.alm.helpers.Trace;
import com.microsoft.alm.secret.Credential;
import com.microsoft.alm.secret.TokenPair;
import com.microsoft.alm.secret.VsoTokenScope;

import java.net.URI;
import java.util.UUID;

/**
 * Facilitates Azure Directory authentication.
 */
public final class VsoAadAuthentication extends BaseVsoAuthentication implements IVsoAadAuthentication
{
    /**
     * The default authority host for all Azure Directory authentication
     */
    public static final String DefaultAuthorityHost = "https://management.core.windows.net/";

    /**
     * @param tenantId                 <p>The unique identifier for the responsible Azure tenant.</p>
     *                                 <p>Use {@link BaseVsoAuthentication}
     *                                 to detect the tenant identity and create the authentication object.</p>
     * @param tokenScope               The scope of all access tokens acquired by the authority.
     * @param personalAccessTokenStore The secure secret store for storing any personal
     *                                 access tokens acquired.
     * @param adaRefreshTokenStore     The secure secret store for storing any Azure tokens
     *                                 acquired
     */
    public VsoAadAuthentication(
            final UUID tenantId,
            final VsoTokenScope tokenScope,
            final ICredentialStore personalAccessTokenStore,
            final ITokenStore adaRefreshTokenStore)
    {
        super(tokenScope,
              personalAccessTokenStore,
              adaRefreshTokenStore);
        if (tenantId == null || tenantId.equals(Guid.Empty))
        {
            this.VsoAuthority = new VsoAzureAuthority(DefaultAuthorityHost);
        }
        else
        {
            // create an authority host url in the format of https://login.microsoft.com/12345678-9ABC-DEF0-1234-56789ABCDEF0
            String authorityHost = AzureAuthority.getAuthorityUrl(tenantId);
            this.VsoAuthority = new VsoAzureAuthority(authorityHost);
        }
    }

    /**
     * Test constructor which allows for using fake credential stores
     */
    VsoAadAuthentication(
            final ICredentialStore personalAccessTokenStore,
            final ITokenStore adaRefreshTokenStore,
            final ITokenStore vsoIdeTokenCache,
            final IVsoAuthority vsoAuthority)
    {
        super(personalAccessTokenStore,
              adaRefreshTokenStore,
              vsoIdeTokenCache,
              vsoAuthority);
    }

    /**
     * <p>Creates an interactive logon session, using ADAL secure browser GUI, which
     * enables users to authenticate with the Azure tenant and acquire the necessary access
     * tokens to exchange for a VSO personal access token.</p>
     * <p>Tokens acquired are stored in the secure secret stores provided during
     * initialization.</p>
     *
     * @param targetUri           The unique identifier for the resource for which access is to
     *                            be acquired.
     * @param requestCompactToken <p>Requests a compact format personal access token; otherwise requests a standard
     *                            personal access token.</p>
     *                            <p>Compact tokens are necessary for clients which have restrictions on the size of
     *                            the basic authentication header which they can create (example: Git).</p>
     * @return                    True if a authentication and personal access token acquisition was successful; otherwise false.
     */
    public boolean interactiveLogon(final URI targetUri, final boolean requestCompactToken)
    {
        BaseSecureStore.validateTargetUri(targetUri);

        Trace.writeLine("VsoAadAuthentication::interactiveLogon");

        TokenPair tokens;
        if ((tokens = this.VsoAuthority.acquireToken(targetUri, this.ClientId, this.Resource, RedirectUri, (String) null)) != null)
        {
            Trace.writeLine("   token acquisition succeeded.");

            this.storeRefreshToken(targetUri, tokens.RefreshToken);

            return this.generatePersonalAccessToken(targetUri, tokens.AccessToken,  requestCompactToken);
        }

        Trace.writeLine("   interactive logon failed");
        return false;
    }

    /**
     * <p>Uses credentials to authenticate with the Azure tenant and acquire the necessary
     * access tokens to exchange for a VSO personal access token.</p>
     * <p>Tokens acquired are stored in the secure secret stores provided during
     * initialization.</p>
     *
     * @param targetUri           The unique identifier for the resource for which access is to
     *                            be acquired.
     * @param credentials         The credentials required to meet the criteria of the Azure
     *                            tenant authentication challenge (i.e. username + password).
     * @param requestCompactToken <p>Requests a compact format personal access token; otherwise requests a standard
     *                            personal access token.</p>
     *                            <p>Compact tokens are necessary for clients which have restrictions on the size of
     *                            the basic authentication header which they can create (example: Git).</p>
     * @return                    True if authentication and personal access token acquisition was successful; otherwise false.
     */
    public boolean noninteractiveLogonWithCredentials(final URI targetUri, final Credential credentials, final boolean requestCompactToken)
    {
        throw new NotImplementedException(449288);
    }

    /**
     * <p>Uses Active Directory Federation Services to authenticate with the Azure tenant
     * non-interactively and acquire the necessary access tokens to exchange for a VSO personal
     * access token.</p>
     * <p>Tokens acquired are stored in the secure secret stores provided during
     * initialization.</p>
     *
     * @param targetUri           The unique identifier for the resource for which access is to
     *                            be acquired.
     * @param requestCompactToken <p>Requests a compact format personal access token; otherwise requests a standard
     *                            personal access token.</p>
     *                            <p>Compact tokens are necessary for clients which have restrictions on the size of
     *                            the basic authentication header which they can create (example: Git).</p>
     * @return                    True if authentication and personal access token acquisition was successful; otherwise false.
     */
    public boolean noninteractiveLogon(final URI targetUri, final boolean requestCompactToken)
    {
        throw new NotImplementedException(449285);
    }

    public boolean deviceLogon(final URI targetUri, final boolean requestCompactToken, final Action<DeviceFlowResponse> callback)
    {
        BaseSecureStore.validateTargetUri(targetUri);

        Trace.writeLine("VsoAadAuthentication::deviceLogon");

        TokenPair tokens;
        if ((tokens = this.VsoAuthority.acquireToken(targetUri, this.ClientId, this.Resource, RedirectUri, callback)) != null)
        {
            Trace.writeLine("   token successfully acquired.");

            this.storeRefreshToken(targetUri, tokens.RefreshToken);

            return this.generatePersonalAccessToken(targetUri, tokens.AccessToken, requestCompactToken);
        }

        Trace.writeLine("   failed to acquire token.");
        return false;
    }

    /**
     * Sets credentials for future use with this authentication object.
     *
     * Not supported.
     * @param targetUri   The uniform resource indicator of the resource access tokens are being set for.
     * @param credentials The credentials being set.
     * @return            True if successful; false otherwise.
     */
    @Override public boolean setCredentials(final URI targetUri, final Credential credentials)
    {
        BaseSecureStore.validateTargetUri(targetUri);
        Credential.validate(credentials);

        Trace.writeLine("VsoMsaAuthentication::SetCredentials");
        Trace.writeLine("   setting AAD credentials is not supported");

        // does nothing with VSO AAD backed accounts
        return false;

    }
}


<|start_filename|>src/main/java/com/microsoft/alm/authentication/SecretStoreAdapter.java<|end_filename|>
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root.

package com.microsoft.alm.authentication;

import com.microsoft.alm.secret.Credential;
import com.microsoft.alm.secret.Token;

public class SecretStoreAdapter implements ISecureStore
{
    private final com.microsoft.alm.storage.SecretStore<Token> tokenSecretStore;
    private final com.microsoft.alm.storage.SecretStore<Credential> credentialSecretStore;

    public SecretStoreAdapter(final com.microsoft.alm.storage.SecretStore<Token> tokenSecretStore, final com.microsoft.alm.storage.SecretStore<Credential> credentialSecretStore)
    {
        if (tokenSecretStore == null)
            throw new IllegalArgumentException("tokenSecretStore can't be null");
        if (credentialSecretStore == null)
            throw new IllegalArgumentException("credentialSecretStore can't be null");

        this.tokenSecretStore = tokenSecretStore;
        this.credentialSecretStore = credentialSecretStore;
    }

    @Override
    public void delete(final String targetName)
    {
        if (tokenSecretStore.get(targetName) != null)
        {
            tokenSecretStore.delete(targetName);
        }
        else if (credentialSecretStore.get(targetName) != null)
        {
            credentialSecretStore.delete(targetName);
        }
    }

    @Override
    public Credential readCredentials(final String targetName)
    {
        return credentialSecretStore.get(targetName);
    }

    @Override
    public Token readToken(final String targetName)
    {
        return tokenSecretStore.get(targetName);
    }

    @Override
    public void writeCredential(final String targetName, final Credential credentials)
    {
        credentialSecretStore.add(targetName, credentials);
    }

    @Override
    public void writeToken(final String targetName, final Token token)
    {
        tokenSecretStore.add(targetName, token);
    }
}


<|start_filename|>src/main/java/com/microsoft/alm/gitcredentialmanager/Interactivity.java<|end_filename|>
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root.

package com.microsoft.alm.gitcredentialmanager;

/**
 * Level of interactivity allowed and enabled.
 */
enum Interactivity
{
    /**
     * Present an interactive logon prompt when necessary, otherwise use cached credentials
     */
    Auto,
    /**
     * Always present an interactive logon prompt regardless if cached credentials exist
     */
    Always,
    /**
     * Never present an present an interactive logon prompt, fail without cached credentials
     */
    Never
}


<|start_filename|>src/test/java/com/microsoft/alm/helpers/StringHelperTest.java<|end_filename|>
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root.

package com.microsoft.alm.helpers;

import org.junit.Assert;
import org.junit.Test;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;

public class StringHelperTest
{
    public static void assertLinesEqual(final String expected, final String actual) throws IOException
    {
        final StringReader expectedSr = new StringReader(expected);
        final BufferedReader expectedBr = new BufferedReader(expectedSr);
        final StringReader actualSr = new StringReader(actual);
        final BufferedReader actualBr = new BufferedReader(actualSr);

        String expectedLine;
        String actualLine;
        while ((expectedLine = expectedBr.readLine()) != null)
        {
            if ((actualLine = actualBr.readLine()) != null)
            {
                Assert.assertEquals(expectedLine, actualLine);
            }
            else
            {
                Assert.fail("'expected' contained more lines than 'actual'.");
            }
        }
        if ((actualLine = actualBr.readLine()) != null)
        {
            Assert.fail("'actual' contained more lines than 'expected'.");
        }
    }
}


<|start_filename|>src/main/java/org/slf4j/impl/TraceLoggerFactory.java<|end_filename|>
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root.

package org.slf4j.impl;

import org.slf4j.ILoggerFactory;
import org.slf4j.Logger;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * An implementation of {@link ILoggerFactory} which always returns
 * {@link TraceLogger} instances.
 */
public class TraceLoggerFactory implements ILoggerFactory
{
    private final ConcurrentMap<String, Logger> loggersByName = new ConcurrentHashMap<String, Logger>();

    @Override
    public Logger getLogger(final String name)
    {
        Logger simpleLogger = loggersByName.get(name);
        if (simpleLogger != null) {
            return simpleLogger;
        } else {
            Logger newInstance = new TraceLogger(name);
            Logger oldInstance = loggersByName.putIfAbsent(name, newInstance);
            return oldInstance == null ? newInstance : oldInstance;
        }
    }
}


<|start_filename|>src/test/java/com/microsoft/alm/helpers/NullUserAgent.java<|end_filename|>
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root.

package com.microsoft.alm.helpers;

import com.microsoft.alm.oauth2.useragent.AuthorizationException;
import com.microsoft.alm.oauth2.useragent.AuthorizationResponse;
import com.microsoft.alm.oauth2.useragent.UserAgent;

import java.net.URI;

/**
 * An implementation of {@link UserAgent} that does nothing.
 */
public class NullUserAgent implements UserAgent {

    public static final UserAgent INSTANCE = new NullUserAgent();

    private NullUserAgent() {
        // not meant to be constructed by others
    }

    @Override
    public AuthorizationResponse requestAuthorizationCode(
            final URI authorizationEndpoint, final URI redirectUri) throws AuthorizationException {
        return null;
    }
}


<|start_filename|>src/main/java/com/microsoft/alm/authentication/BaseSecureStore.java<|end_filename|>
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root.

package com.microsoft.alm.authentication;

import com.microsoft.alm.helpers.Trace;
import com.microsoft.alm.secret.Credential;
import com.microsoft.alm.secret.Token;

import java.net.URI;

public abstract class BaseSecureStore
{
    public static final char[] IllegalCharacters = { ':', ';', '\\', '?', '@', '=', '&', '%', '$' };

    private final ISecureStore delegate;

    protected BaseSecureStore(final ISecureStore delegate)
    {
        this.delegate = delegate;
    }

    protected void delete(final String targetName)
    {
        Trace.writeLine("BaseSecureStore::delete");

        try
        {
            delegate.delete(targetName);
        }
        catch (final Throwable throwable)
        {
            Trace.writeLine(throwable.toString());
        }
    }

    protected abstract String getTargetName(final URI targetUri);

    protected Credential readCredentials(final String targetName)
    {
        Trace.writeLine("BaseSecureStore::readCredentials");

        return delegate.readCredentials(targetName);
    }

    protected Token readToken(final String targetName)
    {
        Trace.writeLine("BaseSecureStore::readToken");

        return delegate.readToken(targetName);
    }

    protected void writeCredential(final String targetName, final Credential credentials)
    {
        Trace.writeLine("BaseSecureStore::writeCredential");

        delegate.writeCredential(targetName,  credentials);
    }

    protected void writeToken(final String targetName, final Token token)
    {
        Trace.writeLine("BaseSecureStore::writeToken");

        delegate.writeToken(targetName, token);
    }

    static void validateTargetUri(final URI targetUri)
    {
        if (targetUri == null)
            throw new IllegalArgumentException("targetUri");
        if (!targetUri.isAbsolute())
            throw new IllegalArgumentException("The target URI must be an absolute URI");
    }
}


<|start_filename|>src/main/java/com/microsoft/alm/authentication/IGithubAuthentication.java<|end_filename|>
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root.

package com.microsoft.alm.authentication;

import com.microsoft.alm.secret.Credential;

import java.net.URI;
import java.util.concurrent.atomic.AtomicReference;

public interface IGithubAuthentication extends IAuthentication
{
    boolean interactiveLogon(final URI targetUri, final AtomicReference<Credential> credentials);
    boolean noninteractiveLogonWithCredentials(final URI targetUri, final String username, final String password, final String authenticationCode);
    boolean validateCredentials(final URI targetUri, final Credential credentials);
}


<|start_filename|>src/main/java/com/microsoft/alm/authentication/AzureDeviceFlow.java<|end_filename|>
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root.

package com.microsoft.alm.authentication;

import com.microsoft.alm.helpers.QueryString;

import java.net.URI;

public class AzureDeviceFlow extends DeviceFlowImpl
{
    private String resource;
    private URI redirectUri;

    public String getResource()
    {
        return resource;
    }

    public void setResource(final String resource)
    {
        this.resource = resource;
    }

    public URI getRedirectUri()
    {
        return redirectUri;
    }

    public void setRedirectUri(final URI redirectUri)
    {
        this.redirectUri = redirectUri;
    }

    @Override
    protected void contributeAuthorizationRequestParameters(final QueryString bodyParameters)
    {
        if (resource != null)
        {
            bodyParameters.put("resource", resource);
        }

        if (redirectUri != null)
        {
            bodyParameters.put(OAuthParameter.REDIRECT_URI, redirectUri.toString());
        }
    }

    @Override
    protected DeviceFlowResponse buildDeviceFlowResponse(final String responseText)
    {
        return AzureDeviceFlowResponse.fromJson(responseText);
    }
}


<|start_filename|>src/main/java/com/microsoft/alm/authentication/VsoAzureAuthority.java<|end_filename|>
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root.

package com.microsoft.alm.authentication;

import com.microsoft.alm.helpers.Action;
import com.microsoft.alm.helpers.Debug;
import com.microsoft.alm.helpers.Environment;
import com.microsoft.alm.helpers.Guid;
import com.microsoft.alm.helpers.HttpClient;
import com.microsoft.alm.helpers.NotImplementedException;
import com.microsoft.alm.helpers.StringContent;
import com.microsoft.alm.helpers.StringHelper;
import com.microsoft.alm.helpers.Trace;
import com.microsoft.alm.secret.Credential;
import com.microsoft.alm.secret.Token;
import com.microsoft.alm.secret.TokenType;
import com.microsoft.alm.secret.VsoTokenScope;

import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URI;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicReference;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

class VsoAzureAuthority extends AzureAuthority implements IVsoAuthority
{
    /**
     * The maximum wait time for a network request before timing out
     */
    public static final int RequestTimeout = 15 * 1000; // 15 second limit

    public VsoAzureAuthority() { this (null); }
    public VsoAzureAuthority(final String authorityHostUrl)
    {
        super();
        if (authorityHostUrl != null)
        {
            this.authorityHostUrl = authorityHostUrl;
        }
    }

    /**
     * Generates a personal access token for use with Visual Studio Online.
     *
     * @param targetUri           The uniform resource indicator of the resource access tokens are being requested for.
     * @param accessToken
     * @param tokenScope
     * @param requireCompactToken
     * @return
     */
    @Override public Token generatePersonalAccessToken(final URI targetUri, final Token accessToken, final VsoTokenScope tokenScope, final boolean requireCompactToken)
    {
        Debug.Assert(targetUri != null, "The targetUri parameter is null");
        Debug.Assert(accessToken != null && !StringHelper.isNullOrWhiteSpace(accessToken.Value) && (accessToken.Type == TokenType.Access || accessToken.Type == TokenType.Federated), "The accessToken parameter is null or invalid");
        Debug.Assert(tokenScope != null, "The tokenScope parameter is invalid");

        Trace.writeLine("VsoAzureAuthority::generatePersonalAccessToken");

        try
        {
            // TODO: 449524: create a `HttpClient` with a minimum number of redirects, default creds, and a reasonable timeout (access token generation seems to hang occasionally)
            final HttpClient client = new HttpClient(Global.getUserAgent());
            Trace.writeLine("   using token to acquire personal access token");
            accessToken.contributeHeader(client.Headers);

            if (populateTokenTargetId(targetUri, accessToken))
            {
                final URI requestUrl = createPersonalAccessTokenRequestUri(client, targetUri, requireCompactToken);

                final StringContent content = getAccessTokenRequestBody(targetUri, accessToken, tokenScope);
                final HttpURLConnection response = client.post(requestUrl, content);
                if (response.getResponseCode() == HttpURLConnection.HTTP_OK)
                {
                    final String responseText = HttpClient.readToString(response);

                    final Token token = parsePersonalAccessTokenFromJson(responseText);
                    if (token != null)
                    {
                        Trace.writeLine("   personal access token acquisition succeeded.");
                    }
                    return token;
                }
            }
        }
        catch (final IOException e)
        {
            throw new Error(e);
        }
        return null;
    }

    private URI createPersonalAccessTokenRequestUri(final HttpClient client, final URI targetUri,
                                                    final boolean requireCompactToken) throws IOException
    {
        final String SessionTokenUrl = "_apis/token/sessiontokens?api-version=1.0";
        final String CompactTokenUrl = SessionTokenUrl + "&tokentype=compact";

        Debug.Assert(client != null, "The client is null");

        final URI identityServiceUri = getIdentityServiceUri(client, targetUri);
        if (identityServiceUri == null)
        {
            throw new RuntimeException("Failed to find Identity Service for " + targetUri.toString());
        }

        String url = identityServiceUri.toString();

        if (!url.endsWith("/")) {
            url += "/";
        }

        url += requireCompactToken ? CompactTokenUrl : SessionTokenUrl;

        return URI.create(url);
    }

    private URI getIdentityServiceUri(final HttpClient client, final URI targetUri) throws IOException
    {
        final String locationServiceUrlFormat = "https://%1$s/_apis/ServiceDefinitions/LocationService2/951917AC-A960-4999-8464-E3F0AA25B381?api-version=1.0";

        Debug.Assert(client != null, ("The client parameter is null."));
        Debug.Assert(targetUri != null && targetUri.isAbsolute(), "The targetUri parameter is null or invalid");

        final String locationServiceUrl = String.format(locationServiceUrlFormat, targetUri.getHost());
        URI identityServiceUri = null;

        final HttpURLConnection response = client.get(URI.create(locationServiceUrl));
        if (response.getResponseCode() == HttpURLConnection.HTTP_OK)
        {
            final String responseText = HttpClient.readToString(response);

            // Identity Service uri is the "location" field.
            identityServiceUri = parseLocationFromJson(responseText);
            if (identityServiceUri != null)
            {
                Trace.writeLine("   parsed identity service url: " + identityServiceUri);
            }
        }

        return identityServiceUri;
    }

    public boolean populateTokenTargetId(final URI targetUri, final Token accessToken)
    {
        Debug.Assert(targetUri != null && targetUri.isAbsolute(), "The targetUri parameter is null or invalid");
        Debug.Assert(accessToken != null && !StringHelper.isNullOrWhiteSpace(accessToken.Value) && (accessToken.Type == TokenType.Access || accessToken.Type == TokenType.Federated), "The accessToken parameter is null or invalid");

        Trace.writeLine("VsoAzureAuthority::populateTokenTargetId");

        String resultId = null;
        try
        {
            // create an request to the VSO deployment data end-point
            final HttpURLConnection request = createConnectionDataRequest(targetUri, accessToken);

            // send the request and wait for the response
            final String content = HttpClient.readToString(request);

            resultId = parseInstanceIdFromJson(content);
        }
        catch (final IOException e)
        {
            throw new Error(e);
        }

        final AtomicReference<UUID> instanceId = new AtomicReference<UUID>();
        if (Guid.tryParse(resultId, instanceId))
        {
            Trace.writeLine("   target identity is " + resultId);
            accessToken.setTargetIdentity(instanceId.get());

            return true;
        }

        return false;
    }

    /**
     * Validates that {@link Credential} are valid to grant access to the Visual Studio
     * Online service represented by the {@literal targetUri} parameter.
     *
     * @param targetUri   Uniform resource identifier for a VSO service.
     * @param credentials {@link Credential} expected to grant access to the VSO service.
     * @return True if successful; otherwise false.
     */
    @Override public boolean validateCredentials(final URI targetUri, final Credential credentials)
    {
        Debug.Assert(targetUri != null && targetUri.isAbsolute(), "The targetUri parameter is null or invalid");
        Debug.Assert(credentials != null, "The credentials parameter is null or invalid");

        Trace.writeLine("VsoAzureAuthority::validateCredentials");

        try
        {
            // create an request to the VSO deployment data end-point
            final HttpURLConnection request = createConnectionDataRequest(targetUri, credentials);

            // send the request and wait for the response
            request.connect();
            final int statusCode = request.getResponseCode();
            // we're looking for 'OK 200' here, anything else is failure
            Trace.writeLine("   server returned: " + statusCode);
            return statusCode == HttpURLConnection.HTTP_OK;
        }
        catch (final IOException e)
        {
            throw new Error(e);
        }
        catch (final Throwable ignored)
        {
            throw new Error("   unexpected error", ignored);
        }
    }

    /**
     * <p>Validates that {@link Token} are valid to grant access to the Visual Studio
     * Online service represented by the {@literal targetUri} parameter.</p>
     * <p>Tokens of {@link TokenType#Refresh} cannot grant access, and
     * therefore always fail - this does not mean the token is invalid.</p>
     *
     * @param targetUri   Uniform resource identifier for a VSO service.
     * @param token       {@link Token} expected to grant access to the VSO service.
     * @return True if successful; otherwise false.
     */
    @Override public boolean validateToken(final URI targetUri, final Token token)
    {
        throw new NotImplementedException(449243);
    }

    private static final Pattern TOKEN_PATTERN = Pattern.compile(
        "\"token\"\\s*:\\s*\"([^\"]+)\"",
        Pattern.CASE_INSENSITIVE
    );
    static Token parsePersonalAccessTokenFromJson(final String json)
    {
        Token token = null;
        if (!StringHelper.isNullOrWhiteSpace(json))
        {
            // find the 'token : <value>' portion of the result content, if any
            final Matcher matcher = TOKEN_PATTERN.matcher(json);
            if (matcher.find())
            {
                final String tokenValue = matcher.group(1);
                token = new Token(tokenValue, TokenType.Personal);
            }
        }
        return token;
    }

     private static final Pattern INSTANCE_ID_PATTERN = Pattern.compile(
        "\"instanceId\"\\s*:\\s*\"([^\"]+)\"",
        Pattern.CASE_INSENSITIVE
    );
    static String parseInstanceIdFromJson(final String json)
    {
        String result = null;

        final Matcher matcher = INSTANCE_ID_PATTERN.matcher(json);
        if (matcher.find())
        {
            result = matcher.group(1);
        }

        return result;
    }

    private static final Pattern LOCATION_PATTERN = Pattern.compile(
            "\"location\"\\s*:\\s*\"([^\"]+)\"",
            Pattern.CASE_INSENSITIVE
    );
    static URI parseLocationFromJson(final String json)
    {
        URI locationServiceUri = null;
        if (!StringHelper.isNullOrWhiteSpace(json))
        {
            // find the 'location : <value>' portion of the result content, if any
            final Matcher matcher = LOCATION_PATTERN.matcher(json);
            if (matcher.find())
            {
                final String location = matcher.group(1);
                locationServiceUri = URI.create(location);
            }
        }
        return locationServiceUri;
    }

    private StringContent getAccessTokenRequestBody(final URI targetUri, final Token accessToken, final VsoTokenScope tokenScope)
    {
        final String ContentJsonFormat = "{ \"scope\" : \"%1$s\", \"targetAccounts\" : [\"%2$s\"], \"displayName\" : \"Git: %3$s on %4$s\" }";

        Debug.Assert(accessToken != null && (accessToken.Type == TokenType.Access || accessToken.Type == TokenType.Federated), "The accessToken parameter is null or invalid");
        Debug.Assert(tokenScope != null, "The tokenScope parameter is null");

        final String targetIdentity = accessToken.getTargetIdentity().toString();
        Trace.writeLine("   creating access token scoped to '" + tokenScope + "' for '" + targetIdentity + "'");

        final String jsonContent = String.format(ContentJsonFormat, tokenScope, targetIdentity, targetUri, Environment.getMachineName());
        final StringContent content = StringContent.createJson(jsonContent);
        return content;
    }

    private HttpURLConnection createConnectionDataRequest(final URI targetUri, final Credential credentials) throws IOException
    {
        Debug.Assert(targetUri != null && targetUri.isAbsolute(), "The targetUri parameter is null or invalid");
        Debug.Assert(credentials != null, "The credentials parameter is null or invalid");

        final HttpClient client = new HttpClient(Global.getUserAgent());

        // create an request to the VSO deployment data end-point
        final URI requestUri = createConnectionDataUri(targetUri);

        credentials.contributeHeader(client.Headers);

        final HttpURLConnection result = client.get(requestUri, new Action<HttpURLConnection>()
        {
            @Override public void call(final HttpURLConnection conn)
            {
                conn.setConnectTimeout(RequestTimeout);
            }
        });
        return result;
    }

    private HttpURLConnection createConnectionDataRequest(final URI targetUri, final Token token) throws IOException
    {
        Debug.Assert(targetUri != null && targetUri.isAbsolute(), "The targetUri parameter is null or invalid");
        Debug.Assert(token != null && (token.Type == TokenType.Access || token.Type == TokenType.Federated), "The token parameter is null or invalid");

        Trace.writeLine("VsoAzureAuthority::createConnectionDataRequest");

        final HttpClient client = new HttpClient(Global.getUserAgent());

        // create an request to the VSO deployment data end-point
        final URI requestUri = createConnectionDataUri(targetUri);

        Trace.writeLine("   validating token");
        token.contributeHeader(client.Headers);

        final HttpURLConnection result = client.get(requestUri, new Action<HttpURLConnection>()
        {
            @Override public void call(final HttpURLConnection conn)
            {
                conn.setConnectTimeout(RequestTimeout);
            }
        });
        return result;
    }

    private URI createConnectionDataUri(final URI targetUri)
    {
        final String VsoValidationUrlFormat = "https://%1$s/_apis/connectiondata";

        Debug.Assert(targetUri != null & targetUri.isAbsolute(), "The targetUri parameter is null or invalid");

        // create a url to the connection data end-point, it's deployment level and "always on".
        final String validationUrl = String.format(VsoValidationUrlFormat, targetUri.getHost());

        final URI result = URI.create(validationUrl);
        return result;
    }
}


<|start_filename|>src/main/java/com/microsoft/alm/authentication/Where.java<|end_filename|>
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root.

package com.microsoft.alm.authentication;

import com.microsoft.alm.helpers.Environment;
import com.microsoft.alm.helpers.Func;
import com.microsoft.alm.helpers.IOHelper;
import com.microsoft.alm.helpers.IteratorExtensions;
import com.microsoft.alm.helpers.ObjectExtensions;
import com.microsoft.alm.helpers.Path;
import com.microsoft.alm.helpers.StringHelper;

import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.IOException;
import java.util.concurrent.atomic.AtomicReference;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Where
{
    private static final Pattern gitdirPattern = Pattern.compile("gitdir\\s*:\\s([^\\r\\n]+)", Pattern.CASE_INSENSITIVE);

    /**
     * Finds the "best" path to an app of a given name.
     *
     * @param name The name of the application, without extension, to find.
     * @param path Path to the first match file which the operating system considers
     *             executable.
     * @return True if succeeds; false otherwise.
     */
    public static boolean app(final String name, final AtomicReference<String> path)
    {
        if (!StringHelper.isNullOrWhiteSpace(name))
        {
            final String pathext = ObjectExtensions.coalesce(System.getenv("PATHEXT"), StringHelper.Empty);
            final String envpath = ObjectExtensions.coalesce(System.getenv("PATH"), StringHelper.Empty);

            final String pathSeparator = System.getProperty("path.separator");
            final Func<String, Boolean> existenceChecker = new Func<String, Boolean>()
            {
                @Override public Boolean call(final String s)
                {
                    return new File(s).exists();
                }
            };
            if (app(name, path, pathext, envpath, pathSeparator, existenceChecker))
            {
                return true;
            }
        }
        path.set(null);
        return false;
    }

    static boolean app(
            final String name,
            final AtomicReference<String> path,
            final String pathext,
            final String envpath,
            final String pathSeparator,
            final Func<String, Boolean> existenceChecker)
    {
        final String[] exts = pathext.split(pathSeparator);
        final String[] paths = envpath.split(pathSeparator);

        for (int i = 0; i < paths.length; i++)
        {
            if (StringHelper.isNullOrWhiteSpace(paths[i]))
                continue;

            for (int j = 0; j < exts.length; j++)
            {
                // we need to consider the case without an extension,
                // so skip the null or empty check that was in the C# version

                final String value = String.format("%1$s/%2$s%3$s", paths[i], name, exts[j]);
                final Boolean result = existenceChecker.call(value);
                if (result != null && (boolean)result)
                {
                    path.set(value);
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * Gets the path to the Git global configuration file.
     *
     * @param path Path to the Git global configuration.
     * @return True if succeeds; false otherwise.
     */
    public static boolean gitGlobalConfig(final AtomicReference<String> path)
    {
        final String GlobalConfigFileName = ".gitconfig";

        path.set(null);

        String globalPath = Path.combine(Environment.getFolderPath(Environment.SpecialFolder.UserProfile), GlobalConfigFileName);

        if (Path.fileExists(globalPath))
        {
            path.set(globalPath);
        }

        return path.get() != null;
    }

    /**
     * Gets the path to the Git local configuration file based on the startingDirectory.
     *
     * @param startingDirectory A directory of the repository where the configuration file is contained.
     * @param path Path to the Git local configuration.
     * @return True if succeeds; false otherwise.
     * @throws IOException if reading from the config file fails.
     */
    public static boolean gitLocalConfig(final String startingDirectory, final AtomicReference<String> path) throws IOException
    {
        final String GitOdbFolderName = ".git";
        final String LocalConfigFileName = "config";

        path.set(null);

        if (!StringHelper.isNullOrWhiteSpace(startingDirectory))
        {
            File dir = new File(startingDirectory);
            if (dir.exists())

            {
                // the hasOdb Func was simplified to an exists() check
                File result = null;
                while (dir != null && dir.exists() && dir.getParentFile() != null && dir.getParentFile().exists())
                {
                    result = new File(dir, GitOdbFolderName);
                    if (result.exists())
                        break;

                    dir = dir.getParentFile();
                }

                if (result != null && result.exists())
                {
                    if (result.isDirectory())
                    {
                        final String localPath = Path.combine(result.getAbsolutePath(), LocalConfigFileName);
                        if (Path.fileExists(localPath))
                        {
                            path.set(localPath);
                        }
                    }
                    else
                    {
                        // parse the file like gitdir: ../.git/modules/libgit2sharp
                        String content = null;

                        // shortcut the opening streams & readers just to read the whole file as a string
                        content = IOHelper.readFileToString(result);

                        final Matcher match;
                        if ((match = gitdirPattern.matcher(content)).matches()
                            && match.groupCount() > 1)
                        {
                            content = match.group(1);
                            // don't replace / with \\

                            String localPath = null;

                            if (Path.isAbsolute(content))
                            {
                                localPath = content;
                            }
                            else
                            {
                                localPath = Path.getDirectoryName(result.getAbsolutePath());
                                localPath = Path.combine(localPath, content);
                            }

                            if (Path.directoryExists(localPath))
                            {
                                localPath = Path.combine(localPath, LocalConfigFileName);
                                if (Path.fileExists(localPath))
                                {
                                    path.set(localPath);
                                }
                            }
                        }
                    }
                }
            }
        }

        return path.get() != null;
    }

    /**
     * Gets the path to the Git local configuration file based on the current working directory.
     *
     * @param path Path to the Git local configuration.
     * @return True if succeeds; false otherwise.
     * @throws IOException if reading from the config file fails.
     */
    public static boolean gitLocalConfig(final AtomicReference<String> path) throws IOException
    {
        return gitLocalConfig(Environment.getCurrentDirectory(), path);
    }

    /**
     * Gets the path to the Git system configuration file.
     *
     * @param path Path to the Git system configuration.
     * @return True if succeeds; false otherwise.
     */
    public static boolean gitSystemConfig(final AtomicReference<String> path)
    {
        final String SystemConfigFileName = "gitconfig";

        // find Git on the local disk - the system config is stored relative to it
        if (app("git", path))
        {
            File gitInfo = new File(path.get());
            File dir = gitInfo.getParentFile();
            if (dir.getParentFile() != null)
            {
                dir = dir.getParentFile();
            }

            final File[] subDirs = dir.listFiles(new FileFilter()
            {
                @Override public boolean accept(final File pathname)
                {
                    return pathname.isDirectory();
                }
            });
            for (final File subDir : subDirs)
            {
                final File file = new File(subDir, SystemConfigFileName);
                if (file.isFile())
                {
                    path.set(file.getAbsolutePath());
                    return true;
                }
            }
        }

        path.set(null);
        return false;
    }
}


<|start_filename|>src/main/java/com/microsoft/alm/authentication/UserIdentifier.java<|end_filename|>
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root.

package com.microsoft.alm.authentication;

import com.microsoft.alm.helpers.StringHelper;

/**
 * Contains identifier for a user.
 */
public final class UserIdentifier {
    private static final String ANY_USER_ID = "AnyUser";
    /**
     * A static instance of {@link UserIdentifier} to represent any user.
     */
    public static final UserIdentifier ANY_USER = new UserIdentifier(ANY_USER_ID, UserIdentifierType.UNIQUE_ID);

    private final String id;
    private final UserIdentifierType type;

    public UserIdentifier(final String id, final UserIdentifierType type) {
        if (StringHelper.isNullOrWhiteSpace(id)) {
            throw new IllegalArgumentException("id is null or empty");
        }
        this.id = id;
        this.type = type;
    }

    /**
     * @return the type of the {@link UserIdentifier}.
     */
    public UserIdentifierType getType() {
        return this.type;
    }

    /**
     * @return id of the {@link UserIdentifier}.
     */
    public String getId() {
        return this.id;
    }

    boolean isAnyUser() {
        return this.type == ANY_USER.type && this.id.equals(ANY_USER.id);
    }

    String getUniqueId() {
        return (!this.isAnyUser() && this.type == UserIdentifierType.UNIQUE_ID) ? this.id : null;
    }

    String getDisplayableId() {
        return (!this.isAnyUser() && (this.type == UserIdentifierType.OPTIONAL_DISPLAYABLE_ID || this.type == UserIdentifierType.REQUIRED_DISPLAYABLE_ID)) ? this.id : null;
    }
}


<|start_filename|>src/main/java/com/microsoft/alm/authentication/BaseAuthentication.java<|end_filename|>
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root.

package com.microsoft.alm.authentication;

import com.microsoft.alm.secret.Credential;

import java.net.URI;
import java.util.concurrent.atomic.AtomicReference;

/**
 * Base authentication mechanisms for setting, retrieving, and deleting stored credentials.
 */
public abstract class BaseAuthentication implements IAuthentication
{
    /**
     * Deletes a {@link Credential} from the storage used by the authentication object.
     *
     * @param targetUri
     *        The uniform resource indicator used to uniquely identify the credentials.
     */
    public abstract void deleteCredentials(final URI targetUri);

    /**
     * Gets a {@link Credential} from the storage used by the authentication object.
     *
     * @param targetUri
     *        The uniform resource indicator used to uniquely identify the credentials.
     *
     * @param credentials
     *        If successful a {@link Credential} object from the authentication object,
     *        authority or storage; otherwise null.
     *
     * @return true if successful; otherwise false.
     */
    public abstract boolean getCredentials(final URI targetUri, final AtomicReference<Credential> credentials);

    /**
     * Sets a {@link Credential} in the storage used by the authentication object.
     *
     * @param targetUri
     *        The uniform resource indicator used to uniquely identify the credentials.
     *
     * @param credentials The value to be stored
     *
     * @return true if successful; otherwise false.
     */
    public abstract boolean setCredentials(final URI targetUri, final Credential credentials);
}


<|start_filename|>src/main/java/com/microsoft/alm/authentication/DeviceFlow.java<|end_filename|>
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root.

package com.microsoft.alm.authentication;

import com.microsoft.alm.oauth2.useragent.AuthorizationException;
import com.microsoft.alm.secret.TokenPair;

import java.net.URI;

/**
 * An object that implements part of the OAuth 2.0 Device Flow.
 *
 * @see <a href="https://tools.ietf.org/html/draft-ietf-oauth-device-flow-01">
 *     OAuth 2.0 Device Flow IETF Draft 01
 *     </a>
 */
public interface DeviceFlow
{
    /**
     * The client initiates the flow by requesting a set of verification
     * codes from the authorization server's device endpoint.
     *
     * @param deviceEndpoint the authorization server's endpoint capable of issuing
     *                       verification codes, user codes, and verification URLs.
     * @param clientId       the client identifier as described in Section 2.2 of RFC6749.
     * @param scope          the scope of the access request as described by
     *                       Section 3.3 of RFC6749. (optional)
     * @return               a {@link DeviceFlowResponse} representing how the 2nd
     *                       phase of the protocol should proceed.
     *
     * @see                  "steps (A) and (B) of the Device Flow"
     */
    DeviceFlowResponse requestAuthorization(final URI deviceEndpoint, final String clientId, final String scope);

    /**
     * The client polls the authorization server's token endpoint repeatedly
     * until the end-user grants or denies the request, or the verification
     * code expires.
     *
     * @param tokenEndpoint           the authorization server's
     *                                token endpoint as described in Section 4.1.1 of RFC6749.
     * @param clientId                the client identifier as described in Section 2.2 of RFC6749.
     * @param deviceFlowResponse      the response obtained from
     *                                {@link #requestAuthorization(URI, String, String)}.
     * @return                        a pair of tokens.
     * @throws AuthorizationException the end-user denied the request,
     *                                or the verification code expired.
     *
     * @see                           "steps (E) and (F) of the Device Flow"
     */
    TokenPair requestToken(final URI tokenEndpoint, String clientId, final DeviceFlowResponse deviceFlowResponse) throws AuthorizationException;
}


<|start_filename|>src/test/java/com/microsoft/alm/authentication/WhereTest.java<|end_filename|>
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root.

package com.microsoft.alm.authentication;

import com.microsoft.alm.helpers.Func;
import com.microsoft.alm.helpers.StringHelper;
import org.junit.Assert;
import org.junit.Test;

import java.util.concurrent.atomic.AtomicReference;

import static org.mockito.Matchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

public class WhereTest
{
    @Test public void app_ping() throws Exception
    {
        final AtomicReference<String> path = new AtomicReference<String>();

        final boolean success = Where.app("ping", path);
        final String actual = path.get();

        Assert.assertEquals("Doesn't ping exist on all platforms?", true, success);
        Assert.assertNotNull(actual);
    }
    
    @Test public void app_simulateWindowsSuccess()
    {
        final AtomicReference<String> path = new AtomicReference<String>();
        final String envpath = "c:/Windows;c:/Windows/System32;c:/Java";
        @SuppressWarnings("unchecked")
        final Func<String, Boolean> existenceChecker = (Func<String, Boolean>) mock(Func.class);
        final String pathValue = "c:/Java/java.exe";
        when(existenceChecker.call(pathValue)).thenReturn(true);

        final boolean result = Where.app("java", path, ".exe", envpath, ";", existenceChecker);

        Assert.assertEquals(true, result);
        Assert.assertEquals(pathValue, path.get());
        verify(existenceChecker, times(3)).call(anyString());
    }

    @Test public void app_simulateWindowsFailure()
    {
        final AtomicReference<String> path = new AtomicReference<String>();
        final String envpath = "c:/Windows;c:/Windows/System32;c:/Java";
        @SuppressWarnings("unchecked")
        final Func<String, Boolean> existenceChecker = (Func<String, Boolean>) mock(Func.class);
        when(existenceChecker.call("c:/Java/java.exe")).thenReturn(false);

        final boolean result = Where.app("java", path, ".exe", envpath, ";", existenceChecker);

        Assert.assertEquals(false, result);
        verify(existenceChecker, times(3)).call(anyString());
    }

    @Test public void app_simulateNixSuccess()
    {
        final AtomicReference<String> path = new AtomicReference<String>();
        final String envpath = "/usr/bin:/usr/sbin:/opt/java";
        @SuppressWarnings("unchecked")
        final Func<String, Boolean> existenceChecker = (Func<String, Boolean>) mock(Func.class);
        final String pathValue = "/opt/java/java";
        when(existenceChecker.call(pathValue)).thenReturn(true);

        final boolean result = Where.app("java", path, StringHelper.Empty, envpath, ":", existenceChecker);

        Assert.assertEquals(true, result);
        Assert.assertEquals(pathValue, path.get());
        verify(existenceChecker, times(3)).call(anyString());
    }
    
    @Test public void app_simulateNixFailure()
    {
        final AtomicReference<String> path = new AtomicReference<String>();
        final String envpath = "/usr/bin:/usr/sbin:/opt/java";
        @SuppressWarnings("unchecked")
        final Func<String, Boolean> existenceChecker = (Func<String, Boolean>) mock(Func.class);
        when(existenceChecker.call("/opt/java")).thenReturn(false);

        final boolean result = Where.app("java", path, StringHelper.Empty, envpath, ":", existenceChecker);

        Assert.assertEquals(false, result);
        verify(existenceChecker, times(3)).call(anyString());
    }
}


<|start_filename|>src/main/java/com/microsoft/alm/authentication/PromptValue.java<|end_filename|>
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root.

package com.microsoft.alm.authentication;

class PromptValue
{
    static final String LOGIN = "login";
    static final String REFRESH_SESSION = "refresh_session";

    // The behavior of this value is identical to prompt=none for managed users; However, for federated users, AAD
    // redirects to ADFS as it cannot determine in advance whether ADFS can login user silently (e.g. via WIA) or not.
    static final String ATTEMPT_NONE = "attempt_none";
}


<|start_filename|>src/main/java/com/microsoft/alm/authentication/AzureAuthority.java<|end_filename|>
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root.

package com.microsoft.alm.authentication;

import com.microsoft.alm.helpers.Action;
import com.microsoft.alm.helpers.Debug;
import com.microsoft.alm.helpers.Guid;
import com.microsoft.alm.helpers.HttpClient;
import com.microsoft.alm.helpers.NotImplementedException;
import com.microsoft.alm.helpers.ObjectExtensions;
import com.microsoft.alm.helpers.QueryString;
import com.microsoft.alm.helpers.StringContent;
import com.microsoft.alm.helpers.StringHelper;
import com.microsoft.alm.helpers.Trace;
import com.microsoft.alm.helpers.UriHelper;
import com.microsoft.alm.oauth2.useragent.AuthorizationException;
import com.microsoft.alm.oauth2.useragent.AuthorizationResponse;
import com.microsoft.alm.oauth2.useragent.Provider;
import com.microsoft.alm.oauth2.useragent.ProviderScanner;
import com.microsoft.alm.oauth2.useragent.UserAgent;
import com.microsoft.alm.oauth2.useragent.UserAgentImpl;
import com.microsoft.alm.secret.Credential;
import com.microsoft.alm.secret.Token;
import com.microsoft.alm.secret.TokenPair;
import com.microsoft.alm.secret.TokenType;

import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.List;
import java.util.Map;
import java.util.UUID;

/**
 * Interfaces with Azure to perform authentication and identity services.
 */
class AzureAuthority implements IAzureAuthority
{
    /**
     * The base URL for logon services in Azure.
     */
    public static final String AuthorityHostUrlBase = "https://login.microsoftonline.com";
    /**
     * The common Url for logon services in Azure.
     */
    public static final String DefaultAuthorityHostUrl = AuthorityHostUrlBase + "/common";

    /**
     * Creates a new {@link AzureAuthority} with the default authority host url.
     */
    public AzureAuthority() { this(DefaultAuthorityHostUrl); }

    /**
     * Creates a new {@link AzureAuthority} with an authority host url.
     *
     * @param authorityHostUrl Non-default authority host url.
     */
    public AzureAuthority(final String authorityHostUrl)
    {
        this(authorityHostUrl, new UserAgentImpl(), new AzureDeviceFlow());
    }

    AzureAuthority(final String authorityHostUrl, final UserAgent userAgent, final AzureDeviceFlow azureDeviceFlow)
    {
        Debug.Assert(UriHelper.isWellFormedUriString(authorityHostUrl), "The authorityHostUrl parameter is invalid.");
        Debug.Assert(userAgent != null, "The userAgent parameter is null.");

        this.authorityHostUrl = authorityHostUrl;
        _adalTokenCache = /* TODO: 449201: consider new InsecureStore("adalTokenCache.xml");*/null;
        _userAgent = userAgent;
        _azureDeviceFlow = azureDeviceFlow;
    }

    private final VsoAdalTokenCache _adalTokenCache;
    private final UserAgent _userAgent;
    private final AzureDeviceFlow _azureDeviceFlow;

    protected String authorityHostUrl;
    /**
     * The URL used to interact with the Azure identity service.
     */
    public String getAuthorityHostUrl() { return authorityHostUrl; }

    /**
     * Acquires a {@link TokenPair} from the authority via an interactive user logon
     * prompt.
     *
     * @param targetUri       The uniform resource indicator of the resource access tokens are being requested for.
     * @param clientId        Identifier of the client requesting the token.
     * @param resource        Identifier of the target resource that is the recipient of the requested token.
     * @param redirectUri     Address to return to upon receiving a response from the authority.
     * @param queryParameters Optional: appended as-is to the query string in the HTTP authentication request to the
     *                        authority.
     * @return If successful, a {@link TokenPair}; otherwise null.
     */
    public TokenPair acquireToken(final URI targetUri, final String clientId, final String resource, final URI redirectUri, String queryParameters)
    {
        Debug.Assert(targetUri != null && targetUri.isAbsolute(), "The targetUri parameter is null or invalid");
        Debug.Assert(!StringHelper.isNullOrWhiteSpace(clientId), "The clientId parameter is null or empty");
        Debug.Assert(!StringHelper.isNullOrWhiteSpace(resource), "The resource parameter is null or empty");
        Debug.Assert(redirectUri != null, "The redirectUri parameter is null");
        Debug.Assert(redirectUri.isAbsolute(), "The redirectUri parameter is not an absolute Uri");

        Trace.writeLine("AzureAuthority::acquireToken");

        final UUID correlationId = null;
        TokenPair tokens = null;
        queryParameters = ObjectExtensions.coalesce(queryParameters, StringHelper.Empty);

        // TODO: 449243: check _adalTokenCache first, then attempt to acquire token from refresh token

        final String authorizationCode = acquireAuthorizationCode(resource, clientId, redirectUri, queryParameters);
        if (authorizationCode == null)
        {
            Trace.writeLine("   token acquisition failed.");
            return tokens;
        }

        final HttpClient client = new HttpClient(Global.getUserAgent());
        try
        {
            final URI tokenEndpoint = createTokenEndpointUri(authorityHostUrl);
            final StringContent requestContent = createTokenRequest(resource, clientId, authorizationCode, redirectUri, correlationId);
            final HttpURLConnection connection = client.post(tokenEndpoint, requestContent, new Action<HttpURLConnection>()
            {
                @Override public void call(final HttpURLConnection conn)
                {
                    conn.setUseCaches(false);
                }
            });
            client.ensureOK(connection);
            final String responseContent = HttpClient.readToString(connection);
            tokens = new TokenPair(responseContent);

            // TODO: 449201: store access + refresh tokens to _adalTokenCache

            Trace.writeLine("   token acquisition succeeded.");
        }
        catch (final IOException e)
        {
            throw new Error("   token acquisition failed.", e);
        }
        return tokens;
    }

    /**
     * Acquires a {@link TokenPair} from the authority using optionally provided
     * credentials or via the current identity.
     *
     * @param targetUri   The uniform resource indicator of the resource access tokens are being requested for.
     * @param clientId    Identifier of the client requesting the token.
     * @param resource    Identifier of the target resource that is the recipient of the requested token.
     * @param credentials Optional: user credential to use for token acquisition.
     * @return If successful, a {@link TokenPair}; otherwise null.
     */
    public TokenPair acquireToken(final URI targetUri, final String clientId, final String resource, final Credential credentials)
    {
        throw new NotImplementedException(449285);
    }

    public TokenPair acquireToken(final URI targetUri, final String clientId, final String resource, final URI redirectUri, final Action<DeviceFlowResponse> callback)
    {
        Debug.Assert(targetUri != null && targetUri.isAbsolute(), "The targetUri parameter is null or invalid");
        Debug.Assert(!StringHelper.isNullOrWhiteSpace(clientId), "The clientId parameter is null or empty");
        Debug.Assert(!StringHelper.isNullOrWhiteSpace(resource), "The resource parameter is null or empty");
        Debug.Assert(callback != null, "The callback parameter is null");

        Trace.writeLine("AzureAuthority::acquireToken");

        _azureDeviceFlow.setResource(resource);
        _azureDeviceFlow.setRedirectUri(redirectUri);
        final StringBuilder sb = new StringBuilder(authorityHostUrl);
        sb.append("/oauth2/devicecode");
        final URI deviceEndpoint = URI.create(sb.toString());
        final DeviceFlowResponse response = _azureDeviceFlow.requestAuthorization(deviceEndpoint, clientId, null);

        callback.call(response);

        TokenPair tokens = null;
        final URI tokenEndpoint = createTokenEndpointUri(authorityHostUrl);
        try
        {
            tokens = _azureDeviceFlow.requestToken(tokenEndpoint, clientId, response);

            Trace.writeLine("   token acquisition succeeded.");
        }
        catch (final AuthorizationException e)
        {
            Trace.writeLine("   token acquisition failed: ", e);
        }
        return tokens;
    }

    /**
     * Acquires an access token from the authority using a previously acquired refresh token.
     *
     * @param targetUri    The uniform resource indicator of the resource access tokens are being requested for.
     * @param clientId     Identifier of the client requesting the token.
     * @param resource     Identifier of the target resource that is the recipient of the requested token.
     * @param refreshToken The {@link Token} of type {@link TokenType#Refresh}.
     * @return If successful, a {@link TokenPair}; otherwise null.
     */
    public TokenPair acquireTokenByRefreshToken(final URI targetUri, final String clientId, final String resource, final Token refreshToken)
    {
        throw new NotImplementedException(449243);
    }

    String acquireAuthorizationCode(final String resource, final String clientId, final URI redirectUri, final String queryParameters)
    {
        final String expectedState = UUID.randomUUID().toString();
        String authorizationCode = null;
        final String errorMessage = "Authorization code could not be obtained: ";
        try
        {
            final URI authorizationEndpoint = createAuthorizationEndpointUri(authorityHostUrl, resource, clientId, redirectUri, UserIdentifier.ANY_USER, expectedState, PromptBehavior.ALWAYS, queryParameters);
            final ProviderScanner providerScanner = (ProviderScanner) _userAgent;
            if (!providerScanner.hasCompatibleProvider())
            {
                final Map<Provider, List<String>> unmetRequirements = providerScanner.getUnmetProviderRequirements();
                final StringBuilder sb = new StringBuilder();
                UserAgentImpl.describeUnmetRequirements(unmetRequirements, sb);
                Trace.writeLine(sb.toString());
                return null;
            }
            final AuthorizationResponse response = _userAgent.requestAuthorizationCode(authorizationEndpoint, redirectUri);
            authorizationCode = response.getCode();
            // verify that the authorization response gave us the state we sent in the authz endpoint URI
            final String actualState = response.getState();
            if (!expectedState.equals(actualState))
            {
                // the states are somehow different; better to assume malice and ignore the authz code
                authorizationCode = null;
            }
        }
        catch (final AuthorizationException e)
        {
            Trace.writeLine(errorMessage, e);
        }
        return authorizationCode;
    }

    static URI createAuthorizationEndpointUri(final String authorityHostUrl, final String resource, final String clientId, final URI redirectUri, final UserIdentifier userId, final String state, final PromptBehavior promptBehavior, final String queryParameters)
    {
        final QueryString qs = new QueryString();
        qs.put(OAuthParameter.RESOURCE, resource);
        qs.put(OAuthParameter.CLIENT_ID, clientId);
        qs.put(OAuthParameter.RESPONSE_TYPE, OAuthParameter.CODE);
        qs.put(OAuthParameter.REDIRECT_URI, redirectUri.toString());

        if (!userId.isAnyUser()
            && (userId.getType() == UserIdentifierType.OPTIONAL_DISPLAYABLE_ID
                || userId.getType() == UserIdentifierType.REQUIRED_DISPLAYABLE_ID))
        {
            qs.put(OAuthParameter.LOGIN_HINT, userId.getId());
        }

        if (state != null)
        {
            qs.put(OAuthParameter.STATE, state);
        }

        String promptValue = null;
        switch (promptBehavior)
        {
            case ALWAYS:
                promptValue = PromptValue.LOGIN;
                break;
            case NEVER:
                promptValue = PromptValue.ATTEMPT_NONE;
                break;
            case REFRESH_SESSION:
                throw new NotImplementedException(449280, "implement when oauth2-useragent supports persistent cookies");
        }
        if (promptValue != null)
        {
            qs.put(OAuthParameter.PROMPT, promptValue);
        }

        final StringBuilder sb = new StringBuilder(authorityHostUrl);
        sb.append("/oauth2/authorize?");
        sb.append(qs.toString());
        if (!StringHelper.isNullOrWhiteSpace(queryParameters))
        {
            // TODO: 449282: ADAL.NET checks if queryParameters contains any duplicate parameters
            int start = (queryParameters.charAt(0) == '&') ? 1 : 0;
            sb.append('&').append(queryParameters, start, queryParameters.length());
        }
        final URI result;
        try
        {
            result = new URI(sb.toString());
        }
        catch (final URISyntaxException e)
        {
            throw new Error(e);
        }
        return result;
    }

    static URI createTokenEndpointUri(final String authorityHostUrl)
    {
        final StringBuilder sb = new StringBuilder(authorityHostUrl);
        sb.append("/oauth2/token");
        final URI result;
        try
        {
            result = new URI(sb.toString());
        }
        catch (final URISyntaxException e)
        {
            throw new Error(e);
        }
        return result;
    }

    static StringContent createTokenRequest(final String resource, final String clientId, final String authorizationCode, final URI redirectUri, final UUID correlationId)
    {
        final QueryString qs = new QueryString();
        qs.put(OAuthParameter.RESOURCE, resource);
        qs.put(OAuthParameter.CLIENT_ID, clientId);
        qs.put(OAuthParameter.GRANT_TYPE, OAuthParameter.AUTHORIZATION_CODE);
        qs.put(OAuthParameter.CODE, authorizationCode);
        qs.put(OAuthParameter.REDIRECT_URI, redirectUri.toString());
        if (correlationId != null && !Guid.Empty.equals(correlationId))
        {
            qs.put(OAuthParameter.CORRELATION_ID, correlationId.toString());
            qs.put(OAuthParameter.REQUEST_CORRELATION_ID_IN_RESPONSE, "true");
        }
        final StringContent result = StringContent.createUrlEncoded(qs);
        return result;
    }

    public static String getAuthorityUrl(final UUID tenantId)
    {
        return String.format("%1$s/%2$s", AuthorityHostUrlBase, tenantId.toString());
    }
}


<|start_filename|>src/main/java/com/microsoft/alm/gitcredentialmanager/OperationArguments.java<|end_filename|>
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root.

package com.microsoft.alm.gitcredentialmanager;

import com.microsoft.alm.helpers.Debug;
import com.microsoft.alm.helpers.ObjectExtensions;
import com.microsoft.alm.helpers.StringHelper;
import com.microsoft.alm.secret.Credential;

import java.io.BufferedReader;
import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;

final class OperationArguments
{
    OperationArguments(final BufferedReader stdin) throws IOException, URISyntaxException
    {
        Debug.Assert(stdin != null, "The stdin parameter is null");

        this.Authority = AuthorityType.Auto;
        this.Interactivity = com.microsoft.alm.gitcredentialmanager.Interactivity.Auto;
        this.ValidateCredentials = true;
        this.WriteLog = false;
        this.EraseOsxKeyChain = true;

        String protocol = null;
        String host = null;
        String path = null;
        String line;
        while (!StringHelper.isNullOrWhiteSpace((line = stdin.readLine())))
        {
            String[] pair = line.split("=", 2);

            if (pair.length == 2)
            {
                // Java doesn't support a switch on a String!
                if ("protocol".equals(pair[0]))
                {
                    protocol = pair[1];
                }
                else if ("host".equals(pair[0]))
                {
                    host = pair[1];
                }
                else if ("path".equals(pair[0]))
                {
                    path = pair[1];
                }
                else if ("username".equals(pair[0]))
                {
                    userName = pair[1];
                }
                else if ("password".equals(pair[0]))
                {
                    password = pair[1];
                }
            }
        }
        Protocol = protocol;
        Host = host;
        Path = path;

        if (this.Protocol != null && this.Host != null)
        {
            this.TargetUri = new URI(String.format("%1$s://%2$s/", this.Protocol, this.Host));
        }
        else
        {
            this.TargetUri = null;
        }
    }

    public final String Protocol;
    public final String Host;
    public final String Path;
    public final URI TargetUri;

    private String userName;
    public String getUserName()
    {
        return userName;
    }

    private String password;
    public String getPassword()
    {
        return password;
    }

    public AuthorityType Authority;

    public Interactivity Interactivity;

    public boolean ValidateCredentials;

    public boolean WriteLog;

    public boolean EraseOsxKeyChain;

    public boolean CanFallbackToInsecureStore;

    public void setCredentials(final Credential credentials)
    {
        this.userName = credentials.Username;
        this.password = <PASSWORD>.Password;
    }

    @Override
    public String toString()
    {
        final StringBuilder builder = new StringBuilder();

        builder.append("protocol=")
                .append(ObjectExtensions.coalesce(this.Protocol, StringHelper.Empty))
                .append("\n");
        builder.append("host=")
                .append(ObjectExtensions.coalesce(this.Host, StringHelper.Empty))
                .append("\n");
        builder.append("path=")
                .append(ObjectExtensions.coalesce(this.Path, StringHelper.Empty))
                .append("\n");
        // only write out username if we know it
        if (this.userName != null)
        {
            builder.append("username=")
                    .append(this.userName)
                    .append("\n");
        }
        // only write out password if we know it
        if (this.password != null)
        {
            builder.append("password=")
                    .append(this.password)
                    .append("\n");
        }

        return builder.toString();
    }
}


<|start_filename|>src/main/java/com/microsoft/alm/helpers/Trace.java<|end_filename|>
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root.

package com.microsoft.alm.helpers;

import java.io.PrintStream;
import java.util.ArrayList;
import java.util.List;

/**
 * Equivalent to System.Diagnostics.Trace
 */
// TODO: 449522: Wire this up to some logging framework?
public class Trace
{
    private static final List<PrintStream> listeners = new ArrayList<PrintStream>();

    public static void flush()
    {
        for (final PrintStream listener : listeners)
        {
            listener.flush();
        }
    }

    public static List<PrintStream> getListeners()
    {
        return listeners;
    }

    public static void writeLine(final String message)
    {
        for (final PrintStream listener : listeners)
        {
            listener.println(message);
        }
    }

    public static void writeLine(final String message, final Throwable throwable)
    {
        writeLine(message + throwable.toString());
    }
}


<|start_filename|>src/main/java/com/microsoft/alm/gitcredentialmanager/InsecureStore.java<|end_filename|>
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root.

package com.microsoft.alm.gitcredentialmanager;

import com.microsoft.alm.authentication.ISecureStore;
import com.microsoft.alm.helpers.IOHelper;
import com.microsoft.alm.helpers.Trace;
import com.microsoft.alm.helpers.XmlHelper;
import com.microsoft.alm.secret.Credential;
import com.microsoft.alm.secret.Token;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.w3c.dom.Text;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.Map;

public class InsecureStore implements ISecureStore
{
    static final String MIGRATION_SUFFIX = ".old";
    private final File backingFile;

    final Map<String, Token> Tokens = new HashMap<String, Token>();
    final Map<String, Credential> Credentials = new HashMap<String, Credential>();

    private boolean isEnabled = true;

    /**
     * Creates an instance that only keeps the values in memory, never touching a file.
     *
     * @deprecated If you need an in-memory store, use SecretCache.
     */
    @Deprecated
    public InsecureStore()
    {
        this(null);
    }

    /**
     * Creates an instance that reads from and writes to the specified backingFile.
     *
     * @param backingFile the file to read from and write to.  Does not need to exist first.
     */
    public InsecureStore(final File backingFile)
    {
        this.backingFile = backingFile;
        reload();
    }

    void reload()
    {
        if (backingFile != null && backingFile.isFile() && backingFile.length() > 0)
        {
            FileInputStream fis = null;
            try
            {
                fis = new FileInputStream(backingFile);
                final InsecureStore clone = fromXml(fis);
                if (clone != null)
                {
                    this.Tokens.clear();
                    this.Tokens.putAll(clone.Tokens);

                    this.Credentials.clear();
                    this.Credentials.putAll(clone.Credentials);
                }
            }
            catch (final FileNotFoundException e)
            {
                Trace.writeLine("backingFile '" + backingFile.getAbsolutePath() + "' did not exist.");
            }
            finally
            {
                IOHelper.closeQuietly(fis);
            }
        }
    }

    static InsecureStore fromXml(final InputStream source)
    {
        try
        {
            final InsecureStore result = new InsecureStore(null);
            final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            final DocumentBuilder builder = dbf.newDocumentBuilder();
            final Document document = builder.parse(source);
            final Element insecureStoreElement = document.getDocumentElement();

            final NodeList tokensOrCredentialsList = insecureStoreElement.getChildNodes();
            for (int toc = 0; toc < tokensOrCredentialsList.getLength(); toc++)
            {
                final Node tokensOrCredentials = tokensOrCredentialsList.item(toc);
                if (tokensOrCredentials.getNodeType() != Node.ELEMENT_NODE)
                    continue;
                if ("Tokens".equals(tokensOrCredentials.getNodeName()))
                {
                    result.Tokens.clear();
                }
                else if ("Credentials".equals(tokensOrCredentials.getNodeName()))
                {
                    result.Credentials.clear();
                }
                else continue;
                final NodeList entryList = tokensOrCredentials.getChildNodes();
                for (int e = 0; e < entryList.getLength(); e++)
                {
                    final Node entryNode = entryList.item(e);
                    if (entryNode.getNodeType() != Node.ELEMENT_NODE || !"entry".equals(entryNode.getNodeName())) continue;
                    if ("Tokens".equals(tokensOrCredentials.getNodeName()))
                    {
                        loadToken(result, entryNode);
                    }
                    else if ("Credentials".equals(tokensOrCredentials.getNodeName()))
                    {
                        loadCredential(result, entryNode);
                    }
                }
            }
            return result;
        }
        catch (final Exception e)
        {
            Trace.writeLine("Warning: unable to deserialize InsecureStore. Is the file corrupted?");
            return null;
        }
    }

    private static void loadCredential(final InsecureStore result, final Node entryNode)
    {
        String key = null;
        Credential value = null;
        final NodeList keyOrValueList = entryNode.getChildNodes();
        for (int kov = 0; kov < keyOrValueList.getLength(); kov++)
        {
            final Node keyOrValueNode = keyOrValueList.item(kov);
            if (keyOrValueNode.getNodeType() != Node.ELEMENT_NODE) continue;

            final String keyOrValueName = keyOrValueNode.getNodeName();
            if ("key".equals(keyOrValueName))
            {
                key = XmlHelper.getText(keyOrValueNode);
            }
            else if ("value".equals(keyOrValueName))
            {
                value = Credential.fromXml(keyOrValueNode);
            }
        }
        result.Credentials.put(key, value);
    }

    private static void loadToken(final InsecureStore result, final Node entryNode)
    {
        String key = null;
        Token value = null;
        final NodeList keyOrValueList = entryNode.getChildNodes();
        for (int kov = 0; kov < keyOrValueList.getLength(); kov++)
        {
            final Node keyOrValueNode = keyOrValueList.item(kov);
            if (keyOrValueNode.getNodeType() != Node.ELEMENT_NODE) continue;
            final String keyOrValueName = keyOrValueNode.getNodeName();
            if ("key".equals(keyOrValueName))
            {
                key = XmlHelper.getText(keyOrValueNode);
            }
            else if ("value".equals(keyOrValueName))
            {
                value = Token.fromXml(keyOrValueNode);
            }
        }
        result.Tokens.put(key, value);
    }

    void toXml(final OutputStream destination)
    {
        try
        {
            final DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            final DocumentBuilder builder = dbf.newDocumentBuilder();
            final Document document = builder.newDocument();

            final Element insecureStoreNode = document.createElement("insecureStore");
            insecureStoreNode.appendChild(createTokensNode(document));
            insecureStoreNode.appendChild(createCredentialsNode(document));
            document.appendChild(insecureStoreNode);

            final TransformerFactory tf = TransformerFactory.newInstance();
            final Transformer transformer = tf.newTransformer();
            transformer.setOutputProperty(OutputKeys.INDENT, "yes");
            transformer.setOutputProperty(OutputKeys.STANDALONE, "yes");
            //https://johnsonsolutions.blogspot.ca/2007/08/xml-transformer-indent-doesnt-work-with.html
            transformer.setOutputProperty("{http://xml.apache.org/xslt}indent-amount", "4");
            transformer.transform(new DOMSource(document), new StreamResult(destination));
        }
        catch (final Exception e)
        {
            throw new Error(e);
        }
    }

    private Element createTokensNode(final Document document)
    {
        final Element tokensNode = document.createElement("Tokens");
        for (final Map.Entry<String, Token> entry : Tokens.entrySet())
        {
            final Element entryNode = document.createElement("entry");

            final Element keyNode = document.createElement("key");
            final Text keyValue = document.createTextNode(entry.getKey());
            keyNode.appendChild(keyValue);
            entryNode.appendChild(keyNode);

            final Token value = entry.getValue();
            if (value != null)
            {
                final Element valueNode = value.toXml(document);

                entryNode.appendChild(valueNode);
            }

            tokensNode.appendChild(entryNode);
        }
        return tokensNode;
    }

    private Element createCredentialsNode(final Document document)
    {
        final Element credentialsNode = document.createElement("Credentials");
        for (final Map.Entry<String, Credential> entry : Credentials.entrySet())
        {
            final Element entryNode = document.createElement("entry");

            final Element keyNode = document.createElement("key");
            final Text keyValue = document.createTextNode(entry.getKey());
            keyNode.appendChild(keyValue);
            entryNode.appendChild(keyNode);

            final Credential value = entry.getValue();
            if (value != null)
            {
                final Element valueNode = value.toXml(document);

                entryNode.appendChild(valueNode);
            }
            credentialsNode.appendChild(entryNode);
        }
        return credentialsNode;
    }

    private void ensureEnabled()
    {
        if (!isEnabled)
        {
            throw new IllegalStateException("This InsecureStore has been disabled.");
        }
    }

    @Override
    public synchronized void delete(final String targetName)
    {
        ensureEnabled();

        if (Tokens.containsKey(targetName))
        {
            Tokens.remove(targetName);
        }
        else if (Credentials.containsKey(targetName))
        {
            Credentials.remove(targetName);
        }
    }

    @Override
    public synchronized Credential readCredentials(final String targetName)
    {
        ensureEnabled();

        return Credentials.get(targetName);
    }

    @Override
    public synchronized Token readToken(final String targetName)
    {
        ensureEnabled();

        return Tokens.get(targetName);
    }

    @Override
    public synchronized void writeCredential(final String targetName, final Credential credentials)
    {
        ensureEnabled();

        Credentials.put(targetName, credentials);
    }

    @Override
    public synchronized void writeToken(final String targetName, final Token token)
    {
        ensureEnabled();

        Tokens.put(targetName, token);
    }

    /**
     * Exports all tokens and credentials to the specified {@link ISecureStore}
     * and then the polite thing to do is to rename the backing file
     * and prevent further use via this instance.
     * This will still make a downgrade possible
     * and the archival of the old file will be the user's responsibility.
     *
     * @param destination the {@link ISecureStore} that will replace this {@link InsecureStore}
     */
    public synchronized void migrateAndDisable(final ISecureStore destination)
    {
        ensureEnabled();

        for (final Map.Entry<String, Token> pair : Tokens.entrySet())
        {
            final String targetName = pair.getKey();
            final Token token = pair.getValue();
            destination.writeToken(targetName, token);
        }

        for (final Map.Entry<String, Credential> pair : Credentials.entrySet())
        {
            final String targetName = pair.getKey();
            final Credential credential = pair.getValue();
            destination.writeCredential(targetName, credential);
        }

        if (backingFile != null)
        {
            // TODO: Add a parameter to control whether a rename or a delete should take place
            final File disabledBackingFile = new File(backingFile.getAbsolutePath() + MIGRATION_SUFFIX);
            final boolean wasRenamed = backingFile.renameTo(disabledBackingFile);
            if (!wasRenamed)
            {
                final String renameFailureTemplate = "Unable to rename '%1$s' to '%2$s' after migrating its contents.";
                final String renameFailureMessage = String.format(renameFailureTemplate, backingFile.getAbsolutePath(), disabledBackingFile.getAbsolutePath());
                throw new Error(renameFailureMessage);
            }
        }

        isEnabled = false;
    }
}


<|start_filename|>src/test/java/com/microsoft/alm/gitcredentialmanager/OperationArgumentsTest.java<|end_filename|>
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root.

package com.microsoft.alm.gitcredentialmanager;

import org.junit.Assert;
import org.junit.Test;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.StringReader;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.List;

import static org.hamcrest.CoreMatchers.is;

public class OperationArgumentsTest
{
    @Test
    public void typical() throws IOException, URISyntaxException
    {
        final String input = "protocol=https\n" +
                "host=example.visualstudio.com\n" +
                "path=path\n" +
                "username=userName\n" +
                "password=<PASSWORD>" +
                "";

        final BufferedReader br = new BufferedReader(new StringReader(input));
        OperationArguments cut;
        try
        {
            cut = new OperationArguments(br);
        }
        finally
        {
            br.close();
        }

        Assert.assertEquals("https", cut.Protocol);
        Assert.assertEquals("example.visualstudio.com", cut.Host);
        Assert.assertEquals("https://example.visualstudio.com/", cut.TargetUri.toString());
        Assert.assertEquals("path", cut.Path);
        Assert.assertEquals("userName", cut.getUserName());
        Assert.assertEquals("incorrect", cut.getPassword());

        final List<String> expected = readLines(input);
        final List<String> actual = readLines(cut.toString());
        Assert.assertThat(actual, is(expected));
    }

    private static List<String> readLines(final String input) throws IOException
    {
        final ArrayList<String> result = new ArrayList<String>();
        final BufferedReader br = new BufferedReader(new StringReader(input));
        try
        {
            String line;
            while ((line = br.readLine()) != null)
            {
                result.add(line);
            }
        }
        finally
        {
            br.close();
        }
        return result;
    }
}


<|start_filename|>src/main/java/com/microsoft/alm/authentication/OAuthParameter.java<|end_filename|>
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root.

package com.microsoft.alm.authentication;

class OAuthParameter
{
    static final String RESPONSE_TYPE = "response_type";
    static final String GRANT_TYPE = "grant_type";
    static final String AUTHORIZATION_CODE = "authorization_code";
    static final String DEVICE_CODE = "device_code";
    static final String USER_CODE = "user_code";
    static final String CLIENT_ID = "client_id";
    static final String REDIRECT_URI = "redirect_uri";
    static final String VERIFICATION_URI = "verification_uri";
    static final String RESOURCE = "resource";
    static final String SCOPE = "scope";
    static final String CODE = "code";
    static final String EXPIRES_IN = "expires_in";
    static final String LOGIN_HINT = "login_hint";
    static final String STATE = "state";
    static final String INTERVAL = "interval";

    static final String ERROR_CODE = "error";
    static final String ERROR_DESCRIPTION = "error_description";
    static final String ERROR_URI = "error_uri";

    static final String ERROR_AUTHORIZATION_PENDING = "authorization_pending";
    static final String ERROR_SLOW_DOWN = "slow_down";

    static final String CORRELATION_ID = "client-request-id"; // correlation id is not standard oauth2 parameter
    static final String REQUEST_CORRELATION_ID_IN_RESPONSE = "return-client-request-id"; // not standard oauth2 parameter
    static final String PROMPT = "prompt"; // prompt is not standard oauth2 parameter
}


<|start_filename|>src/main/java/com/microsoft/alm/authentication/BasicAuthentication.java<|end_filename|>
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root.

package com.microsoft.alm.authentication;

import com.microsoft.alm.helpers.Trace;
import com.microsoft.alm.secret.Credential;

import java.net.URI;
import java.util.concurrent.atomic.AtomicReference;

public class BasicAuthentication extends BaseAuthentication implements IAuthentication
{
    /**
     * Creates a new {@link BasicAuthentication} object with an underlying credential store.
     *
     * @param credentialStore
     *        The {@link ICredentialStore} to delegate to.
     */
    public BasicAuthentication(final ICredentialStore credentialStore)
    {
        if (credentialStore == null)
            throw new IllegalArgumentException("The `credentialStore` parameter is null or invalid.");

        this.CredentialStore = credentialStore;
    }

    ICredentialStore CredentialStore;

    /**
     * Deletes a {@link Credential} from the storage used by the authentication object.
     *
     * @param targetUri
     *        The uniform resource indicator used to uniquely identify the credentials.
     */
    @Override public void deleteCredentials(final URI targetUri)
    {
        BaseSecureStore.validateTargetUri(targetUri);

        Trace.writeLine("BasicAuthentication::deleteCredentials");

        this.CredentialStore.deleteCredentials(targetUri);
    }
    /**
     * Gets a {@link Credential} from the storage used by the authentication object.
     *
     * @param targetUri
     *        The uniform resource indicator used to uniquely identify the credentials.
     *
     * @param credentials
     *        If successful a {@link Credential} object from the authentication object,
     *        authority or storage; otherwise null.
     *
     * @return true if successful; otherwise false.
     */
    @Override public boolean getCredentials(final URI targetUri, final AtomicReference<Credential> credentials)
    {
        BaseSecureStore.validateTargetUri(targetUri);

        Trace.writeLine("BasicAuthentication::getCredentials");

        this.CredentialStore.readCredentials(targetUri, credentials);

        return credentials.get() != null;
    }
    /**
     * Sets a {@link Credential} in the storage used by the authentication object.
     *
     * @param targetUri
     *        The uniform resource indicator used to uniquely identify the credentials.
     *
     * @param credentials The value to be stored
     *
     * @return true if successful; otherwise false.
     */
    @Override public boolean setCredentials(final URI targetUri, final Credential credentials)
    {
        BaseSecureStore.validateTargetUri(targetUri);
        Credential.validate(credentials);

        Trace.writeLine("BasicAuthentication::setCredentials");

        this.CredentialStore.writeCredentials(targetUri, credentials);
        return true;
    }
}


<|start_filename|>src/main/java/com/microsoft/alm/authentication/IAzureAuthority.java<|end_filename|>
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root.

package com.microsoft.alm.authentication;

import com.microsoft.alm.helpers.Action;
import com.microsoft.alm.secret.Credential;
import com.microsoft.alm.secret.Token;
import com.microsoft.alm.secret.TokenPair;

import java.net.URI;

interface IAzureAuthority
{
    TokenPair acquireToken(final URI targetUri, final String clientId, final String resource, final URI redirectUri, final String queryParameters);
    TokenPair acquireToken(final URI targetUri, final String clientId, final String resource, final Credential credentials);
    TokenPair acquireToken(final URI targetUri, final String clientId, final String resource, final URI redirectUri, final Action<DeviceFlowResponse> callback);
    TokenPair acquireTokenByRefreshToken(final URI targetUri, final String clientId, final String resource, final Token refreshToken);
}


<|start_filename|>src/main/java/org/slf4j/impl/TraceLogger.java<|end_filename|>
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root.

// A lot of the code from this file was inspired from slf4j-simple's SimpleLogger class
// and do its copyright and permission notices (MIT License) are included below.
/**
 * Copyright (c) 2004-2012 QOS.ch
 * All rights reserved.
 *
 * Permission is hereby granted, free of charge, to any person obtaining
 * a copy of this software and associated documentation files (the
 * "Software"), to  deal in  the Software without  restriction, including
 * without limitation  the rights to  use, copy, modify,  merge, publish,
 * distribute,  sublicense, and/or sell  copies of  the Software,  and to
 * permit persons to whom the Software  is furnished to do so, subject to
 * the following conditions:
 *
 * The  above  copyright  notice  and  this permission  notice  shall  be
 * included in all copies or substantial portions of the Software.
 *
 * THE  SOFTWARE IS  PROVIDED  "AS  IS", WITHOUT  WARRANTY  OF ANY  KIND,
 * EXPRESS OR  IMPLIED, INCLUDING  BUT NOT LIMITED  TO THE  WARRANTIES OF
 * MERCHANTABILITY,    FITNESS    FOR    A   PARTICULAR    PURPOSE    AND
 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 * OF CONTRACT, TORT OR OTHERWISE,  ARISING FROM, OUT OF OR IN CONNECTION
 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 */

package org.slf4j.impl;

import com.microsoft.alm.helpers.Trace;
import org.slf4j.helpers.FormattingTuple;
import org.slf4j.helpers.MarkerIgnoringBase;
import org.slf4j.helpers.MessageFormatter;
import org.slf4j.spi.LocationAwareLogger;

public class TraceLogger extends MarkerIgnoringBase
{
    /** The current log level */
    protected int currentLogLevel = LocationAwareLogger.DEBUG_INT;

    TraceLogger(final String name)
    {
        this.name = name;
    }

    /**
     * Is the given log level currently enabled?
     *
     * @param logLevel is this level enabled?
     *
     * @return {@code true} if the specified logLevel is enabled;
     *         {@code false} otherwise.
     */
    protected boolean isLevelEnabled(final int logLevel) {
        // log level are numerically ordered so can use simple numeric
        // comparison
        return (logLevel >= currentLogLevel);
    }

    /**
     * For formatted messages, first substitute arguments and then log.
     */
    private void formatAndLog(final int level, final String format, final Object arg1, final Object arg2) {
        if (!isLevelEnabled(level)) {
            return;
        }
        FormattingTuple tp = MessageFormatter.format(format, arg1, arg2);
        log(level, tp.getMessage(), tp.getThrowable());
    }

    /**
     * For formatted messages, first substitute arguments and then log.
     *
     */
    private void formatAndLog(final int level, final String format, final Object... arguments) {
        if (!isLevelEnabled(level)) {
            return;
        }
        FormattingTuple tp = MessageFormatter.arrayFormat(format, arguments);
        log(level, tp.getMessage(), tp.getThrowable());
    }

    private void log(final int level, final String message, final Throwable throwable)
    {
        if (!isLevelEnabled(level)) {
            return;
        }
        if (throwable != null)
        {
            Trace.writeLine(message, throwable);
        }
        else
        {
            Trace.writeLine(message);
        }
    }

    @Override
    public boolean isTraceEnabled()
    {
        return isLevelEnabled(LocationAwareLogger.TRACE_INT);
    }

    @Override
    public void trace(final String msg)
    {
        log(LocationAwareLogger.TRACE_INT, msg, null);
    }

    @Override
    public void trace(final String format, final Object arg)
    {
        formatAndLog(LocationAwareLogger.TRACE_INT, format, arg, null);
    }

    @Override
    public void trace(final String format, final Object arg1, final Object arg2)
    {
        formatAndLog(LocationAwareLogger.TRACE_INT, format, arg1, arg2);
    }

    @Override
    public void trace(final String format, final Object... arguments)
    {
        formatAndLog(LocationAwareLogger.TRACE_INT, format, arguments);
    }

    @Override
    public void trace(final String msg, final Throwable t)
    {
        log(LocationAwareLogger.TRACE_INT, msg, t);
    }

    @Override
    public boolean isDebugEnabled()
    {
        return isLevelEnabled(LocationAwareLogger.DEBUG_INT);
    }

    @Override
    public void debug(final String msg)
    {
        log(LocationAwareLogger.DEBUG_INT, msg, null);
    }

    @Override
    public void debug(final String format, final Object arg)
    {
        formatAndLog(LocationAwareLogger.DEBUG_INT, format, arg, null);
    }

    @Override
    public void debug(final String format, final Object arg1, final Object arg2)
    {
        formatAndLog(LocationAwareLogger.DEBUG_INT, format, arg1, arg2);
    }

    @Override
    public void debug(final String format, final Object... arguments)
    {
        formatAndLog(LocationAwareLogger.DEBUG_INT, format, arguments);
    }

    @Override
    public void debug(final String msg, final Throwable t)
    {
        log(LocationAwareLogger.DEBUG_INT, msg, t);
    }

    @Override
    public boolean isInfoEnabled()
    {
        return isLevelEnabled(LocationAwareLogger.INFO_INT);
    }

    @Override
    public void info(final String msg)
    {
        log(LocationAwareLogger.INFO_INT, msg, null);
    }

    @Override
    public void info(final String format, final Object arg)
    {
        formatAndLog(LocationAwareLogger.INFO_INT, format, arg, null);
    }

    @Override
    public void info(final String format, final Object arg1, final Object arg2)
    {
        formatAndLog(LocationAwareLogger.INFO_INT, format, arg1, arg2);
    }

    @Override
    public void info(final String format, final Object... arguments)
    {
        formatAndLog(LocationAwareLogger.INFO_INT, format, arguments);
    }

    @Override
    public void info(final String msg, final Throwable t)
    {
        log(LocationAwareLogger.INFO_INT, msg, t);
    }

    @Override
    public boolean isWarnEnabled()
    {
        return isLevelEnabled(LocationAwareLogger.WARN_INT);
    }

    @Override
    public void warn(final String msg)
    {
        log(LocationAwareLogger.WARN_INT, msg, null);
    }

    @Override
    public void warn(final String format, final Object arg)
    {
        formatAndLog(LocationAwareLogger.WARN_INT, format, arg, null);
    }

    @Override
    public void warn(final String format, final Object arg1, final Object arg2)
    {
        formatAndLog(LocationAwareLogger.WARN_INT, format, arg1, arg2);
    }

    @Override
    public void warn(final String format, final Object... arguments)
    {
        formatAndLog(LocationAwareLogger.WARN_INT, format, arguments);
    }

    @Override
    public void warn(final String msg, final Throwable t)
    {
        log(LocationAwareLogger.WARN_INT, msg, t);
    }

    @Override
    public boolean isErrorEnabled()
    {
        return isLevelEnabled(LocationAwareLogger.ERROR_INT);
    }

    @Override
    public void error(final String msg)
    {
        log(LocationAwareLogger.ERROR_INT, msg, null);
    }

    @Override
    public void error(final String format, final Object arg)
    {
        formatAndLog(LocationAwareLogger.ERROR_INT, format, arg, null);
    }

    @Override
    public void error(final String format, final Object arg1, final Object arg2)
    {
        formatAndLog(LocationAwareLogger.ERROR_INT, format, arg1, arg2);
    }

    @Override
    public void error(final String format, final Object... arguments)
    {
        formatAndLog(LocationAwareLogger.ERROR_INT, format, arguments);
    }

    @Override
    public void error(final String msg, final Throwable t)
    {
        log(LocationAwareLogger.ERROR_INT, msg, t);
    }
}


<|start_filename|>src/main/java/com/microsoft/alm/authentication/VsoAdalTokenCache.java<|end_filename|>
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root.

package com.microsoft.alm.authentication;

import com.microsoft.alm.helpers.NotImplementedException;

class VsoAdalTokenCache // TODO: 449520: extends TokenCache
{
    private static final String AdalCachePath = "Microsoft\\VSCommon\\VSAccountManagement";
    private static final String AdalCacheFile = "AdalCache.cache";

    /**
     * Default constructor.
     */
    public VsoAdalTokenCache()
    {
        throw new NotImplementedException(449520);
    }

    /**
     * Constructor receiving state of the cache.
     *
     * @param state Current state of the cache as a blob.
     */
    public VsoAdalTokenCache(final byte[] state)
    {
        throw new NotImplementedException(449520);
    }

    private final String _cacheFilePath;

    private final Object lock = new Object();

    // TODO: 449520: arguments should be TokenCacheNotificationArgs
    private void afterAccessNotification(final Object args)
    {
        throw new NotImplementedException(449520);
    }


    private void beforeAccessNotification(final Object args)
    {
        throw new NotImplementedException(449520);
    }
}


<|start_filename|>src/test/java/com/microsoft/alm/authentication/ConfigurationTest.java<|end_filename|>
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root.

package com.microsoft.alm.authentication;

import org.junit.Assert;
import org.junit.Test;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.atomic.AtomicReference;

public class ConfigurationTest
{
    @Test
    public void parseGitConfig_simple() throws Exception
    {
        final String input = "\n" +
                "[core]\n" +
                "    autocrlf = false\n" +
                "";

        final Map<String, String> values = testParseGitConfig(input);

        Assert.assertEquals("false", values.get("core.autocrlf"));
    }

    @Test
    public void parseGitConfig_overwritesValues() throws Exception
    {
        final String input = "\n" +
                "[core]\n" +
                "    autocrlf = true\n" +
                "    autocrlf = FileNotFound\n" +
                "    autocrlf = false\n" +
                "";

        final Map<String, String> values = testParseGitConfig(input);

        Assert.assertEquals("false", values.get("core.autocrlf"));
    }

    @Test
    public void parseGitConfig_partiallyQuoted() throws Exception
    {
        final String input = "\n" +
                "[core \"oneQuote]\n" +
                "    autocrlf = \"false\n" +
                "";

        final Map<String, String> values = testParseGitConfig(input);

        Assert.assertEquals("false", values.get("core.oneQuote.autocrlf"));
    }

    @Test
    public void parseGitConfig_sampleFile() throws Exception
    {
        final TreeMap<String, String> values = new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER);
        final Class<? extends ConfigurationTest> me = this.getClass();

        final BufferedReader br = new BufferedReader(new InputStreamReader(me.getResourceAsStream("sample.gitconfig")));
        try
        {
            Configuration.parseGitConfig(br, values);
        }
        finally
        {
            br.close();
        }

        Assert.assertEquals(36, values.size());
        Assert.assertEquals("The quotes remained.", "\\\"C:/Utils/Compare It!/wincmp3.exe\\\" \\\"$(cygpath -w \\\"$LOCAL\\\")\\\" \\\"$(cygpath -w \\\"$REMOTE\\\")\\\"", values.get("difftool.cygcompareit.cmd"));
        Assert.assertEquals("The quotes were stripped.", "!f() { git fetch origin && git checkout -b $1 origin/master --no-track; }; f", values.get("alias.cob"));
    }

    @Test
    public void readThroughPublicMethods() throws IOException, URISyntaxException
    {
        final String input = "\n" +
            "[core]\n" +
            "    autocrlf = false\n" +
            "[credential \"microsoft.visualstudio.com\"]\n" +
            "    authority = AAD\n" +
            "[credential \"visualstudio.com\"]\n" +
            "    authority = MSA\n" +
            "[credential \"https://ntlm.visualstudio.com\"]\n" +
            "    authority = NTLM\n" +
            "[credential]\n" +
            "    helper = manager\n" +
            "";
        Configuration cut;

        final BufferedReader br = new BufferedReader(new StringReader(input));
        try
        {
            cut = new Configuration(br);
        }
        finally
        {
            br.close();
        }

        Assert.assertEquals(true, cut.containsKey("CoRe.AuToCrLf"));
        Assert.assertEquals("false", cut.get("CoRe.AuToCrLf"));

        AtomicReference<Configuration.Entry> entryRef = new AtomicReference<Configuration.Entry>();
        Assert.assertEquals(true, cut.tryGetEntry("core", (String) null, "autocrlf", entryRef));
        Assert.assertEquals("false", entryRef.get().Value);

        Assert.assertEquals(true, cut.tryGetEntry("credential", new URI("https://microsoft.visualstudio.com"), "authority", entryRef));
        Assert.assertEquals("AAD", entryRef.get().Value);

        Assert.assertEquals(true, cut.tryGetEntry("credential", new URI("https://mseng.visualstudio.com"), "authority", entryRef));
        Assert.assertEquals("MSA", entryRef.get().Value);

        Assert.assertEquals(true, cut.tryGetEntry("credential", new URI("https://ntlm.visualstudio.com"), "authority", entryRef));
        Assert.assertEquals("NTLM", entryRef.get().Value);
    }

    private static Map<String, String> testParseGitConfig(final String input) throws IOException
    {
        final TreeMap<String, String> values = new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER);

        final BufferedReader br = new BufferedReader(new StringReader(input));
        try
        {
            Configuration.parseGitConfig(br, values);
        }
        finally
        {
            br.close();
        }
        return values;
    }
}


<|start_filename|>src/main/java/com/microsoft/alm/authentication/BaseVsoAuthentication.java<|end_filename|>
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root.

package com.microsoft.alm.authentication;

import com.microsoft.alm.helpers.Action;
import com.microsoft.alm.helpers.Debug;
import com.microsoft.alm.helpers.Guid;
import com.microsoft.alm.helpers.HttpClient;
import com.microsoft.alm.helpers.StringHelper;
import com.microsoft.alm.helpers.Trace;
import com.microsoft.alm.secret.Credential;
import com.microsoft.alm.secret.Token;
import com.microsoft.alm.secret.TokenPair;
import com.microsoft.alm.secret.VsoTokenScope;

import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URI;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicReference;

/**
 * Base functionality for performing authentication operations against Visual Studio Online.
 */
public abstract class BaseVsoAuthentication extends BaseAuthentication
{
    public static final String DefaultResource = "499b84ac-1321-427f-aa17-267ca6975798";
    public static final String DefaultClientId = "97877f11-0fc6-4aee-b1ff-febb0519dd00";
    public static final URI RedirectUri = URI.create("https://java.visualstudio.com");

    protected static final String AdalRefreshPrefix = "ada";

    private BaseVsoAuthentication(final VsoTokenScope tokenScope, final ICredentialStore personalAccessTokenStore, final ITokenStore vsoIdeTokenCache, final ITokenStore adaRefreshTokenStore, final IVsoAuthority vsoAuthority)
    {
        if (tokenScope == null)
            throw new IllegalArgumentException("The `tokenScope` parameter is null.");
        if (personalAccessTokenStore == null)
            throw new IllegalArgumentException("The `personalAccessTokenStore` parameter is null.");

        this.ClientId = DefaultClientId;
        this.Resource = DefaultResource;
        this.TokenScope = tokenScope;
        this.VsoIdeTokenCache = vsoIdeTokenCache;
        this.PersonalAccessTokenStore = personalAccessTokenStore;
        this.AdaRefreshTokenStore = adaRefreshTokenStore != null ? adaRefreshTokenStore : new SecretCache(AdalRefreshPrefix);
        this.VsoAuthority = vsoAuthority;
    }
    /**
     * Invoked by a derived classes implementation. Allows custom back-end implementations to be used.
     *
     * @param tokenScope The desired scope of the acquired personal access token(s).
     * @param personalAccessTokenStore The secret store for acquired personal access token(s).
     * @param adaRefreshTokenStore The secret store for acquired Azure refresh token(s).
     */
    protected BaseVsoAuthentication(
            final VsoTokenScope tokenScope,
            final ICredentialStore personalAccessTokenStore,
            final ITokenStore adaRefreshTokenStore
    )
    {
        this(tokenScope, personalAccessTokenStore, new SecretCache("registry"), adaRefreshTokenStore, new VsoAzureAuthority());
    }
    BaseVsoAuthentication(
            final ICredentialStore personalAccessTokenStore,
            final ITokenStore adaRefreshTokenStore,
            final ITokenStore vsoIdeTokenCache,
            final IVsoAuthority vsoAuthority)
    {
        this(VsoTokenScope.ProfileRead, personalAccessTokenStore, vsoIdeTokenCache, adaRefreshTokenStore, vsoAuthority);

        Debug.Assert(adaRefreshTokenStore != null, "The adaRefreshTokenStore parameter is null.");
        Debug.Assert(vsoIdeTokenCache != null, "The vsoIdeTokenCache parameter is null.");
        Debug.Assert(vsoAuthority != null, "The vsoAuthority parameter is null.");
    }

    /**
     * The application client identity by which access will be requested.
     */
    public final String ClientId;
    /**
     * The Azure resource for which access will be requested.
     */
    public final String Resource;
    /**
     * The desired scope of the authentication token to be requested.
     */
    public final VsoTokenScope TokenScope;

    final ITokenStore VsoIdeTokenCache;

    ICredentialStore PersonalAccessTokenStore;
    ITokenStore AdaRefreshTokenStore;
    IVsoAuthority VsoAuthority;
    UUID TenantId;

    /**
     * Deletes a set of stored credentials by their target resource.
     *
     * @param targetUri The 'key' by which to identify credentials.
     */
    @Override public void deleteCredentials(final URI targetUri)
    {
        BaseSecureStore.validateTargetUri(targetUri);

        Trace.writeLine("BaseVsoAuthentication::deleteCredentials");

        AtomicReference<Credential> credentials = new AtomicReference<Credential>();
        AtomicReference<Token> token = new AtomicReference<Token>();
        if (this.PersonalAccessTokenStore.readCredentials(targetUri, credentials))
        {
            this.PersonalAccessTokenStore.deleteCredentials(targetUri);
        }
        else if (this.AdaRefreshTokenStore.readToken(targetUri, token))
        {
            this.AdaRefreshTokenStore.deleteToken(targetUri);
        }
    }

    /**
     * Attempts to get a set of credentials from storage by their target resource.
     *
     * @param targetUri   The 'key' by which to identify credentials.
     * @param credentials Credentials associated with the URI if successful; null otherwise.
     * @return True if successful; false otherwise.
     */
    @Override public boolean getCredentials(final URI targetUri, final AtomicReference<Credential> credentials)
    {
        BaseSecureStore.validateTargetUri(targetUri);

        Trace.writeLine("BaseVsoAuthentication::getCredentials");

        if (this.PersonalAccessTokenStore.readCredentials(targetUri, credentials))
        {
            Trace.writeLine("   successfully retrieved stored credentials, updating credential cache");
        }

        return credentials.get() != null;
    }

    /**
     * Attempts to generate a new personal access token (credentials) via use of a stored
     * Azure refresh token, identified by the target resource.
     *
     * @param targetUri           The 'key' by which to identify the refresh token.
     * @param requireCompactToken Generates a compact token if true; generates a self
     *                            describing token if false.
     * @return True if successful; false otherwise.
     */
    public boolean refreshCredentials(final URI targetUri, final boolean requireCompactToken)
    {
        BaseSecureStore.validateTargetUri(targetUri);

        Trace.writeLine("BaseVsoAuthentication::refreshCredentials");

        try
        {
            TokenPair tokens = null;

            AtomicReference<Token> refreshToken = new AtomicReference<Token>();
            // attempt to read from the local store
            if (this.AdaRefreshTokenStore.readToken(targetUri, refreshToken))
            {
                if ((tokens = this.VsoAuthority.acquireTokenByRefreshToken(targetUri, this.ClientId, this.Resource, refreshToken.get())) !=
                        null)
                {
                    Trace.writeLine("   Azure token found in primary cache.");

                    this.TenantId = tokens.AccessToken.getTargetIdentity();

                    return this.generatePersonalAccessToken(targetUri, tokens.AccessToken, requireCompactToken);
                }
            }

            AtomicReference<Token> federatedAuthToken = new AtomicReference<Token>();
            // attempt to utilize any fedauth tokens captured by the IDE
            if (this.VsoIdeTokenCache.readToken(targetUri, federatedAuthToken))
            {
                Trace.writeLine("   federated auth token found in IDE cache.");

                return this.generatePersonalAccessToken(targetUri, federatedAuthToken.get(), requireCompactToken);
            }
        }
        catch (final Exception exception)
        {
            Debug.Assert(false, exception.getMessage());
        }

        Trace.writeLine("   failed to refresh credentials.");
        return false;
    }

    /**
     * Validates that a set of credentials grants access to the target resource.
     *
     * @param targetUri   The target resource to validate against.
     * @param credentials The credentials to validate.
     * @return True if successful; false otherwise.
     */
    public boolean validateCredentials(final URI targetUri, final Credential credentials)
    {
        Trace.writeLine("BaseVsoAuthentication::validateCredentials");

        return this.VsoAuthority.validateCredentials(targetUri, credentials);
    }

    /**
     *
     *
     * @param targetUri           The target resource for which to acquire the personal access
     *                            token for.
     * @param accessToken         Azure Directory access token with privileges to grant access
     *                            to the target resource.
     * @param requestCompactToken Generates a compact token if true;
     *                            generates a self describing token if false.
     * @return True if successful; false otherwise.
     */
    protected boolean generatePersonalAccessToken(final URI targetUri, final Token accessToken, final boolean requestCompactToken)
    {
        Debug.Assert(targetUri != null, "The targetUri parameter is null");
        Debug.Assert(accessToken != null, "The accessToken parameter is null");

        Trace.writeLine("BaseVsoAuthentication::generatePersonalAccessToken");

        Token personalAccessToken;
        if ((personalAccessToken = this.VsoAuthority.generatePersonalAccessToken(targetUri, accessToken, TokenScope, requestCompactToken)) != null)
        {
            this.PersonalAccessTokenStore.writeCredentials(targetUri, Token.toCredential(personalAccessToken));
        }

        return personalAccessToken != null;

    }

    /**
     * Stores an Azure Directory refresh token.
     *
     * @param targetUri    The 'key' by which to identify the token.
     * @param refreshToken The token to be stored.
     */
    protected void storeRefreshToken(final URI targetUri, final Token refreshToken)
    {
        Debug.Assert(targetUri != null, "The targetUri parameter is null");
        Debug.Assert(refreshToken != null, "The refreshToken parameter is null");

        Trace.writeLine("BaseVsoAuthentication::storeRefreshToken");

        this.AdaRefreshTokenStore.writeToken(targetUri, refreshToken);
    }

    /**
     * Detects the backing authority of the end-point.
     *
     * @param targetUri The resource which the authority protects.
     * @param tenantId  The identity of the authority tenant; null otherwise.
     * @return True if the authority is Visual Studio Online; false otherwise.
     */
    public static boolean detectAuthority(final URI targetUri, final AtomicReference<UUID> tenantId)
    {
        final String VsoBaseUrlHost = "visualstudio.com";
        final String VsoResourceTenantHeader = "X-VSS-ResourceTenant";

        Trace.writeLine("BaseVsoAuthentication::detectAuthority");

        tenantId.set(Guid.Empty);

        if (StringHelper.endsWithIgnoreCase(targetUri.getHost(), VsoBaseUrlHost))
        {
            Trace.writeLine("   detected visualstudio.com, checking AAD vs MSA");

            String tenant = null;

            HttpURLConnection connection = null;
            final HttpClient client = new HttpClient(Global.getUserAgent());
            try
            {
                connection = client.head(targetUri, new Action<HttpURLConnection>()
                {
                    @Override public void call(final HttpURLConnection conn)
                    {
                        conn.setInstanceFollowRedirects(false);
                    }
                });

                tenant = connection.getHeaderField(VsoResourceTenantHeader);
                Trace.writeLine("   server has responded");

                return !StringHelper.isNullOrWhiteSpace(tenant)
                        && Guid.tryParse(tenant, tenantId);
            }
            catch (final IOException e)
            {
                throw new Error(e);
            }
        }

        Trace.writeLine("   failed detection");

        // if all else fails, fallback to basic authentication
        return false;
    }

    /**
     * Creates a new authentication broker based for the specified resource.
     *
     * @param targetUri                The resource for which authentication is being requested.
     * @param scope                    The scope of the access being requested.
     * @param personalAccessTokenStore Storage container for personal access token secrets.
     * @param adaRefreshTokenStore     Storage container for Azure access token secrets.
     * @param authentication           An implementation of {@link BaseAuthentication} if one was detected;
     *                                 null otherwise.
     * @return True if an authority could be determined; false otherwise.
     */
    public static boolean getAuthentication(
            final URI targetUri,
            final VsoTokenScope scope,
            final ICredentialStore personalAccessTokenStore,
            final ITokenStore adaRefreshTokenStore,
            final AtomicReference<IAuthentication> authentication)
    {
        Trace.writeLine("BaseVsoAuthentication::getAuthentication");

        final AtomicReference<UUID> tenantId = new AtomicReference<UUID>();
        if (detectAuthority(targetUri, tenantId))
        {
            // empty Guid is MSA, anything else is AAD
            if (Guid.Empty.equals(tenantId.get()))
            {
                Trace.writeLine("   MSA authority detected");
                authentication.set(new VsoMsaAuthentication(scope, personalAccessTokenStore, adaRefreshTokenStore));
            }
            else
            {
                Trace.writeLine("   AAD authority for tenant '" + tenantId + "' detected");
                authentication.set(new VsoAadAuthentication(tenantId.get(), scope, personalAccessTokenStore, adaRefreshTokenStore));
                ((BaseVsoAuthentication)authentication.get()).TenantId = tenantId.get();
            }
        }
        else
        {
            authentication.set(null);
        }

        return authentication.get() != null;
    }
}


<|start_filename|>src/main/java/com/microsoft/alm/authentication/VsoMsaAuthentication.java<|end_filename|>
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root.

package com.microsoft.alm.authentication;

import com.microsoft.alm.helpers.Action;
import com.microsoft.alm.helpers.Trace;
import com.microsoft.alm.secret.Credential;
import com.microsoft.alm.secret.TokenPair;
import com.microsoft.alm.secret.VsoTokenScope;

import java.net.URI;

public final class VsoMsaAuthentication extends BaseVsoAuthentication implements IVsoMsaAuthentication
{
    public final String DefaultAuthorityHost = AzureAuthority.AuthorityHostUrlBase + "/live.com";

    public VsoMsaAuthentication(
            VsoTokenScope tokenScope,
            ICredentialStore personalAccessTokenStore,
            ITokenStore adaRefreshTokenStore)
    {
        super(tokenScope,
               personalAccessTokenStore,
               adaRefreshTokenStore);
        this.VsoAuthority = new VsoAzureAuthority(DefaultAuthorityHost);
    }
    /**
     * Test constructor which allows for using fake credential stores
     */
    VsoMsaAuthentication(
            ICredentialStore personalAccessTokenStore,
            ITokenStore adaRefreshTokenStore,
            ITokenStore vsoIdeTokenCache,
            IVsoAuthority liveAuthority)
    {
        super(personalAccessTokenStore,
               adaRefreshTokenStore,
               vsoIdeTokenCache,
               liveAuthority);
    }

    /**
     * Opens an interactive logon prompt to acquire acquire an authentication token from the
     * Microsoft Live authentication and identity service.
     *
     * @param targetUri
     * The uniform resource indicator of the resource access tokens are being requested for.
     * 
     * @param requireCompactToken
     * True if a compact access token is required; false if a standard token is acceptable.
     * 
     * @return True if successful; otherwise false.
     */
    @Override public boolean interactiveLogon(URI targetUri, boolean requireCompactToken)
    {
        final String QueryParameters = "domain_hint=live.com&display=popup&site_id=501454&nux=1";

        BaseSecureStore.validateTargetUri(targetUri);

        Trace.writeLine("VsoMsaAuthentication::InteractiveLogon");

        TokenPair tokens;
        if ((tokens = this.VsoAuthority.acquireToken(targetUri, this.ClientId, this.Resource, RedirectUri, QueryParameters)) != null)
        {
            Trace.writeLine("   token successfully acquired.");

            this.storeRefreshToken(targetUri, tokens.RefreshToken);

            return this.generatePersonalAccessToken(targetUri, tokens.AccessToken, requireCompactToken);
        }

        Trace.writeLine("   failed to acquire token.");
        return false;
    }

    public boolean deviceLogon(final URI targetUri, final boolean requestCompactToken, final Action<DeviceFlowResponse> callback)
    {
        BaseSecureStore.validateTargetUri(targetUri);

        Trace.writeLine("VsoMsaAuthentication::deviceLogon");

        TokenPair tokens;
        if ((tokens = this.VsoAuthority.acquireToken(targetUri, this.ClientId, this.Resource, RedirectUri, callback)) != null)
        {
            Trace.writeLine("   token successfully acquired.");

            this.storeRefreshToken(targetUri, tokens.RefreshToken);

            return this.generatePersonalAccessToken(targetUri, tokens.AccessToken, requestCompactToken);
        }

        Trace.writeLine("   failed to acquire token.");
        return false;
    }

    /**
     * Sets credentials for future use with this authentication object.
     * Not supported.
     * @param targetUri The uniform resource indicator of the resource access tokens are being set for.
     * 
     * @param credentials The credentials being set.
     * @return True if successful; false otherwise.
     */
    @Override public boolean setCredentials(URI targetUri, Credential credentials)
    {
        BaseSecureStore.validateTargetUri(targetUri);
        Credential.validate(credentials);

        Trace.writeLine("VsoMsaAuthentication::SetCredentials");
        Trace.writeLine("   setting MSA credentials is not supported");

        // does nothing with VSO MSA backed accounts
        return false;
    }

}


<|start_filename|>src/main/java/com/microsoft/alm/authentication/GithubAuthenticationResultType.java<|end_filename|>
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root.

package com.microsoft.alm.authentication;

public enum GithubAuthenticationResultType
{
    Success,
    Failure,
    TwoFactorApp,
    TwoFactorSms,
}


<|start_filename|>src/main/java/com/microsoft/alm/authentication/Global.java<|end_filename|>
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root.

package com.microsoft.alm.authentication;

final class Global
{
    public static final int PasswordMaxLength = 2047;
    public static final int UsernameMaxLength = 511;

    private static String userAgent = null;

    /**
     * Creates the correct user-agent string for HTTP calls.
     *
     * @return The `user-agent` string for "git-tools".
     * Example from Windows version:
     * git-credential-manager (Microsoft Windows NT 6.2.9200.0; Win32NT; x64) CLR/4.0.30319 git-tools/1.0.0
     * Example from Java version:
     * git-credential-manager (Windows 8.1; 6.3; amd64) Java HotSpot(TM) 64-Bit Server VM/1.8.0_51-b16 git-tools/1.0.0-SNAPSHOT
     */
    public static String getUserAgent()
    {
        if (userAgent == null)
        {
            // https://stackoverflow.com/a/6773868/
            final String version = Global.class.getPackage().getImplementationVersion();
            userAgent = String.format("git-credential-manager (%1$s; %2$s; %3$s) %4$s/%5$s git-tools/%6$s",
                    System.getProperty("os.name"), // "Windows Server 2012 R2", "Mac OS X", "Linux"
                    System.getProperty("os.version"), // "6.3", "10.9.5", "3.19.0-28-generic"
                    System.getProperty("os.arch"), // "amd64", "x86_64", "amd64"
                    System.getProperty("java.vm.name"), // "Java HotSpot(TM) 64-Bit Server VM", "OpenJDK 64-Bit Server VM"
                    System.getProperty("java.runtime.version"), // "1.8.0_60-b27", "1.7.0_71-b14", "1.7.0_79-b14"
                    version);
        }
        return userAgent;
    }
}


<|start_filename|>src/test/java/com/microsoft/alm/gitcredentialmanager/ProgramTest.java<|end_filename|>
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root.

package com.microsoft.alm.gitcredentialmanager;

import com.microsoft.alm.helpers.Func;
import com.microsoft.alm.helpers.Trace;
import com.microsoft.alm.oauth2.useragent.Provider;
import com.microsoft.alm.oauth2.useragent.subprocess.TestableProcess;
import com.microsoft.alm.oauth2.useragent.subprocess.TestableProcessFactory;
import org.junit.Assert;
import org.junit.Ignore;
import org.junit.Test;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URL;
import java.util.Arrays;
import java.util.List;

public class ProgramTest
{
    @Ignore("This test requires user intervention and must be run manually.")
    @Test public void get() throws Exception
    {
        Trace.getListeners().add(System.err);
        final String hostAccount = System.getProperty("hostAccount");
        final String input = "protocol=https\n" +
            "host=" + hostAccount + ".visualstudio.com\n" +
            "path=\n" +
            "";
        final InputStream inputStream = new ByteArrayInputStream(input.getBytes("UTF-8"));
        final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        final Program program = new Program(inputStream, new PrintStream(outputStream), new Program.ComponentFactory());

        program.innerMain(new String[]{"get"});

        final String output = outputStream.toString("UTF-8");
        final String expected = "protocol=https\n" +
            "host=" + hostAccount + ".visualstudio.com\n" +
            "path=\n" +
            "username=Personal Access Token\n";
        if (!output.startsWith(expected))
        {
            Assert.fail("'" + output + "' did not start with '" + expected + "'.");
        }
        if (!output.contains("password="))
        {
            Assert.fail("'" + output + "' did not contain 'password='.");
        }
    }

    @Test public void isValidGitVersion_happy()
    {
        // greater version
        Assert.assertEquals(0, Program.isValidGitVersion("git version 2.4.9").size());

        // min version
        Assert.assertEquals(0, Program.isValidGitVersion("git version 1.9.0").size());
    }

    @Test public void checkGitRequirements() throws UnsupportedEncodingException
    {
        final TestableProcess process = new TestProcess("git version 2.6.2\n");
        final TestableProcessFactory processFactory = new TestableProcessFactory()
        {
            @Override public TestableProcess create(final String... strings) throws IOException
            {
                return process;
            }
        };

        final List<String> actual = Program.checkGitRequirements(processFactory);

        Assert.assertEquals(0, actual.size());
    }

    @Test public void isValidGitVersion_badVersion()
    {
        Assert.assertEquals(1, Program.isValidGitVersion("git version 1.8.3").size());
    }

    @Test public void isValidGitVersion_noResponse()
    {
        Assert.assertEquals(1, Program.isValidGitVersion(null).size());
    }

    @Test public void isValidGitVersion_noGitFound()
    {
        Assert.assertEquals(1, Program.isValidGitVersion("-bash: git: command not found").size());
    }

    @Test public void checkOsRequirements_macOsHappy()
    {
        Assert.assertEquals(0, Program.checkOsRequirements("Mac OS X", "10.9.5").size());

        Assert.assertEquals(0, Program.checkOsRequirements("Mac OS X", "10.9.6").size());

        Assert.assertEquals(0, Program.checkOsRequirements("Mac OS X", "10.10.1").size());

        Assert.assertEquals(0, Program.checkOsRequirements("Mac OS X", "10.10.4").size());

        Assert.assertEquals(0, Program.checkOsRequirements("Mac OS X", "10.10.5").size());

        Assert.assertEquals(0, Program.checkOsRequirements("Mac OS X", "10.10.6").size());

        Assert.assertEquals(0, Program.checkOsRequirements("Mac OS X", "10.11").size());

        Assert.assertEquals(0, Program.checkOsRequirements("Mac OS X", "10.11.0").size());

        Assert.assertEquals(0, Program.checkOsRequirements("Mac OS X", "10.12").size());

        Assert.assertEquals(0, Program.checkOsRequirements("Mac OS X", "11.1.1").size());
    }

    @Test public void checkOsRequirements_macOsBadVersion()
    {
        Assert.assertEquals(1, Program.checkOsRequirements("Mac OS X", "10.9.1").size());

        Assert.assertEquals(1, Program.checkOsRequirements("Mac OS X", "10.9.4").size());

        Assert.assertEquals(1, Program.checkOsRequirements("Mac OS X", "10.8.10").size());

        Assert.assertEquals(1, Program.checkOsRequirements("Mac OS X", "10.8.5").size());

        Assert.assertEquals(1, Program.checkOsRequirements("Mac OS X", "9.10.5").size());

        Assert.assertEquals(1, Program.checkOsRequirements("Mac OS X", "9.11.5").size());
    }

    @Test public void checkOsRequirements_linuxOsHappy()
    {
        Assert.assertEquals(0, Program.checkOsRequirements("Linux", "1.1.1").size());
    }

    @Test public void checkOsRequirements_unsupportedOs()
    {
        Assert.assertEquals(1, Program.checkOsRequirements("Windows", "1.1.1").size());

        Assert.assertEquals(1, Program.checkOsRequirements("Unknown", "1.1.1").size());
    }

    private static final Provider TestProviderFoo = new Provider("foo")
    {
        @Override public List<String> checkRequirements()
        {
            return null;
        }

        @Override public void augmentProcessParameters(List<String> list, List<String> list1)
        {

        }
    };
    private static final Provider TestProviderBar = new Provider("bar")
    {
        @Override public List<String> checkRequirements()
        {
            return null;
        }

        @Override public void augmentProcessParameters(List<String> list, List<String> list1)
        {

        }
    };
    private static final Provider TestProviderPateChinois = new Provider("Pâté Chinois")
    {
        @Override public List<String> checkRequirements()
        {
            return Arrays.asList("steak", "blé d'inde", "patates");
        }

        @Override public void augmentProcessParameters(List<String> list, List<String> list1)
        {

        }
    };
    private static final Provider TestProviderBananaNutChocolateCake = new Provider("Banana Nut Chocolate Cake")
    {
        @Override public List<String> checkRequirements()
        {
            return Arrays.asList("bananas", "nuts", "chocolate");
        }

        @Override public void augmentProcessParameters(List<String> list, List<String> list1)
        {

        }
    };

    @Test public void configureGit_perUserWithOpenJdkOnFedoraLinux() throws Exception
    {
        final TestableProcess process = new TestProcess("");
        final TestableProcessFactory processFactory = new TestableProcessFactory()
        {
            @Override
            public TestableProcess create(final String... strings) throws IOException
            {
                Assert.assertEquals("git", strings[0]);
                Assert.assertEquals("config", strings[1]);
                Assert.assertEquals("--global", strings[2]);
                Assert.assertEquals("--add", strings[3]);
                Assert.assertEquals("credential.helper", strings[4]);
                Assert.assertEquals("!/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.65-3.b17.fc22.x86_64/bin/java -Ddebug=false -Djava.net.useSystemProxies=true -jar /usr/bin/git-credential-manager-1.1.0.jar", strings[5]);
                return process;
            }
        };
        Program.configureGit(processFactory, "global", "/usr/lib/jvm/java-1.8.0-openjdk-1.8.0.65-3.b17.fc22.x86_64/bin/java", "/usr/bin/git-credential-manager-1.1.0.jar", false);
    }

    @Test public void configureGit_allUsersDebugWithOracleJdkOnMac() throws Exception
    {
        final TestableProcess process = new TestProcess("");
        final TestableProcessFactory processFactory = new TestableProcessFactory()
        {
            @Override
            public TestableProcess create(final String... strings) throws IOException
            {
                Assert.assertEquals("git", strings[0]);
                Assert.assertEquals("config", strings[1]);
                Assert.assertEquals("--system", strings[2]);
                Assert.assertEquals("--add", strings[3]);
                Assert.assertEquals("credential.helper", strings[4]);
                Assert.assertEquals("!/System/Library/Frameworks/JavaVM.framework/Versions/Current/Commands/java -Ddebug=true -Djava.net.useSystemProxies=true -jar /usr/local/bin/git-credential-manager-1.1.0.jar", strings[5]);
                return process;
            }
        };
        Program.configureGit(processFactory, "system", "/System/Library/Frameworks/JavaVM.framework/Versions/Current/Commands/java", "/usr/local/bin/git-credential-manager-1.1.0.jar", true);
    }

    @Test public void determinePathToJar_typical() throws Exception
    {
        final URL jarUrl = URI.create("file:/home/example/git-credential-manager/git-credential-manager-1.0.0.jar!/com/microsoft/alm/gitcredentialmanager/").toURL();

        final String actual = Program.determinePathToJar(jarUrl);

        final String expected = "/home/example/git-credential-manager/git-credential-manager-1.0.0.jar";
        Assert.assertEquals(expected, actual);
    }

    @Test public void determinePathToJar_withSpaces() throws Exception
    {
        final URL jarUrl = URI.create("file:/home/example/with%20spaces/git-credential-manager-1.0.0.jar!/com/microsoft/alm/gitcredentialmanager/").toURL();

        final String actual = Program.determinePathToJar(jarUrl);

        final String expected = "/home/example/with spaces/git-credential-manager-1.0.0.jar";
        Assert.assertEquals(expected, actual);
    }

    private static class FakeFileChecker implements Func<File, Boolean>
    {
        private final String expectedPath;
        public FakeFileChecker(final String expectedPath)
        {
            this.expectedPath = expectedPath;
        }
        @Override public Boolean call(File file)
        {
            final String absolutePath = file.getAbsolutePath();
            final boolean result = absolutePath.equals(expectedPath);
            return result;
        }
    }

    @Test public void findProgram_notFound() throws Exception
    {
        final List<String> directories = Arrays.asList("/usr/bin", "/usr/local/bin", "/bin");
        final File expectedFile = new File("/usr/sbin/git-credential-osxkeychain");
        final String expectedPath = expectedFile.getAbsolutePath();
        final String executableName = "git-credential-osxkeychain";
        final Func<File, Boolean> isFile = new FakeFileChecker(expectedPath);

        final File actual = Program.findProgram(directories, executableName, isFile);

        Assert.assertEquals(null, actual);
    }

    @Test public void findProgram_found() throws Exception
    {
        final List<String> directories = Arrays.asList("/usr/bin", "/usr/local/bin", "/bin");
        final File expectedFile = new File("/usr/local/bin/git-credential-osxkeychain");
        final String expectedPath = expectedFile.getAbsolutePath();
        final String executableName = "git-credential-osxkeychain";
        final Func<File, Boolean> isFile = new FakeFileChecker(expectedPath);

        final File actual = Program.findProgram(directories, executableName, isFile);

        Assert.assertNotNull(actual);
        Assert.assertEquals(expectedPath, actual.getAbsolutePath());
    }

    @Test public void findProgram_fromPathString_notFound() throws Exception
    {
        final String pathString = "/usr/bin:/usr/local/bin:/bin";
        final File expectedFile = new File("/usr/sbin/git-credential-osxkeychain");
        final String expectedPath = expectedFile.getAbsolutePath();
        final String executableName = "git-credential-osxkeychain";
        final Func<File, Boolean> isFile = new FakeFileChecker(expectedPath);

        final File actual = Program.findProgram(pathString, ":", executableName, isFile);

        Assert.assertEquals(null, actual);
    }

    @Test public void findProgram_fromPathString_found() throws Exception
    {
        final String pathString = "/usr/bin:/usr/local/bin:/bin";
        final File expectedFile = new File("/usr/local/bin/git-credential-osxkeychain");
        final String expectedPath = expectedFile.getAbsolutePath();
        final String executableName = "git-credential-osxkeychain";
        final Func<File, Boolean> isFile = new FakeFileChecker(expectedPath);

        final File actual = Program.findProgram(pathString, ":", executableName, isFile);

        Assert.assertNotNull(actual);
        Assert.assertEquals(expectedPath, actual.getAbsolutePath());
    }
}


<|start_filename|>src/main/java/com/microsoft/alm/authentication/Configuration.java<|end_filename|>
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root.

package com.microsoft.alm.authentication;

import com.microsoft.alm.helpers.Debug;
import com.microsoft.alm.helpers.Environment;
import com.microsoft.alm.helpers.Path;
import com.microsoft.alm.helpers.StringHelper;
import com.microsoft.alm.helpers.Trace;

import java.io.BufferedReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URI;
import java.util.Map;
import java.util.TreeMap;
import java.util.concurrent.atomic.AtomicReference;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public final class Configuration
{
    private static final Pattern CommentLinePattern = Pattern.compile
    (
    //   ^\s*[#;]
        "^\\s*[#;]"
    );
    private static final Pattern SectionNamePattern = Pattern.compile
    (
    //   ^\s*\[\s*(\w+)\s*(\"[^\"]+\"){0,1}\]
        "^\\s*\\[\\s*(\\w+)\\s*(\\\"[^\\]]+){0,1}\\]"
    );
    private static final Pattern NameValuePattern = Pattern.compile
    (
    //   ^\s*(\w+)\s*=\s*(.+)
        "^\\s*(\\w+)\\s*=\\s*(.+)"
    );

    private static final char HostSplitCharacter = '.';

    public Configuration(final String directory) throws IOException
    {
        if (StringHelper.isNullOrWhiteSpace(directory))
            throw new IllegalArgumentException("directory is null or empty");
        if (!Path.directoryExists(directory))
            throw new IllegalArgumentException("directory does not exist");

        loadGitConfiguration(directory);
    }

    public Configuration() throws IOException
    {
        this(Environment.getCurrentDirectory());
    }

    Configuration(final BufferedReader configReader) throws IOException
    {
        parseGitConfig(configReader, _values);
    }

    private final Map<String, String> _values = new TreeMap<String, String>(String.CASE_INSENSITIVE_ORDER);

    public String get(final String key)
    {
        return _values.get(key);
    }

    public boolean containsKey(final String key)
    {
        return _values.containsKey(key);
    }

    public boolean tryGetEntry(final String prefix, final String key, final String suffix, final AtomicReference<Entry> entry)
    {
        Debug.Assert(prefix != null, "The prefix parameter is null");
        Debug.Assert(suffix != null, "The suffix parameter is null");

        String match = StringHelper.isNullOrEmpty(key)
                ? String.format("%1$s.%2$s", prefix, suffix)
                : String.format("%1$s.%2$s.%3$s", prefix, key, suffix);

        // if there's a match, return it
        if (_values.containsKey(match))
        {
            entry.set(new Entry(match, _values.get(match)));
            return true;
        }

        // nothing found
        entry.set(null);
        return false;
    }

    public boolean tryGetEntry(final String prefix, final URI targetUri, final String key, final AtomicReference<Entry> entry)
    {
        Debug.Assert(key != null, "The key parameter is null");

        Trace.writeLine("Configuration::tryGetEntry");

        if (targetUri != null)
        {
            // return match seeking from most specific (<prefix>.<scheme>://<host>.<key>) to least specific (credential.<key>)
            if (tryGetEntry(prefix, String.format("%1$s://%2$s", targetUri.getScheme(), targetUri.getHost()), key, entry)
                    || tryGetEntry(prefix, targetUri.getHost(), key, entry))
                return true;

            if (!StringHelper.isNullOrWhiteSpace(targetUri.getHost()))
            {
                final String[] fragments = targetUri.getHost().split("\\" + HostSplitCharacter);
                String host = null;

                // look for host matches stripping a single sub-domain at a time off
                // don't match against a top-level domain (aka ".com")
                for (int i = 1; i < fragments.length - 1; i++)
                {
                    host = StringHelper.join(".", fragments, i, fragments.length - i);
                    if (tryGetEntry(prefix, host, key, entry))
                        return true;
                }
            }
        }

        // try to find an unadorned match as a complete fallback
        if (tryGetEntry(prefix, StringHelper.Empty, key, entry))
            return true;

        // nothing found
        entry.set(null);
        return false;
    }

    public void loadGitConfiguration(final String directory) throws IOException
    {
        final AtomicReference<String> systemConfig = new AtomicReference<String>();
        final AtomicReference<String> globalConfig = new AtomicReference<String>();
        final AtomicReference<String> localConfig = new AtomicReference<String>();

        Trace.writeLine("Configuration::loadGitConfiguration");

        // read Git's three configs from lowest priority to highest, overwriting values as
        // higher priority configurations are parsed, storing them in a handy lookup table

        // find and parse Git's system config
        if (Where.gitSystemConfig(systemConfig))
        {
            parseGitConfig(systemConfig.get());
        }

        // find and parse Git's global config
        if (Where.gitGlobalConfig(globalConfig))
        {
            parseGitConfig(globalConfig.get());
        }

        // find and parse Git's local config
        if (Where.gitLocalConfig(directory, localConfig))
        {
            parseGitConfig(localConfig.get());
        }

        for (final Map.Entry pair : _values.entrySet())
        {
            Trace.writeLine(String.format("   %1$s = %2$s", pair.getKey(), pair.getValue()));
        }
    }

    private void parseGitConfig(final String configPath) throws IOException
    {
        Debug.Assert(!StringHelper.isNullOrWhiteSpace(configPath), "The configPath parameter is null or invalid.");
        Debug.Assert(Path.fileExists(configPath), "The configPath parameter references a non-existent file.");
        Debug.Assert(_values != null, "The configPath parameter is null or invalid.");

        Trace.writeLine("Configuration::ParseGitConfig");

        if (!Path.fileExists(configPath))
            return;

        final BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(configPath)));
        try
        {
            parseGitConfig(br, _values);
        }
        finally
        {
            br.close();
        }
    }

    static void parseGitConfig(final BufferedReader configReader, final Map<String, String> destination) throws IOException
    {
        Matcher match = null;
        String section = null;

        // parse each line in the config independently - Git's configs do not accept multi-line values
        String line;
        while ((line = configReader.readLine()) != null)
        {
            // skip empty and commented lines
            if (StringHelper.isNullOrWhiteSpace(line))
                continue;
            if (CommentLinePattern.matcher(line).matches())
                continue;

            // sections begin with values like [section] or [section "section name"]. All subsequent lines,
            // until a new section is encountered, are children of the section
            if ((match = SectionNamePattern.matcher(line)).matches())
            {
                // NOTE: in Java, match.groupCount() is one less than .NET's match.Groups.Count
                if (match.groupCount() >= 1 && !StringHelper.isNullOrWhiteSpace(match.group(1)))
                {
                    section = match.group(1).trim();

                    // check if the section is named, if so: process the name
                    if (match.groupCount() >= 2 && !StringHelper.isNullOrWhiteSpace(match.group(2)))
                    {
                        String val = match.group(2).trim();

                        // triming off enclosing quotes makes usage easier, only trim in pairs
                        if (val.charAt(0) == '"')
                        {
                            // NOTE: Java: substring(beginIndex, endIndex), .NET: Substring(startIndex, length)
                            if (val.charAt(val.length() - 1) == '"')
                            {
                                val = val.substring(1, val.length() - 1);
                            }
                            else
                            {
                                val = val.substring(1, val.length() - 0);
                            }
                        }

                        section += HostSplitCharacter + val;
                    }
                }
            }
            // section children should be in the format of name = value pairs
            else if ((match = NameValuePattern.matcher(line)).matches())
            {
                if (match.groupCount() >= 2
                    && !StringHelper.isNullOrWhiteSpace(match.group(1))
                    && !StringHelper.isNullOrWhiteSpace(match.group(2)))
                {
                    final String key = section + HostSplitCharacter + match.group(1).trim();
                    String val = match.group(2).trim();

                    // triming off enclosing quotes makes usage easier, only trim in pairs
                    if (val.charAt(0) == '"')
                    {
                        // NOTE: Java: substring(beginIndex, endIndex), .NET: Substring(startIndex, length)
                        if (val.charAt(val.length() - 1) == '"')
                        {
                            val = val.substring(1, val.length() - 1);
                        }
                        else
                        {
                            val = val.substring(1, val.length() - 0);
                        }
                    }

                    // add or update the (key, value)
                    destination.put(key, val);
                }
            }
        }
    }

    public class Entry
    {
        public Entry(final String key, final String value)
        {
            Key = key;
            Value = value ;
        }

        public final String Key;
        public final String Value;
    }
}



<|start_filename|>src/test/groovy/org/slf4j/impl/TraceLoggerTest.groovy<|end_filename|>
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root.

package org.slf4j.impl

import com.microsoft.alm.helpers.Trace
import groovy.transform.CompileStatic
import org.junit.After
import org.junit.Before
import org.junit.Test
import org.slf4j.LoggerFactory

/**
 * A class to test {@link TraceLogger}.
 */
@CompileStatic
public class TraceLoggerTest {

    @Before @After public void clearTraceListeners() {
        Trace.listeners.clear();
    }

    @Test public void debugLogging() {
        final baos = new ByteArrayOutputStream();
        final destination = new PrintStream(baos);
        Trace.listeners.add(destination);
        final logger = LoggerFactory.getLogger(TraceLoggerTest.class);

        logger.debug("The {} {} fox jumps over the {} dog's back.", "quick", "brown", "lazy");

        final actual = baos.toString().trim();
        assert "The quick brown fox jumps over the lazy dog's back." == actual;
    }
}


<|start_filename|>src/test/java/com/microsoft/alm/authentication/VsoAadAuthenticationTest.java<|end_filename|>
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root.

package com.microsoft.alm.authentication;

import com.microsoft.alm.helpers.Guid;
import com.microsoft.alm.secret.VsoTokenScope;
import org.junit.Assert;
import org.junit.Test;

import java.net.URI;
import java.net.URISyntaxException;

public class VsoAadAuthenticationTest
{
    @Test public void ctor_DefaultAuthorityHost() throws URISyntaxException
    {
        final SecretCache secretCache = new SecretCache("test");

        final VsoAadAuthentication vaa = new VsoAadAuthentication(Guid.Empty, VsoTokenScope.CodeWrite, secretCache, secretCache);

        final AzureAuthority azureAuthority = (AzureAuthority) vaa.VsoAuthority;
        final URI uri = new URI(azureAuthority.authorityHostUrl);
        Assert.assertEquals(true, uri.isAbsolute());
    }
}


<|start_filename|>src/main/java/com/microsoft/alm/authentication/PromptBehavior.java<|end_filename|>
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root.

package com.microsoft.alm.authentication;

/**
 * Indicates whether acquireToken should automatically prompt only if necessary or whether
 * it should prompt regardless of whether there is a cached token.
 */
public enum PromptBehavior {
    /**
     * Acquire token will prompt the user for credentials only when necessary.  If a token
     * that meets the requirements is already cached then the user will not be prompted.
     */
    AUTO,

    /**
     * The user will be prompted for credentials even if there is a token that meets the requirements
     * already in the cache.
     */
    ALWAYS,

    /**
     * The user will not be prompted for credentials.  If prompting is necessary then the acquireToken request
     * will fail.
     */
    NEVER,

    /**
     * Not yet implemented. Reserved for future use.
     */
    /*
     * Re-authorizes (through displaying browser) the resource usage, making sure that the resulting access
     * token contains updated claims. If user logon cookies are available, the user will not be asked for
     * credentials again and the logon dialog will dismiss automatically.
     */
    REFRESH_SESSION,
    ;
}


<|start_filename|>src/main/java/com/microsoft/alm/authentication/IVsoMsaAuthentication.java<|end_filename|>
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root.

package com.microsoft.alm.authentication;

import com.microsoft.alm.helpers.Action;
import com.microsoft.alm.secret.Credential;

import java.net.URI;

public interface IVsoMsaAuthentication extends IAuthentication
{
    boolean interactiveLogon(final URI targetUri, boolean requestCompactToken);
    boolean deviceLogon(final URI targetUri, final boolean requestCompactToken, final Action<DeviceFlowResponse> callback);
    boolean refreshCredentials(final URI targetUri, final boolean requireCompactToken);
    boolean validateCredentials(final URI targetUri, final Credential credentials);
}


<|start_filename|>src/main/java/com/microsoft/alm/gitcredentialmanager/AuthorityType.java<|end_filename|>
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root.

package com.microsoft.alm.gitcredentialmanager;

/**
 * Type of authentication and identity authority expected.
 */
enum AuthorityType
{
    /**
     * Attempt to detect the authority automatically, fallback to {@link #Basic} if
     * unable to detect an authority.
     */
    Auto,
    /**
     * Basic username and password scheme
     */
    Basic,
    /**
     * Username and password scheme using Microsoft's Live system
     */
    MicrosoftAccount,
    /**
     * Azure Directory Authentication based, including support for ADFS
     */
    AzureDirectory,
    /**
     * GitHub authentication
     */
    GitHub,
    /**
     * Operating system / network integrated authentication layer.
     */
    Integrated,
}


<|start_filename|>src/main/java/com/microsoft/alm/authentication/ISecureStore.java<|end_filename|>
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root.

package com.microsoft.alm.authentication;

import com.microsoft.alm.secret.Credential;
import com.microsoft.alm.secret.Token;

public interface ISecureStore
{
    void delete(final String targetName);

    Credential readCredentials(final String targetName);

    Token readToken(String targetName);

    void writeCredential(String targetName, Credential credentials);

    void writeToken(String targetName, Token token);
}


<|start_filename|>src/main/java/org/slf4j/impl/StaticLoggerBinder.java<|end_filename|>
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root.

package org.slf4j.impl;

import org.slf4j.ILoggerFactory;
import org.slf4j.spi.LoggerFactoryBinder;

/**
 * The binding of {@link org.slf4j.LoggerFactory} class with an actual instance of
 * {@link ILoggerFactory} is performed using information returned by this class.
 *
 * This class wires up slf4j to GCM4ML's Trace.
 */
public class StaticLoggerBinder implements LoggerFactoryBinder
{
    /**
     * The unique instance of this class.
     *
     */
    private static final StaticLoggerBinder SINGLETON = new StaticLoggerBinder();

    /**
     * Return the singleton of this class.
     *
     * @return the StaticLoggerBinder singleton
     */
    public static StaticLoggerBinder getSingleton() {
        return SINGLETON;
    }

    /**
     * Declare the version of the SLF4J API this implementation is compiled against.
     * The value of this field is modified with each major release.
     */
    // to avoid constant folding by the compiler, this field must *not* be final
    public static String REQUESTED_API_VERSION = "1.6.99"; // !final

    private static final String loggerFactoryClassStr = TraceLoggerFactory.class.getName();

    /** The ILoggerFactory instance returned by the {@link #getLoggerFactory} method
     * should always be the same object
     */
    private final ILoggerFactory loggerFactory;

    private StaticLoggerBinder() {
        loggerFactory = new TraceLoggerFactory();
    }

    public ILoggerFactory getLoggerFactory() {
        return loggerFactory;
    }

    public String getLoggerFactoryClassStr() {
        return loggerFactoryClassStr;
    }

}


<|start_filename|>src/main/java/com/microsoft/alm/authentication/GithubAuthenticationResult.java<|end_filename|>
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See License.txt in the project root.

package com.microsoft.alm.authentication;

import com.microsoft.alm.secret.Token;

public class GithubAuthenticationResult
{
    public GithubAuthenticationResult(final GithubAuthenticationResultType type)
    {
        this.Type = type;
        this.token = null;
    }

    public GithubAuthenticationResult(final GithubAuthenticationResultType type, final Token token)
    {
        this.Type = type;
        this.token = token;
    }

    public final GithubAuthenticationResultType Type;
    Token token;
    public Token getToken()
    {
        return token;
    }

    public static boolean operatorBoolean(final GithubAuthenticationResult result)
    {
        return result.Type == GithubAuthenticationResultType.Success;
    }

    public static GithubAuthenticationResultType operatorGithubAuthenticationResultType(final GithubAuthenticationResult result)
    {
        return result.Type;
    }

    public static GithubAuthenticationResult operatorGithubAuthenticationResult(final GithubAuthenticationResultType type)
    {
        return new GithubAuthenticationResult(type);
    }

}
