Move auth into middleware (#3133)

Closes #2098.

This cleans up the authentication a bit; after this change we have two stages in the middleware pipeline:

- `AuthenticationMiddleware` reads the JWT token (it does not validate it, this is done by the Azure Functions service) and stores it in `FunctionContext.Items["ONEFUZZ_USER_INFO"]`
- `AuthorizationMiddleware` checks the user info against the `[Authorize]` attribute to see if the user has the required permissions
- Functions can read the user info from the `FunctionContext` if needed

The authorize attribute can be `[Authorize(Allow.User)]` or `Allow.Agent` or `Allow.Admin`. The `Admin` case is new and allows this to be declaratively specified rather than being checked in code. We have several functions which could be changed to use this (e.g. Pool POST/DELETE/PATCH, Scaleset POST/DELETE/PATCH), but I have only changed one so far (JinjaToScriban).

One of the benefits here is that this simplifies the test code a lot: we can set the desired user info directly onto our `(Test)FunctionContext` rather than having to supply a fake that pretends to parse the token from the HTTP request. This will also have benefits when running the service locally for testing purposes (refer to internal issue).

The other benefit is the ability to programmatically read the required authentication for each function, which may help with Swagger generation.
This commit is contained in:
George Pollard
2023-06-07 13:57:22 +12:00
committed by GitHub
parent b44cff5236
commit e448947abe
62 changed files with 881 additions and 1284 deletions

View File

@ -26,33 +26,6 @@ public abstract class AgentCommandsTestsBase : FunctionTestBase {
: base(output, storage) { }
[Fact]
public async Async.Task Authorization_IsRequired() {
var auth = new TestEndpointAuthorization(RequestType.NoAuthorization, Logger, Context);
var func = new AgentCommands(Logger, auth, Context);
var result = await func.Run(TestHttpRequestData.Empty("GET"));
Assert.Equal(HttpStatusCode.Unauthorized, result.StatusCode);
}
[Fact]
public async Async.Task UserAuthorization_IsNotPermitted() {
var auth = new TestEndpointAuthorization(RequestType.User, Logger, Context);
var func = new AgentCommands(Logger, auth, Context);
var result = await func.Run(TestHttpRequestData.Empty("GET"));
Assert.Equal(HttpStatusCode.Unauthorized, result.StatusCode);
}
[Fact]
public async Async.Task AgentAuthorization_IsAccepted() {
var auth = new TestEndpointAuthorization(RequestType.Agent, Logger, Context);
var func = new AgentCommands(Logger, auth, Context);
var result = await func.Run(TestHttpRequestData.Empty("GET"));
Assert.Equal(HttpStatusCode.BadRequest, result.StatusCode); // BadRequest due to no body, not Unauthorized
}
[Fact]
public async Async.Task AgentCommand_GetsCommand() {
var machineId = Guid.NewGuid();
@ -69,8 +42,7 @@ public abstract class AgentCommandsTestsBase : FunctionTestBase {
});
var commandRequest = new NodeCommandGet(machineId);
var auth = new TestEndpointAuthorization(RequestType.Agent, Logger, Context);
var func = new AgentCommands(Logger, auth, Context);
var func = new AgentCommands(Logger, Context);
var result = await func.Run(TestHttpRequestData.FromJson("GET", commandRequest));
Assert.Equal(HttpStatusCode.OK, result.StatusCode);