<commit_before>﻿// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Security.Cryptography.X509Certificates;
using Android.Accounts;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Content.PM;
using Android.Util;
using Java.Security;
using Java.Util.Concurrent;
using OperationCanceledException = Android.Accounts.OperationCanceledException;
using Permission = Android.Content.PM.Permission;
using Signature = Android.Content.PM.Signature;
using Microsoft.Identity.Client.Core;
using Microsoft.Identity.Client.OAuth2;
using Microsoft.Identity.Client.Internal.Broker;
using Microsoft.Identity.Client.Utils;
using Microsoft.Identity.Json.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Microsoft.Identity.Client.Platforms.Android
{
    [global::Android.Runtime.Preserve(AllMembers = true)]
    internal class AndroidBrokerHelper
    {
        private const string RedirectUriScheme = "msauth";
        private const string BrokerTag = BrokerConstants.Signature;
        public const string WorkAccount = "com.microsoft.workaccount.user.info";

        private readonly Context _androidContext;

        // Important: this object MUST be accessed on a background thread. Android will check this and throw otherwise.
        private readonly AccountManager _androidAccountManager;
        private readonly ICoreLogger _logger;

        private const long AccountManagerTimeoutSeconds = 5 * 60;

        public AndroidBrokerHelper(Context androidContext, ICoreLogger logger)
        {
            _androidContext = androidContext ?? throw new ArgumentNullException(nameof(androidContext));
            _logger = logger ?? throw new ArgumentNullException(nameof(logger));

            _logger.Verbose("Getting the Android context for broker request");
            _androidAccountManager = AccountManager.Get(_androidContext);
        }

        public bool CanSwitchToBroker()
        {
            string packageName = _androidContext.PackageName;

            // ADAL switches broker for following conditions:
            // 1- broker app is installed
            // 2- signature of the broker is valid
            // 3- account exists

            //Force this to return true for broker test app

            return VerifyAuthenticator()
                   && !packageName.Equals(BrokerConstants.PackageName, StringComparison.OrdinalIgnoreCase)
                   && !packageName
           .Equals(BrokerConstants.AzureAuthenticatorAppPackageName, StringComparison.OrdinalIgnoreCase);
        }

        public async Task<Intent> GetIntentForInteractiveBrokerRequestAsync(IDictionary<string, string> brokerPayload, Activity callerActivity)
        {
            Intent intent = null;

            try
            {
                IAccountManagerFuture result = null;
                // Callback is not passed since it is making a blocking call to get
                // intent. Activity needs to be launched from calling app
                // to get the calling app's metadata if needed at BrokerActivity.

                Bundle addAccountOptions = new Bundle();
                addAccountOptions.PutString(BrokerConstants.BrokerAccountManagerOperationKey, BrokerConstants.GetIntentForInteractiveRequest);

                result = _androidAccountManager.AddAccount(BrokerConstants.BrokerAccountType,
                    BrokerConstants.AuthtokenType,
                    null,
                    addAccountOptions,
                    null,
                    null,
                    GetPreferredLooper(callerActivity));

                if (result == null)
                {
                    _logger.Info("Android account manager didn't return any results for interactive broker request.");
                }

                Bundle bundleResult = (Bundle)(await result.GetResultAsync(
                     AccountManagerTimeoutSeconds,
                     TimeUnit.Seconds)
                     .ConfigureAwait(false));

                intent = (Intent)bundleResult?.GetParcelable(AccountManager.KeyIntent);

                //Validate that the intent was created successfully.
                if (intent != null)
                {
                    _logger.Info("Intent created from BundleResult is not null. Starting interactive broker request");
                    // Need caller info UID for broker communication
                    intent.PutExtra(BrokerConstants.CallerInfoUID, Binder.CallingUid);
                }
                else
                {
                    _logger.Info("Intent created from BundleResult is null. ");
                    throw new MsalException(MsalError.NullIntentReturnedFromAndroidBroker, MsalErrorMessage.NullIntentReturnedFromBroker);
                }

                intent = GetInteractiveBrokerIntent(brokerPayload, intent);
            }
            catch
            {
                _logger.Error("Error when trying to acquire intent for broker authentication.");
                throw;
            }

            return intent;
        }

        public async Task<string> GetBrokerAuthTokenSilentlyAsync(IDictionary<string, string> brokerPayload, Activity callerActivity)
        {
            CheckForBrokerAccountInfoInAccountManager(brokerPayload, callerActivity);
            Bundle silentOperationBundle = GetSilentBrokerBundle(brokerPayload);
            silentOperationBundle.PutString(BrokerConstants.BrokerAccountManagerOperationKey, BrokerConstants.AcquireTokenSilent);

            IAccountManagerFuture result = _androidAccountManager.AddAccount(BrokerConstants.BrokerAccountType,
                BrokerConstants.AuthtokenType,
                null,
                silentOperationBundle,
                null,
                null,
                GetPreferredLooper(callerActivity));

            if (result != null)
            {
                Bundle bundleResult = (Bundle)(await result.GetResultAsync(
                     AccountManagerTimeoutSeconds,
                     TimeUnit.Seconds)
                     .ConfigureAwait(false));

                if (bundleResult.GetBoolean(BrokerConstants.BrokerRequestV2Success))
                {
                    _logger.Info("Android Broker succsesfully refreshed the access token.");
                    return bundleResult.GetString(BrokerConstants.BrokerResultV2);
                }
            }

            _logger.Info("Android Broker didn't return any results.");
            return null;
        }

        /// <summary>
        /// This method is only used for Silent authnetication requests so that we can check to see if an account exists in the account manager before
        /// sending the silent request to the broker. 
        /// </summary>
        public void CheckForBrokerAccountInfoInAccountManager(IDictionary<string, string> brokerPayload, Activity callerActivity)
        {
            Bundle getAccountsBundle = GetBrokerAccountBundle(brokerPayload);
            getAccountsBundle.PutString(BrokerConstants.BrokerAccountManagerOperationKey, BrokerConstants.GetAccounts);

            var result = _androidAccountManager.AddAccount(BrokerConstants.BrokerAccountType,
                BrokerConstants.AuthtokenType,
                null,
                getAccountsBundle,
                null,
                null,
                GetPreferredLooper(callerActivity));

            if (result == null)
            {
                _logger.Info("Android account manager didn't return any accounts.");
                throw new MsalUiRequiredException(MsalError.NoAndroidBrokerAccountFound, MsalErrorMessage.NoAndroidBrokerAccountFound);
            }

            Bundle bundleResult = (Bundle)result?.Result;
            var accounts = bundleResult?.GetString(BrokerConstants.BrokerAccounts);

            string username = GetValueFromBrokerPayload(brokerPayload, BrokerParameter.Username);
            string homeAccountId = GetValueFromBrokerPayload(brokerPayload, BrokerParameter.HomeAccountId);
            string localAccountId = GetValueFromBrokerPayload(brokerPayload, BrokerParameter.LocalAccountId);

            if (!string.IsNullOrEmpty(accounts))
            {
                dynamic authResult = JArray.Parse(accounts);

                foreach (JObject account in authResult)
                {
                    var accountData = account[BrokerResponseConst.Account];

                    if (string.Equals((accountData[BrokerResponseConst.UserName]).ToString(), username))
                    {<commit_msg>Removing case sensitive check from MSAL broker (#1659)<commit_after>﻿// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Security.Cryptography.X509Certificates;
using Android.Accounts;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Content.PM;
using Android.Util;
using Java.Security;
using Java.Util.Concurrent;
using OperationCanceledException = Android.Accounts.OperationCanceledException;
using Permission = Android.Content.PM.Permission;
using Signature = Android.Content.PM.Signature;
using Microsoft.Identity.Client.Core;
using Microsoft.Identity.Client.OAuth2;
using Microsoft.Identity.Client.Internal.Broker;
using Microsoft.Identity.Client.Utils;
using Microsoft.Identity.Json.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Microsoft.Identity.Client.Platforms.Android
{
    [global::Android.Runtime.Preserve(AllMembers = true)]
    internal class AndroidBrokerHelper
    {
        private const string RedirectUriScheme = "msauth";
        private const string BrokerTag = BrokerConstants.Signature;
        public const string WorkAccount = "com.microsoft.workaccount.user.info";

        private readonly Context _androidContext;

        // Important: this object MUST be accessed on a background thread. Android will check this and throw otherwise.
        private readonly AccountManager _androidAccountManager;
        private readonly ICoreLogger _logger;

        private const long AccountManagerTimeoutSeconds = 5 * 60;

        public AndroidBrokerHelper(Context androidContext, ICoreLogger logger)
        {
            _androidContext = androidContext ?? throw new ArgumentNullException(nameof(androidContext));
            _logger = logger ?? throw new ArgumentNullException(nameof(logger));

            _logger.Verbose("Getting the Android context for broker request");
            _androidAccountManager = AccountManager.Get(_androidContext);
        }

        public bool CanSwitchToBroker()
        {
            string packageName = _androidContext.PackageName;

            // ADAL switches broker for following conditions:
            // 1- broker app is installed
            // 2- signature of the broker is valid
            // 3- account exists

            //Force this to return true for broker test app

            return VerifyAuthenticator()
                   && !packageName.Equals(BrokerConstants.PackageName, StringComparison.OrdinalIgnoreCase)
                   && !packageName
           .Equals(BrokerConstants.AzureAuthenticatorAppPackageName, StringComparison.OrdinalIgnoreCase);
        }

        public async Task<Intent> GetIntentForInteractiveBrokerRequestAsync(IDictionary<string, string> brokerPayload, Activity callerActivity)
        {
            Intent intent = null;

            try
            {
                IAccountManagerFuture result = null;
                // Callback is not passed since it is making a blocking call to get
                // intent. Activity needs to be launched from calling app
                // to get the calling app's metadata if needed at BrokerActivity.

                Bundle addAccountOptions = new Bundle();
                addAccountOptions.PutString(BrokerConstants.BrokerAccountManagerOperationKey, BrokerConstants.GetIntentForInteractiveRequest);

                result = _androidAccountManager.AddAccount(BrokerConstants.BrokerAccountType,
                    BrokerConstants.AuthtokenType,
                    null,
                    addAccountOptions,
                    null,
                    null,
                    GetPreferredLooper(callerActivity));

                if (result == null)
                {
                    _logger.Info("Android account manager didn't return any results for interactive broker request.");
                }

                Bundle bundleResult = (Bundle)(await result.GetResultAsync(
                     AccountManagerTimeoutSeconds,
                     TimeUnit.Seconds)
                     .ConfigureAwait(false));

                intent = (Intent)bundleResult?.GetParcelable(AccountManager.KeyIntent);

                //Validate that the intent was created successfully.
                if (intent != null)
                {
                    _logger.Info("Intent created from BundleResult is not null. Starting interactive broker request");
                    // Need caller info UID for broker communication
                    intent.PutExtra(BrokerConstants.CallerInfoUID, Binder.CallingUid);
                }
                else
                {
                    _logger.Info("Intent created from BundleResult is null. ");
                    throw new MsalException(MsalError.NullIntentReturnedFromAndroidBroker, MsalErrorMessage.NullIntentReturnedFromBroker);
                }

                intent = GetInteractiveBrokerIntent(brokerPayload, intent);
            }
            catch
            {
                _logger.Error("Error when trying to acquire intent for broker authentication.");
                throw;
            }

            return intent;
        }

        public async Task<string> GetBrokerAuthTokenSilentlyAsync(IDictionary<string, string> brokerPayload, Activity callerActivity)
        {
            CheckForBrokerAccountInfoInAccountManager(brokerPayload, callerActivity);
            Bundle silentOperationBundle = GetSilentBrokerBundle(brokerPayload);
            silentOperationBundle.PutString(BrokerConstants.BrokerAccountManagerOperationKey, BrokerConstants.AcquireTokenSilent);

            IAccountManagerFuture result = _androidAccountManager.AddAccount(BrokerConstants.BrokerAccountType,
                BrokerConstants.AuthtokenType,
                null,
                silentOperationBundle,
                null,
                null,
                GetPreferredLooper(callerActivity));

            if (result != null)
            {
                Bundle bundleResult = (Bundle)(await result.GetResultAsync(
                     AccountManagerTimeoutSeconds,
                     TimeUnit.Seconds)
                     .ConfigureAwait(false));

                if (bundleResult.GetBoolean(BrokerConstants.BrokerRequestV2Success))
                {
                    _logger.Info("Android Broker succsesfully refreshed the access token.");
                    return bundleResult.GetString(BrokerConstants.BrokerResultV2);
                }
            }

            _logger.Info("Android Broker didn't return any results.");
            return null;
        }

        /// <summary>
        /// This method is only used for Silent authnetication requests so that we can check to see if an account exists in the account manager before
        /// sending the silent request to the broker. 
        /// </summary>
        public void CheckForBrokerAccountInfoInAccountManager(IDictionary<string, string> brokerPayload, Activity callerActivity)
        {
            Bundle getAccountsBundle = GetBrokerAccountBundle(brokerPayload);
            getAccountsBundle.PutString(BrokerConstants.BrokerAccountManagerOperationKey, BrokerConstants.GetAccounts);

            var result = _androidAccountManager.AddAccount(BrokerConstants.BrokerAccountType,
                BrokerConstants.AuthtokenType,
                null,
                getAccountsBundle,
                null,
                null,
                GetPreferredLooper(callerActivity));

            if (result == null)
            {
                _logger.Info("Android account manager didn't return any accounts.");
                throw new MsalUiRequiredException(MsalError.NoAndroidBrokerAccountFound, MsalErrorMessage.NoAndroidBrokerAccountFound);
            }

            Bundle bundleResult = (Bundle)result?.Result;
            var accounts = bundleResult?.GetString(BrokerConstants.BrokerAccounts);

            string username = GetValueFromBrokerPayload(brokerPayload, BrokerParameter.Username);
            string homeAccountId = GetValueFromBrokerPayload(brokerPayload, BrokerParameter.HomeAccountId);
            string localAccountId = GetValueFromBrokerPayload(brokerPayload, BrokerParameter.LocalAccountId);

            if (!string.IsNullOrEmpty(accounts))
            {
                dynamic authResult = JArray.Parse(accounts);

                foreach (JObject account in authResult)
                {
                    var accountData = account[BrokerResponseConst.Account];

                    if (string.Equals((accountData[BrokerResponseConst.UserName]).ToString(), username, StringComparison.OrdinalIgnoreCase))
                    {
{'fraction_non_alphanumeric': 0.06529604318612531, 'fraction_numerical': 0.0016079940274507552, 'mean_word_length': 2.228401928068224, 'pattern_counts': {'":': 0, '<': 17, '<?xml version=': 0, '>': 17, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3439318', 'n_tokens_mistral': 4048, 'n_tokens_neox': 3815, 'n_words': 970}