/// <summary>
        /// Method that will get the Microsoft Graph token.
        /// </summary>
        /// <param name="graphConfigurationDetails">The graph configuration details.</param>
        /// <returns>The string that represents the Microsoft Graph token.</returns>
        private async Task <string> GetAccessTokenAsync(GraphConfigurationDetails graphConfigurationDetails)
        {
            string authority    = $"{graphConfigurationDetails.Instance}{graphConfigurationDetails.TenantId}";
            var    cache        = new RedisTokenCache(this.cache, graphConfigurationDetails.ClientId);
            var    authContext  = new AuthenticationContext(authority, cache);
            var    userIdentity = new UserIdentifier(graphConfigurationDetails.ShiftsAdminAadObjectId, UserIdentifierType.UniqueId);

            try
            {
                var result = await authContext.AcquireTokenSilentAsync(
                    "https://graph.microsoft.com",
                    new ClientCredential(
                        graphConfigurationDetails.ClientId,
                        graphConfigurationDetails.ClientSecret),
                    userIdentity).ConfigureAwait(false);

                return(result.AccessToken);
            }
            catch (AdalException adalEx)
            {
                this.telemetryClient.TrackException(adalEx);
                var retryResult = await authContext.AcquireTokenAsync(
                    "https://graph.microsoft.com",
                    new ClientCredential(
                        graphConfigurationDetails.ClientId,
                        graphConfigurationDetails.ClientSecret)).ConfigureAwait(false);

                return(retryResult.AccessToken);
            }
        }
Пример #2
0
        /// <summary>
        /// Method that will obtain the Graph token.
        /// </summary>
        /// <param name="tenantId">The Tenant ID.</param>
        /// <param name="instance">The instance.</param>
        /// <param name="clientId">The App ID of the Web Application.</param>
        /// <param name="clientSecret">The client secret.</param>
        /// <param name="userId">The AAD Object ID of the Admin, could also be the UPN.</param>
        /// <returns>A string that represents the Microsoft Graph API token.</returns>
        public async Task <string> GetAccessTokenAsync(
            string tenantId,
            string instance,
            string clientId,
            string clientSecret,
            string userId = default(string))
        {
            string authority    = $"{instance}{tenantId}";
            var    cache        = new RedisTokenCache(this.cache, clientId);
            var    authContext  = new AuthenticationContext(authority, cache);
            var    userIdentity = new UserIdentifier(userId, UserIdentifierType.UniqueId);

            try
            {
                var result = await authContext.AcquireTokenSilentAsync(
                    "https://graph.microsoft.com",
                    new ClientCredential(
                        clientId,
                        clientSecret),
                    userIdentity).ConfigureAwait(false);

                return(result.AccessToken);
            }
            catch (AdalException adalEx)
            {
                this.telemetryClient.TrackException(adalEx);
                var retryResult = await authContext.AcquireTokenAsync(
                    "https://graph.microsoft.com",
                    new ClientCredential(
                        clientId,
                        clientSecret)).ConfigureAwait(false);

                return(retryResult.AccessToken);
            }
        }
        /// <summary>
        /// This method gets called by the runtime. Use this method to add services to the container.
        /// </summary>
        /// <param name="services">The service collection.</param>
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc(opts =>
            {
                opts.Filters.Add(typeof(AdalTokenAcquisitionExceptionFilterAttribute));
            }).SetCompatibilityVersion(CompatibilityVersion.Version_2_2);

            services.AddDataProtection();

            // Wiring up App Insights
            services.AddApplicationInsightsTelemetry();
            services.AddSingleton <IKeyVaultHelper, KeyVaultHelper>();
            services.AddSingleton <AppSettings>();
            services.AddSingleton <IApiHelper, ApiHelper>();

            var serviceProvider = services.BuildServiceProvider();
            var appSettings     = serviceProvider.GetService <AppSettings>();

            // Add a strongly-typed options class to DI
            services.Configure <AuthOptionsModel>(opt =>
            {
                opt.Authority    = appSettings.Authority;
                opt.ClientId     = appSettings.ClientId;
                opt.ClientSecret = appSettings.ClientSecret;
            });

            services.AddScoped <ITokenCacheFactory, TokenCacheFactory>();

            services.AddStackExchangeRedisCache(options =>
            {
                options.Configuration = appSettings.RedisCacheConfiguration;
                options.InstanceName  = this.Configuration["RedisCacheInstanceName"];
            });

            services.AddHttpClient("ShiftsKronosIntegrationAPI", c =>
            {
                c.BaseAddress = new Uri(this.Configuration["BaseAddressFirstTimeSync"]);
                c.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
            }).AddPolicyHandler(GetRetryPolicy());

            services.AddHttpClient("GraphBetaAPI", client =>
            {
                client.BaseAddress = new Uri(this.Configuration["GraphApiUrl"]);
                client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
            }).AddPolicyHandler(GetRetryPolicy());

            services.AddAuthentication(auth =>
            {
                auth.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                auth.DefaultChallengeScheme    = OpenIdConnectDefaults.AuthenticationScheme;
                auth.DefaultSignInScheme       = CookieAuthenticationDefaults.AuthenticationScheme;
            })
            .AddCookie(opts =>
            {
                opts.SlidingExpiration = true;
                opts.AccessDeniedPath  = new PathString("/Account/AccessDenied");
            })
            .AddOpenIdConnect(
                opts =>
            {
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
                opts.Events.OnTicketReceived = async(context) =>
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
                {
                    context.Properties.ExpiresUtc = DateTime.UtcNow.AddHours(1);
                };

                opts.ClientId     = this.Configuration["ClientId"];
                opts.ClientSecret = this.Configuration["ClientSecret"];
                opts.Authority    = this.Configuration["Authority"];
                opts.ResponseType = this.Configuration["ResponseType"];

                this.Configuration.Bind(opts);
                opts.TokenValidationParameters.ValidateIssuer = false;
                opts.Events = new OpenIdConnectEvents
                {
                    OnAuthorizationCodeReceived = async ctx =>
                    {
                        HttpRequest request = ctx.HttpContext.Request;

                        // We need to also specify the redirect URL used
                        string currentUri = UriHelper.BuildAbsolute(request.Scheme, request.Host, request.PathBase, request.Path);

                        // Credentials for app itself
                        var credential = new ClientCredential(appSettings.ClientId, appSettings.ClientSecret);

                        // Construct token cache
                        var distributedCache = ctx.HttpContext.RequestServices.GetRequiredService <IDistributedCache>();
                        var cache            = new RedisTokenCache(distributedCache, appSettings.ClientId);

                        var authContext = new AuthenticationContext("https://login.microsoftonline.com/common", cache);

                        // Get token for Microsoft Graph API using the authorization code
                        string resource             = "https://graph.microsoft.com";
                        AuthenticationResult result = await authContext.AcquireTokenByAuthorizationCodeAsync(
                            ctx.ProtocolMessage.Code, new Uri(currentUri), credential, resource).ConfigureAwait(false);

                        // Tell the OIDC middleware we got the tokens, it doesn't need to do anything
                        ctx.HandleCodeRedemption(result.AccessToken, result.IdToken);
                    },
                };
            });

            services.Configure <CookiePolicyOptions>(options =>
            {
                // This lambda determines whether user consent for non-essential cookies is needed for a given request.
                options.CheckConsentNeeded    = context => true;
                options.MinimumSameSitePolicy = SameSiteMode.None;
            });

            services.AddOptions();
            services.AddHttpClient();

            // As each sub-integration is being implemented, we need to make sure that we can correctly setup the DI.g
            services.AddSingleton <BusinessLogic.Providers.IConfigurationProvider>((provider) => new BusinessLogic.Providers.ConfigurationProvider(
                                                                                       appSettings.StorageConnectionString,
                                                                                       provider.GetRequiredService <TelemetryClient>()));
            services.AddSingleton <ITeamDepartmentMappingProvider>((provider) => new TeamDepartmentMappingProvider(
                                                                       appSettings.StorageConnectionString,
                                                                       provider.GetRequiredService <TelemetryClient>()));
            services.AddSingleton <IUserMappingProvider>((provider) => new UserMappingProvider(
                                                             appSettings.StorageConnectionString,
                                                             provider.GetRequiredService <TelemetryClient>()));
            services.AddSingleton <IGraphUtility>((provider) => new GraphUtility(
                                                      provider.GetRequiredService <TelemetryClient>(),
                                                      provider.GetRequiredService <IDistributedCache>(),
                                                      provider.GetRequiredService <System.Net.Http.IHttpClientFactory>()));
            services.AddSingleton <ShiftsTeamKronosDepartmentViewModel>();
            services.AddSingleton <IShiftMappingEntityProvider>((provider) => new ShiftMappingEntityProvider(
                                                                    provider.GetRequiredService <TelemetryClient>(),
                                                                    appSettings.StorageConnectionString));
            services.AddSingleton <IOpenShiftMappingEntityProvider>((provider) => new OpenShiftMappingEntityProvider(
                                                                        provider.GetRequiredService <TelemetryClient>(),
                                                                        appSettings.StorageConnectionString));
            services.AddSingleton <ITimeOffMappingEntityProvider>((provider) => new TimeOffMappingEntityProvider(
                                                                      provider.GetRequiredService <TelemetryClient>(),
                                                                      appSettings.StorageConnectionString));
            services.AddSingleton <IAzureTableStorageHelper>((provider) => new AzureTableStorageHelper(
                                                                 appSettings.StorageConnectionString,
                                                                 provider.GetRequiredService <TelemetryClient>()));
            services.AddSingleton((provider) => new Utility(
                                      provider.GetRequiredService <TelemetryClient>(),
                                      provider.GetRequiredService <ILogonActivity>(),
                                      provider.GetRequiredService <AppSettings>(),
                                      provider.GetRequiredService <IDistributedCache>(),
                                      provider.GetRequiredService <BusinessLogic.Providers.IConfigurationProvider>(),
                                      provider.GetRequiredService <IAzureTableStorageHelper>(),
                                      provider.GetRequiredService <IGraphUtility>()));

            services.AddSingleton((provider) => new TeamDepartmentMappingController(
                                      provider.GetRequiredService <ITeamDepartmentMappingProvider>(),
                                      provider.GetRequiredService <TelemetryClient>(),
                                      provider.GetRequiredService <ILogonActivity>(),
                                      provider.GetRequiredService <IHyperFindLoadAllActivity>(),
                                      provider.GetRequiredService <ShiftsTeamKronosDepartmentViewModel>(),
                                      provider.GetRequiredService <Utility>(),
                                      provider.GetRequiredService <IGraphUtility>(),
                                      provider.GetRequiredService <AppSettings>(),
                                      provider.GetRequiredService <BusinessLogic.Providers.IConfigurationProvider>(),
                                      provider.GetRequiredService <IDistributedCache>(),
                                      provider.GetRequiredService <IUserMappingProvider>(),
                                      provider.GetRequiredService <System.Net.Http.IHttpClientFactory>()));

            services.AddSingleton((provider) => new UserMappingController(
                                      provider.GetRequiredService <AppSettings>(),
                                      provider.GetRequiredService <IGraphUtility>(),
                                      provider.GetRequiredService <ILogonActivity>(),
                                      provider.GetRequiredService <IHyperFindActivity>(),
                                      provider.GetRequiredService <TelemetryClient>(),
                                      provider.GetRequiredService <IUserMappingProvider>(),
                                      provider.GetRequiredService <ITeamDepartmentMappingProvider>(),
                                      provider.GetRequiredService <BusinessLogic.Providers.IConfigurationProvider>(),
                                      provider.GetRequiredService <IJobAssignmentActivity>(),
                                      provider.GetRequiredService <IHostingEnvironment>(),
                                      provider.GetRequiredService <Utility>()));

            // Wiring up Kronos dependency chain to set up DI container.
            services.AddSingleton <App.KronosWfc.Models.RequestEntities.Logon.Request>();
            services.AddSingleton <ILogonActivity, LogonActivity>((provider) => new LogonActivity(
                                                                      new App.KronosWfc.Models.RequestEntities.Logon.Request(),
                                                                      provider.GetRequiredService <TelemetryClient>(),
                                                                      provider.GetRequiredService <IApiHelper>()));
            services.AddSingleton <IHyperFindLoadAllActivity, HyperFindLoadAllActivity>((provider) => new HyperFindLoadAllActivity(
                                                                                            provider.GetRequiredService <TelemetryClient>(),
                                                                                            provider.GetRequiredService <IApiHelper>()));
            services.AddSingleton <IHyperFindActivity, HyperFindActivity>((provider) => new HyperFindActivity(
                                                                              provider.GetRequiredService <TelemetryClient>(),
                                                                              provider.GetRequiredService <IApiHelper>()));
            services.AddSingleton <IJobAssignmentActivity, JobAssignmentActivity>((provider) => new JobAssignmentActivity(
                                                                                      provider.GetRequiredService <TelemetryClient>(),
                                                                                      provider.GetRequiredService <IApiHelper>()));
        }