-
Notifications
You must be signed in to change notification settings - Fork 644
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Temp keys user policies #3820
Temp keys user policies #3820
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,48 @@ | ||
// Copyright (c) .NET Foundation. All rights reserved. | ||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
using System.ComponentModel.DataAnnotations; | ||
|
||
namespace NuGetGallery | ||
{ | ||
/// <summary> | ||
/// User-subscribed security policy. | ||
/// </summary> | ||
public class UserSecurityPolicy : IEntity | ||
{ | ||
public UserSecurityPolicy() | ||
{ | ||
} | ||
|
||
public UserSecurityPolicy(string name) | ||
{ | ||
Name = name; | ||
} | ||
|
||
/// <summary> | ||
/// Policy key. | ||
/// </summary> | ||
public int Key { get; set; } | ||
|
||
/// <summary> | ||
/// User key. | ||
/// </summary> | ||
public int UserKey { get; set; } | ||
|
||
/// <summary> | ||
/// User subscribed to this security policy. | ||
/// </summary> | ||
public User User { get; set; } | ||
|
||
/// <summary> | ||
/// Type name for the policy handler that provides policy behavior. | ||
/// </summary> | ||
[Required] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Can' this [Required] attribute be defined in EntitiesContext instead? Seems like you are requiring User. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. HasRequired in EntitiesContext is for a required relationship. I added Required here since it's the only non-nullable column that isn't a PK/FK. Not sure how this actually gets used, but we don't seem to be consistent with [Required] usage. |
||
public string Name { get; set; } | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I think type name is an implementation detail. We should have a hard coded constant for this (either an int which is consistent with the rest of the DB or const string). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Policy handler names are now constants. I left as strings which I think will be easier to read when querying the DB. This PR only reads policies, but the next PR (onboarding) will start writing them. |
||
|
||
/// <summary> | ||
/// Support for JSON-serialized properties for specific policies. | ||
/// </summary> | ||
public string Value { get; set; } | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -25,6 +25,7 @@ | |
using NuGetGallery.Infrastructure; | ||
using NuGetGallery.Infrastructure.Authentication; | ||
using NuGetGallery.Infrastructure.Lucene; | ||
using NuGetGallery.Security; | ||
|
||
namespace NuGetGallery | ||
{ | ||
|
@@ -201,6 +202,11 @@ protected override void Load(ContainerBuilder builder) | |
.As<IStatusService>() | ||
.InstancePerLifetimeScope(); | ||
|
||
builder.RegisterType<SecurityPolicyService>() | ||
.AsSelf() | ||
.As<ISecurityPolicyService>() | ||
.InstancePerLifetimeScope(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Any reason for ISecurityPolicyService not to be a single instance (SingleInstance())? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we should prefer InstancePerLifetimeScope over SuntingleInstance until we have a caching/perf/behavior requirement for it. It's easier for unintended interaction between requests to occur with SingleInstance In reply to: 113238685 [](ancestors = 113238685) |
||
|
||
var mailSenderThunk = new Lazy<IMailSender>( | ||
() => | ||
{ | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -8,13 +8,38 @@ | |
using System.Web; | ||
using System.Web.Mvc; | ||
using NuGetGallery.Authentication; | ||
using NuGetGallery.Security; | ||
using AuthenticationTypes = NuGetGallery.Authentication.AuthenticationTypes; | ||
using AuthorizationContext = System.Web.Mvc.AuthorizationContext; | ||
using System.Net; | ||
|
||
namespace NuGetGallery.Filters | ||
{ | ||
public sealed class ApiAuthorizeAttribute : AuthorizeAttribute | ||
{ | ||
/// <summary> | ||
/// Security policy action, or null for no evaluation. | ||
/// </summary> | ||
public SecurityPolicyAction? SecurityPolicyAction { get; } | ||
|
||
/// <summary> | ||
/// Security policy evaluation result. | ||
/// </summary> | ||
private SecurityPolicyResult SecurityPolicyResult { get; set; } | ||
|
||
/// <summary> | ||
/// Security policy service. | ||
/// </summary> | ||
private ISecurityPolicyService SecurityPolicyService { get; set; } | ||
|
||
public ApiAuthorizeAttribute() | ||
{} | ||
|
||
public ApiAuthorizeAttribute(SecurityPolicyAction action) | ||
{ | ||
SecurityPolicyAction = action; | ||
} | ||
|
||
public override void OnAuthorization(AuthorizationContext filterContext) | ||
{ | ||
// Add a warning header if the API key is about to expire (or has expired) | ||
|
@@ -49,16 +74,47 @@ public override void OnAuthorization(AuthorizationContext filterContext) | |
} | ||
} | ||
} | ||
|
||
// Resolve the policy service if security policy checks are required. | ||
if (SecurityPolicyAction.HasValue) | ||
{ | ||
SecurityPolicyService = ((AppController)filterContext.Controller)?.GetService<ISecurityPolicyService>(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This will throw if the |
||
} | ||
|
||
base.OnAuthorization(filterContext); | ||
} | ||
|
||
protected override bool AuthorizeCore(HttpContextBase httpContext) | ||
{ | ||
var authorizeResult = base.AuthorizeCore(httpContext); | ||
|
||
// If ApiKey authorization succeeds, evaluate any security policies. | ||
if (authorizeResult && SecurityPolicyAction.HasValue) | ||
{ | ||
SecurityPolicyResult = SecurityPolicyService.Evaluate(SecurityPolicyAction.Value, httpContext); | ||
return SecurityPolicyResult.Success; | ||
} | ||
|
||
return authorizeResult; | ||
} | ||
|
||
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) | ||
{ | ||
var owinContext = filterContext.HttpContext.GetOwinContext(); | ||
owinContext.Authentication.Challenge(AuthenticationTypes.ApiKey); | ||
owinContext.Response.StatusCode = 401; | ||
filterContext.Result = new HttpUnauthorizedResult(); | ||
|
||
// ApiKey authorization succeeded, but a security policy failed. | ||
if (SecurityPolicyResult != null) | ||
{ | ||
owinContext.Response.StatusCode = 400; | ||
filterContext.Result = new HttpStatusCodeWithBodyResult(HttpStatusCode.BadRequest, SecurityPolicyResult.ErrorMessage); | ||
} | ||
// ApiKey authorization failed. | ||
else | ||
{ | ||
owinContext.Authentication.Challenge(AuthenticationTypes.ApiKey); | ||
owinContext.Response.StatusCode = 401; | ||
filterContext.Result = new HttpUnauthorizedResult(); | ||
} | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
// Copyright (c) .NET Foundation. All rights reserved. | ||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
namespace NuGetGallery.Filters | ||
{ | ||
public enum SecurityPolicyAction | ||
{ | ||
PackagePush, | ||
PackageVerify | ||
} | ||
} |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
// Copyright (c) .NET Foundation. All rights reserved. | ||
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. | ||
|
||
namespace NuGetGallery.Migrations | ||
{ | ||
using System.Data.Entity.Migrations; | ||
|
||
public partial class SecurityPolicies : DbMigration | ||
{ | ||
public override void Up() | ||
{ | ||
CreateTable("UserSecurityPolicies", c => new | ||
{ | ||
Key = c.Int(nullable: false, identity: true), | ||
Name = c.String(nullable: false, maxLength: 256), | ||
UserKey = c.Int(nullable: false), | ||
Value = c.String(nullable: true, maxLength: 256) | ||
}) | ||
.PrimaryKey(t => t.Key) | ||
.ForeignKey("Users", t => t.UserKey); | ||
} | ||
|
||
public override void Down() | ||
{ | ||
DropTable("UserSecurityPolicies"); | ||
} | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is not this duplication of the line 123.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Removed