Skip to content

Commit

Permalink
Merge pull request #22 in AUTH/d2l.security.oauth2.webapi from ~JPARK…
Browse files Browse the repository at this point in the history
…ER/d2l.security.oauth2.webapi:fixes to master

* commit '2f8de21732ebdbc4ce157e4a89f743aa3efdf945':
  Check for AllowFrom attribute at controller level
  Make NoRequiredScopeAttribute public
  Restrict [NoRequiredScope] to methods
  • Loading branch information
j3parker committed Nov 27, 2015
2 parents f8c62db + cd9de48 commit d1cde7e
Show file tree
Hide file tree
Showing 6 changed files with 99 additions and 6 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
using System.Net;
using System.Threading.Tasks;
using NUnit.Framework;
using D2L.Services;

namespace D2L.Security.OAuth2.Authorization {
[TestFixture]
internal sealed class AllowFromControllerTests {
private const string SCOPE = "a:b:c";

[Test]
public async Task Default_NoAuthentication_403() {
await TestUtilities.RunBasicAuthTest( "/allowfrom/default", HttpStatusCode.Unauthorized )
.SafeAsync();
}

[Test]
public async Task Default_UserInvalidScope_401() {
string jwt = await TestUtilities.GetAccessTokenValidForAMinute(
userId: 123,
scope: SCOPE + "foo"
).SafeAsync();

await TestUtilities.RunBasicAuthTest( "/allowfrom/default", jwt, HttpStatusCode.Forbidden )
.SafeAsync();
}

[Test]
public async Task Default_UserValidScope_204() {
string jwt = await TestUtilities.GetAccessTokenValidForAMinute(
userId: 123,
scope: SCOPE
).SafeAsync();

await TestUtilities.RunBasicAuthTest( "/allowfrom/default", jwt, HttpStatusCode.NoContent )
.SafeAsync();
}

[TestCase( 0, "wrong:scope:ok", HttpStatusCode.Unauthorized, TestName="A service with the wrong scope" )]
[TestCase( 0, SCOPE, HttpStatusCode.Unauthorized, TestName= "A service with the right scope" )]
[TestCase( 123, "wrong:scope:ok", HttpStatusCode.Forbidden, TestName = "A user with the wrong scope" )]
[TestCase( 123, SCOPE, HttpStatusCode.NoContent, TestName = "A user with the right scope" )]
public async Task Default_Service_403( long userId, string scope, HttpStatusCode expectedStatusCode ) {
string jwt = await TestUtilities.GetAccessTokenValidForAMinute(
userId: userId == 0 ? (long?)null : userId,
scope: scope
).SafeAsync();

await TestUtilities.RunBasicAuthTest( "/allowfrom/default", jwt, expectedStatusCode )
.SafeAsync();
}

[TestCase( 0, "wrong:scope:ok", HttpStatusCode.Forbidden, TestName="A service with wrong scope fails authz" )]
[TestCase( 0, SCOPE, HttpStatusCode.NoContent, TestName="A service with the right scope succeeds" )]
[TestCase( 123, "wrong:scope:ok", HttpStatusCode.Forbidden, TestName="A user with the wrong scope fails authz (wrong kind of authn) TODO: does 401 instead of 403 (arguably) due to order of attributes" )]
[TestCase( 123, SCOPE, HttpStatusCode.Unauthorized, TestName="A user with the right scope fails authz (wrong kind of authn)" )]
public async Task ServicesOnly_AuthenticationOkCases( long userId, string scope, HttpStatusCode expectedStatusCode ) {
string jwt = await TestUtilities.GetAccessTokenValidForAMinute(
userId: userId == 0 ? (long?)null : userId,
scope: scope
).SafeAsync();

await TestUtilities.RunBasicAuthTest( "/allowfrom/servicesonly", jwt, expectedStatusCode )
.SafeAsync();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -93,9 +93,11 @@
</ItemGroup>
<ItemGroup>
<Compile Include="Authentication\OAuth2AuthenticationFilterTests.cs" />
<Compile Include="Authorization\AllowFromControllerTests.cs" />
<Compile Include="Authorization\DefaultAuthorizationAttributeTests.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="SetUpFixture.cs" />
<Compile Include="TestWebService\Controllers\AllowFromController.cs" />
<Compile Include="TestWebService\Controllers\AuthenticationFilterTestsController.cs" />
<Compile Include="TestWebService\Controllers\AuthorizationAttributeTestsController.cs" />
<Compile Include="TestUtilities.cs" />
Expand Down
4 changes: 2 additions & 2 deletions D2L.Security.OAuth2.WebApi.IntegrationTests/SetupFixture.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,11 @@ private void OwinStartup( IAppBuilder appBuilder ) {
principalDependencyRegistry: new Mock<ID2LPrincipalDependencyRegistry>( MockBehavior.Loose ).Object
);

config.MapHttpAttributeRoutes();

// TODO: adding this globally might suck later depending on what we want to test
config.Filters.Add( authFilter );

config.MapHttpAttributeRoutes();

config.EnsureInitialized();

appBuilder.UseWebApi( config );
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using System.Web.Http;
using D2L.Security.OAuth2.Authorization;

namespace D2L.Security.OAuth2.TestWebService.Controllers {
[DefaultAuthorization]
[AllowFrom( users: true )]
public sealed class AllowFromController : ApiController {
[HttpGet]
[RequireScope("a","b","c")]
[Route("allowfrom/default")]
public void Default() {

}

[HttpGet]
[RequireScope("a","b","c")]
[Route("allowfrom/servicesonly")]
[AllowFrom( users: false, services: true )]
public void ServicesOnly() {

}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,10 @@ private static void RequireScopeSpecification( HttpActionContext context ) {
}

private static void RequirePrincipalTypeSpecification( HttpActionContext context ) {
AuthorizeAttribute allowFromAttribute = context.ActionDescriptor.GetCustomAttributes<AllowFromAttribute>().SingleOrDefault();
var allowFromAttribute = context.ActionDescriptor.GetCustomAttributes<AllowFromAttribute>().SingleOrDefault();
var allowFromAttribute2 = context.ActionDescriptor.ControllerDescriptor.ControllerType.GetCustomAttributes( typeof( AllowFromAttribute ), inherit: false ).SingleOrDefault();

if ( allowFromAttribute == null ) {
if ( allowFromAttribute == null && allowFromAttribute2 == null ) {
throw new Exception( "You must specify the types of callers for this API with [AllowFrom(...)]" );
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
using D2L.Security.OAuth2.Principal;

namespace D2L.Security.OAuth2.Authorization {
[AttributeUsage( AttributeTargets.All, AllowMultiple = false )]
internal sealed class NoRequiredScopeAttribute : AuthorizeAttribute {
[AttributeUsage( AttributeTargets.Method, AllowMultiple = false )]
public sealed class NoRequiredScopeAttribute : AuthorizeAttribute {
// This attribute is only used as a signal in DefaultAuthorizationAttribute
protected override bool IsAuthorized( HttpActionContext context ) {
return true;
Expand Down

0 comments on commit d1cde7e

Please sign in to comment.