Пример #1
0
        private async Task UpdateUserAsync(ApplicationUser user,
                                           BuildAssetRegistryContext dbContext,
                                           UserManager <ApplicationUser> userManager,
                                           SignInManager <ApplicationUser> signInManager,
                                           GitHubClaimResolver gitHubClaimResolver)
        {
            using (IDbContextTransaction txn = await dbContext.Database.BeginTransactionAsync())
            {
                string token = await userManager.GetAuthenticationTokenAsync(user, GitHubScheme, "access_token");

                var newClaims = (await gitHubClaimResolver.GetUserInformationClaims(token)).Concat(
                    await gitHubClaimResolver.GetMembershipClaims(token)
                    ).Where(AccountController.ShouldAddClaimToUser);
                var currentClaims = (await userManager.GetClaimsAsync(user)).ToList();

                // remove old claims
                await userManager.RemoveClaimsAsync(user, currentClaims);

                // add new claims
                await userManager.AddClaimsAsync(user, newClaims);

                user.LastUpdated = DateTimeOffset.UtcNow;
                await dbContext.SaveChangesAsync();

                txn.Commit();
            }
        }
Пример #2
0
        private async Task UpdateUserIfNeededAsync(ApplicationUser user,
                                                   BuildAssetRegistryContext dbContext,
                                                   UserManager <ApplicationUser> userManager,
                                                   SignInManager <ApplicationUser> signInManager,
                                                   GitHubClaimResolver gitHubClaimResolver)
        {
            while (true)
            {
                try
                {
                    if (ShouldUpdateUser(user))
                    {
                        await UpdateUserAsync(user, dbContext, userManager, signInManager, gitHubClaimResolver);
                    }

                    break;
                }
                catch (DbUpdateConcurrencyException)
                {
                    // If we have a concurrent modification exception reload the data from the DB and try again
                    foreach (EntityEntry entry in dbContext.ChangeTracker.Entries())
                    {
                        await entry.ReloadAsync();
                    }
                }
            }
        }
Пример #3
0
 public TokenModel(
     ITokenStore tokens,
     ITokenRevocationProvider revocation,
     GitHubUserTokenHandler handler,
     GitHubClaimResolver resolver)
 {
     _tokens     = tokens;
     _revocation = revocation;
     _handler    = handler;
     _resolver   = resolver;
 }
Пример #4
0
        private void AddServices(IServiceCollection services)
        {
            services.AddMvc().WithRazorPagesRoot("/Pages").AddRazorPagesOptions(o => o.Conventions.AuthorizeFolder("/", MsftAuthorizationPolicyName).AllowAnonymousToPage("/Index"));
            services.AddApplicationInsightsTelemetry(Configuration.GetSection("ApplicationInsights").Bind);
            services.AddAuthentication()
            .AddGitHubOAuth(Configuration.GetSection("GitHubAuthentication"), GitHubScheme)
            .AddScheme <UserTokenOptions, GitHubUserTokenHandler>("github-token", o => { })
            .AddCookie(IdentityConstants.ApplicationScheme,
                       o =>
            {
                o.ExpireTimeSpan     = TimeSpan.FromDays(7);
                o.SlidingExpiration  = true;
                o.Cookie.IsEssential = true;
                o.LoginPath          = "/signin";
                o.LogoutPath         = "/signout";
                o.ReturnUrlParameter = "r";
                o.Events             = new CookieAuthenticationEvents
                {
                    OnValidatePrincipal = async ctx =>
                    {
                        GitHubClaimResolver resolver =
                            ctx.HttpContext.RequestServices.GetRequiredService <GitHubClaimResolver>();
                        ClaimsIdentity identity = ctx.Principal.Identities.FirstOrDefault();
                        identity?.AddClaims(await resolver.GetMembershipClaims(resolver.GetAccessToken(ctx.Principal)));
                    }
                };
            })
            ;
            services.AddAzureTableTokenStore(o => Configuration.GetSection("AzureTableTokenStore").Bind(o));
            services.AddAuthorization(
                options =>
            {
                options.AddPolicy(
                    MsftAuthorizationPolicyName,
                    policy =>
                {
                    policy.RequireAuthenticatedUser();
                    if (!Env.IsDevelopment())
                    {
                        policy.RequireRole("github:team:dotnet/dnceng", "github:team:dotnet/bots-high");
                    }
                });
            });

            services.AddScoped <SimpleSigninMiddleware>();
            services.AddGitHubTokenProvider();
            services.AddSingleton <IInstallationLookup, InMemoryCacheInstallationLookup>();
            services.AddContextAwareAuthenticationScheme(o =>
            {
                o.SelectScheme = p => p.StartsWithSegments("/api") ? "github-token" : IdentityConstants.ApplicationScheme;
            });
            services.AddSingleton <GitHubJwtFactory>();
        }
 public GitHubUserTokenHandler(
     GitHubClaimResolver resolver,
     IOptionsMonitor <UserTokenOptions> options,
     IDataProtectionProvider dataProtector,
     ITokenRevocationProvider revocation,
     ILoggerFactory logger,
     UrlEncoder encoder,
     ISystemClock clock) : base(options, logger, encoder, clock)
 {
     _resolver      = resolver;
     _dataProtector = dataProtector.CreateProtector("github-token");
     _revocation    = revocation;
 }
Пример #6
0
        private void AddServices(IServiceCollection services)
        {
            services.AddRazorPages(o =>
            {
                o.Conventions
                .AuthorizeFolder("/", MsftAuthorizationPolicyName)
                .AllowAnonymousToPage("/Index")
                .AllowAnonymousToPage("/Status")
                .AllowAnonymousToPage("/Error");
                o.RootDirectory = "/Pages";
            });

            services.AddControllers()
            .AddGitHubWebHooks();

            services.AddApplicationInsightsTelemetry(Configuration.GetSection("ApplicationInsights").Bind);
            services.Configure <LoggerFilterOptions>(o =>
            {
                // This handler is added by 'AddApplicationInsightsTelemetry' above and hard limits
                // and reporting below "warning", which basically kills all logging
                // Remove it, we already configured the filters in Program.cs
                o.Rules.Remove(o.Rules.FirstOrDefault(r =>
                                                      r.ProviderName ==
                                                      "Microsoft.Extensions.Logging.ApplicationInsights.ApplicationInsightsLoggerProvider"));
            });

            services.AddAuthentication("contextual")
            .AddPolicyScheme("contextual", "Contextual Scheme",
                             o => { o.ForwardDefaultSelector = context =>
                                    {
                                        if (context.Request.Path.StartsWithSegments("/api"))
                                        {
                                            return("github-token");
                                        }

                                        return(IdentityConstants.ApplicationScheme);
                                    }; })
            .AddGitHubOAuth(Configuration.GetSection("GitHubAuthentication"), GitHubScheme)
            .AddScheme <UserTokenOptions, GitHubUserTokenHandler>("github-token", o => { })
            .AddCookie(IdentityConstants.ApplicationScheme,
                       o =>
            {
                o.ExpireTimeSpan     = TimeSpan.FromDays(7);
                o.SlidingExpiration  = true;
                o.Cookie.IsEssential = true;
                o.LoginPath          = "/signin";
                o.LogoutPath         = "/signout";
                o.ReturnUrlParameter = "r";
                o.Events             = new CookieAuthenticationEvents
                {
                    OnValidatePrincipal = async ctx =>
                    {
                        GitHubClaimResolver resolver =
                            ctx.HttpContext.RequestServices.GetRequiredService <GitHubClaimResolver>();
                        ClaimsIdentity identity = ctx.Principal.Identities.FirstOrDefault();
                        identity?.AddClaims(await resolver.GetMembershipClaims(resolver.GetAccessToken(ctx.Principal)));
                    },
                };
            })
            .AddExternalCookie()
            ;
            services.AddAzureTableTokenStore(o => Configuration.GetSection("AzureTableTokenStore").Bind(o));
            services.AddAuthorization(
                options =>
            {
                options.AddPolicy(
                    MsftAuthorizationPolicyName,
                    policy =>
                {
                    policy.RequireAuthenticatedUser();
                    if (!Env.IsDevelopment())
                    {
                        policy.RequireRole(GitHubClaimResolver.GetTeamRole("dotnet", "dnceng"), GitHubClaimResolver.GetTeamRole("dotnet", "bots-high"));
                    }
                });
            });
            services.AddKustoIngest(options => Configuration.GetSection("Kusto").Bind(options));

            services.AddScoped <SimpleSigninMiddleware>();
            services.AddGitHubTokenProvider();
            services.AddSingleton <IInstallationLookup, InMemoryCacheInstallationLookup>();

            services.AddSingleton <ZenHubClient>();
            services.AddSingleton <IGitHubApplicationClientFactory, GitHubApplicationClientFactory>();
            services.AddSingleton <IGitHubClientFactory, GitHubClientFactory>();
        }
Пример #7
0
        private void AddServices(IServiceCollection services)
        {
            services.AddRazorPages(o =>
            {
                o.Conventions
                .AuthorizeFolder("/", MsftAuthorizationPolicyName)
                .AllowAnonymousToPage("/Index")
                .AllowAnonymousToPage("/Status")
                .AllowAnonymousToPage("/Routes")
                .AllowAnonymousToPage("/Error");
                o.RootDirectory = "/Pages";
            });

            services.AddControllers()
            .AddGitHubWebHooks();

            services.AddApplicationInsightsTelemetry(Configuration.GetSection("ApplicationInsights").Bind);
            services.Configure <LoggerFilterOptions>(o =>
            {
                // This handler is added by 'AddApplicationInsightsTelemetry' above and hard limits
                // and reporting below "warning", which basically kills all logging
                // Remove it, we already configured the filters in Program.cs
                o.Rules.Remove(o.Rules.FirstOrDefault(r =>
                                                      r.ProviderName ==
                                                      "Microsoft.Extensions.Logging.ApplicationInsights.ApplicationInsightsLoggerProvider"));

                // These two categories log a lot of noise at "Information", let's raise them to warning
                o.Rules.Add(new LoggerFilterRule(null, "Microsoft.AspNetCore.Mvc.ViewFeatures.Filters.ValidateAntiforgeryTokenAuthorizationFilter", LogLevel.Warning, null));
                o.Rules.Add(new LoggerFilterRule(null, "Microsoft.AspNetCore.Mvc.ViewFeatures.Filters.AutoValidateAntiforgeryTokenAuthorizationFilter", LogLevel.Warning, null));
            });

            services.AddAuthentication("contextual")
            .AddPolicyScheme("contextual", "Contextual Scheme",
                             o => { o.ForwardDefaultSelector = context =>
                                    {
                                        if (context.Request.Path.StartsWithSegments("/api/webhooks"))
                                        {
                                            return("nothing");
                                        }
                                        if (context.Request.Path.StartsWithSegments("/api"))
                                        {
                                            return("github-token");
                                        }

                                        return(IdentityConstants.ApplicationScheme);
                                    }; })
            .AddGitHubOAuth(Configuration.GetSection("GitHubAuthentication"), GitHubScheme)
            .AddScheme <NothingOptions, NothingHandler>("nothing", o => { })
            .AddScheme <UserTokenOptions, GitHubUserTokenHandler>("github-token", o => { })
            .AddCookie(IdentityConstants.ApplicationScheme,
                       o =>
            {
                o.ExpireTimeSpan     = TimeSpan.FromMinutes(30);
                o.SlidingExpiration  = true;
                o.Cookie.IsEssential = true;
                o.LoginPath          = "/signin";
                o.LogoutPath         = "/signout";
                o.ReturnUrlParameter = "r";
                o.Events             = new CookieAuthenticationEvents
                {
                    OnValidatePrincipal = async ctx =>
                    {
                        GitHubClaimResolver resolver =
                            ctx.HttpContext.RequestServices.GetRequiredService <GitHubClaimResolver>();
                        ClaimsIdentity identity = ctx.Principal.Identities.FirstOrDefault();
                        identity?.AddClaims(await resolver.GetMembershipClaims(resolver.GetAccessToken(ctx.Principal)));
                    },
                };
            })
            .AddExternalCookie()
            ;
            services.AddAzureTableTokenStore(o => Configuration.GetSection("AzureTableTokenStore").Bind(o));
            services.AddAuthorization(
                options =>
            {
                options.AddPolicy(
                    MsftAuthorizationPolicyName,
                    policy =>
                {
                    policy.RequireAuthenticatedUser();
                    if (!Env.IsDevelopment())
                    {
                        policy.RequireRole(GitHubClaimResolver.GetTeamRole("dotnet", "dnceng"), GitHubClaimResolver.GetTeamRole("dotnet", "bots-high"));
                    }
                });
            });
            services.AddKustoIngest(options => Configuration.GetSection("Kusto").Bind(options));

            services.AddScoped <SimpleSigninMiddleware>();
            services.AddGitHubTokenProvider();
            services.AddSingleton <IInstallationLookup, InMemoryCacheInstallationLookup>();

            services.AddSingleton <ZenHubClient>();
            services.AddSingleton <IGitHubApplicationClientFactory, GitHubApplicationClientFactory>();
            services.AddSingleton <IGitHubClientFactory, GitHubClientFactory>();
            services.AddSingleton <ITimelineIssueTriage, TimelineIssueTriage>();
            services.AddSingleton <ExponentialRetry>();
            services.AddSingleton <ISystemClock, SystemClock>();
            services.AddSingleton <Microsoft.Extensions.Internal.ISystemClock, Microsoft.Extensions.Internal.SystemClock>();
            services.AddHttpClient();
            services.AddHealthReporting(
                b =>
            {
                b.AddLogging();
                b.AddAzureTable((o, p) => o.WriteSasUri = p.GetRequiredService <IConfiguration>()["HealthTableUri"]);
            });

            services.AddScoped <ITeamMentionForwarder, TeamMentionForwarder>();
        }
Пример #8
0
        private void ConfigureAuthServices(IServiceCollection services)
        {
            services.AddIdentity <ApplicationUser, IdentityRole <int> >(
                options => { options.Lockout.AllowedForNewUsers = false; })
            .AddEntityFrameworkStores <BuildAssetRegistryContext>();

            services.AddAuthentication(options =>
            {
                options.DefaultAuthenticateScheme = options.DefaultChallengeScheme = options.DefaultScheme = "Contextual";
                options.DefaultSignInScheme       = IdentityConstants.ExternalScheme;
            })
            .AddPolicyScheme("Contextual", "Contextual",
                             policyOptions => { policyOptions.ForwardDefaultSelector = ctx => ctx.Request.Path.StartsWithSegments("/api") ? PersonalAccessTokenDefaults.AuthenticationScheme : IdentityConstants.ApplicationScheme; })
            .AddGitHubOAuth(Configuration.GetSection("GitHubAuthentication"), GitHubScheme)
            .AddPersonalAccessToken <ApplicationUser>(
                options =>
            {
                options.Events = new PersonalAccessTokenEvents <ApplicationUser>
                {
                    OnSetTokenHash = async context =>
                    {
                        var dbContext = context.HttpContext.RequestServices
                                        .GetRequiredService <BuildAssetRegistryContext>();
                        int userId = context.User.Id;
                        var token  = new ApplicationUserPersonalAccessToken
                        {
                            ApplicationUserId = userId,
                            Name    = context.Name,
                            Hash    = context.Hash,
                            Created = DateTimeOffset.UtcNow
                        };
                        await dbContext.Set <ApplicationUserPersonalAccessToken>().AddAsync(token);
                        await dbContext.SaveChangesAsync();

                        return(token.Id);
                    },
                    OnGetTokenHash = async context =>
                    {
                        var dbContext = context.HttpContext.RequestServices
                                        .GetRequiredService <BuildAssetRegistryContext>();
                        ApplicationUserPersonalAccessToken token = await dbContext
                                                                   .Set <ApplicationUserPersonalAccessToken>()
                                                                   .Where(t => t.Id == context.TokenId)
                                                                   .Include(t => t.ApplicationUser)
                                                                   .FirstOrDefaultAsync();
                        if (token != null)
                        {
                            context.Success(token.Hash, token.ApplicationUser);
                        }
                    },
                    OnValidatePrincipal = async context =>
                    {
                        ApplicationUser user = context.User;
                        var dbContext        = context.HttpContext.RequestServices
                                               .GetRequiredService <BuildAssetRegistryContext>();
                        var userManager = context.HttpContext.RequestServices
                                          .GetRequiredService <UserManager <ApplicationUser> >();
                        var signInManager = context.HttpContext.RequestServices
                                            .GetRequiredService <SignInManager <ApplicationUser> >();
                        var gitHubClaimResolver = context.HttpContext.RequestServices
                                                  .GetRequiredService <GitHubClaimResolver>();

                        await UpdateUserIfNeededAsync(user, dbContext, userManager, signInManager, gitHubClaimResolver);

                        ClaimsPrincipal principal = await signInManager.CreateUserPrincipalAsync(user);
                        context.ReplacePrincipal(principal);
                    }
                };
            });
            services.ConfigureExternalCookie(
                options =>
            {
                options.ExpireTimeSpan     = TimeSpan.FromMinutes(30);
                options.ReturnUrlParameter = "returnUrl";
                options.LoginPath          = "/Account/SignIn";
                options.Events             = new CookieAuthenticationEvents
                {
                    OnRedirectToLogin = ctx =>
                    {
                        if (ctx.Request.Path.StartsWithSegments("/api"))
                        {
                            ctx.Response.StatusCode = 401;
                            return(Task.CompletedTask);
                        }

                        ctx.Response.Redirect(ctx.RedirectUri);
                        return(Task.CompletedTask);
                    },
                    OnRedirectToAccessDenied = ctx =>
                    {
                        ctx.Response.StatusCode = 403;
                        return(Task.CompletedTask);
                    },
                };
            });
            services.ConfigureApplicationCookie(
                options =>
            {
                options.ExpireTimeSpan     = LoginCookieLifetime;
                options.SlidingExpiration  = true;
                options.ReturnUrlParameter = "returnUrl";
                options.LoginPath          = "/Account/SignIn";
                options.Events             = new CookieAuthenticationEvents
                {
                    OnSigningIn = async ctx =>
                    {
                        var dbContext = ctx.HttpContext.RequestServices
                                        .GetRequiredService <BuildAssetRegistryContext>();
                        var signInManager = ctx.HttpContext.RequestServices
                                            .GetRequiredService <SignInManager <ApplicationUser> >();
                        var userManager = ctx.HttpContext.RequestServices
                                          .GetRequiredService <UserManager <ApplicationUser> >();
                        ExternalLoginInfo info = await signInManager.GetExternalLoginInfoAsync();

                        var user = await userManager.GetUserAsync(ctx.Principal);
                        await UpdateUserTokenAsync(dbContext, userManager, user, info);

                        IdentityOptions identityOptions = ctx.HttpContext.RequestServices
                                                          .GetRequiredService <IOptions <IdentityOptions> >()
                                                          .Value;

                        // replace the ClaimsPrincipal we are about to serialize to the cookie with a reference
                        Claim claim = ctx.Principal.Claims.First(
                            c => c.Type == identityOptions.ClaimsIdentity.UserIdClaimType);
                        Claim[] claims = { claim };
                        var identity   = new ClaimsIdentity(claims, IdentityConstants.ApplicationScheme);
                        ctx.Principal  = new ClaimsPrincipal(identity);
                    },
                    OnValidatePrincipal = async ctx =>
                    {
                        var dbContext = ctx.HttpContext.RequestServices
                                        .GetRequiredService <BuildAssetRegistryContext>();
                        var userManager = ctx.HttpContext.RequestServices
                                          .GetRequiredService <UserManager <ApplicationUser> >();
                        var signInManager = ctx.HttpContext.RequestServices
                                            .GetRequiredService <SignInManager <ApplicationUser> >();
                        var gitHubClaimResolver = ctx.HttpContext.RequestServices
                                                  .GetRequiredService <GitHubClaimResolver>();

                        // extract the userId from the ClaimsPrincipal and read the user from the Db
                        ApplicationUser user = await userManager.GetUserAsync(ctx.Principal);
                        if (user == null)
                        {
                            ctx.RejectPrincipal();
                        }
                        else
                        {
                            await UpdateUserIfNeededAsync(user, dbContext, userManager, signInManager, gitHubClaimResolver);

                            ClaimsPrincipal principal = await signInManager.CreateUserPrincipalAsync(user);
                            ctx.ReplacePrincipal(principal);
                        }
                    }
                };
            });

            services.AddAuthorization(
                options =>
            {
                options.AddPolicy(
                    MsftAuthorizationPolicyName,
                    policy =>
                {
                    policy.RequireAuthenticatedUser();
                    if (!HostingEnvironment.IsDevelopment())
                    {
                        policy.RequireRole(GitHubClaimResolver.GetTeamRole("dotnet", "dnceng"), GitHubClaimResolver.GetTeamRole("dotnet", "arcade-contrib"));
                    }
                });
            });

            services.Configure <MvcOptions>(
                options =>
            {
                options.Conventions.Add(new DefaultAuthorizeActionModelConvention(MsftAuthorizationPolicyName));
            });
        }