Add correct routes and auth to "agents" Functions (#2109)

The routes and auth were missing from the "agents" functions.
This commit is contained in:
George Pollard
2022-07-01 10:05:07 +12:00
committed by GitHub
parent fb0e055ca9
commit a017b10d17
7 changed files with 176 additions and 30 deletions

View File

@ -5,18 +5,24 @@ namespace Microsoft.OneFuzz.Service;
public class AgentCanSchedule { public class AgentCanSchedule {
private readonly ILogTracer _log; private readonly ILogTracer _log;
private readonly IEndpointAuthorization _auth;
private readonly IOnefuzzContext _context; private readonly IOnefuzzContext _context;
public AgentCanSchedule(ILogTracer log, IOnefuzzContext context) { public AgentCanSchedule(ILogTracer log, IEndpointAuthorization auth, IOnefuzzContext context) {
_log = log; _log = log;
_auth = auth;
_context = context; _context = context;
} }
// [Function("AgentCanSchedule")] // [Function("AgentCanSchedule")]
public async Async.Task<HttpResponseData> Run([HttpTrigger] HttpRequestData req) { public Async.Task<HttpResponseData> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "POST", Route="agents/can_schedule")]
HttpRequestData req)
=> _auth.CallIfAgent(req, Post);
private async Async.Task<HttpResponseData> Post(HttpRequestData req) {
var request = await RequestHandling.ParseRequest<CanScheduleRequest>(req); var request = await RequestHandling.ParseRequest<CanScheduleRequest>(req);
if (!request.IsOk || request.OkV == null) { if (!request.IsOk) {
return await _context.RequestHandling.NotOk(req, request.ErrorV, typeof(CanScheduleRequest).ToString()); return await _context.RequestHandling.NotOk(req, request.ErrorV, typeof(CanScheduleRequest).ToString());
} }

View File

@ -5,26 +5,28 @@ namespace Microsoft.OneFuzz.Service;
public class AgentCommands { public class AgentCommands {
private readonly ILogTracer _log; private readonly ILogTracer _log;
private readonly IEndpointAuthorization _auth;
private readonly IOnefuzzContext _context; private readonly IOnefuzzContext _context;
public AgentCommands(ILogTracer log, IOnefuzzContext context) { public AgentCommands(ILogTracer log, IEndpointAuthorization auth, IOnefuzzContext context) {
_log = log; _log = log;
_auth = auth;
_context = context; _context = context;
} }
// [Function("AgentCommands")] // [Function("AgentCommands")]
public async Async.Task<HttpResponseData> Run([HttpTrigger("get", "delete")] HttpRequestData req) { public Async.Task<HttpResponseData> Run(
return req.Method switch { [HttpTrigger(AuthorizationLevel.Anonymous, "GET", "DELETE", Route="agents/commands")]
"GET" => await Get(req), HttpRequestData req)
"DELETE" => await Delete(req), => _auth.CallIfAgent(req, r => r.Method switch {
"GET" => Get(req),
"DELETE" => Delete(req),
_ => throw new NotImplementedException($"HTTP Method {req.Method} is not supported for this method") _ => throw new NotImplementedException($"HTTP Method {req.Method} is not supported for this method")
}; });
}
private async Async.Task<HttpResponseData> Get(HttpRequestData req) { private async Async.Task<HttpResponseData> Get(HttpRequestData req) {
var request = await RequestHandling.ParseRequest<NodeCommandGet>(req); var request = await RequestHandling.ParseRequest<NodeCommandGet>(req);
if (!request.IsOk || request.OkV == null) { if (!request.IsOk) {
return await _context.RequestHandling.NotOk(req, request.ErrorV, typeof(NodeCommandGet).ToString()); return await _context.RequestHandling.NotOk(req, request.ErrorV, typeof(NodeCommandGet).ToString());
} }
var nodeCommand = request.OkV; var nodeCommand = request.OkV;

View File

@ -7,20 +7,26 @@ namespace Microsoft.OneFuzz.Service;
public class AgentEvents { public class AgentEvents {
private readonly ILogTracer _log; private readonly ILogTracer _log;
private readonly IEndpointAuthorization _auth;
private readonly IOnefuzzContext _context; private readonly IOnefuzzContext _context;
public AgentEvents(ILogTracer log, IOnefuzzContext context) { public AgentEvents(ILogTracer log, IEndpointAuthorization auth, IOnefuzzContext context) {
_log = log; _log = log;
_auth = auth;
_context = context; _context = context;
} }
private static readonly EntityConverter _entityConverter = new(); private static readonly EntityConverter _entityConverter = new();
[Function("AgentEvents")] [Function("AgentEvents")]
public async Async.Task<HttpResponseData> Run([HttpTrigger(AuthorizationLevel.Anonymous, "POST")] HttpRequestData req) { public Async.Task<HttpResponseData> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "POST", Route="agents/events")]
HttpRequestData req)
=> _auth.CallIfAgent(req, Post);
private async Async.Task<HttpResponseData> Post(HttpRequestData req) {
var request = await RequestHandling.ParseRequest<NodeStateEnvelope>(req); var request = await RequestHandling.ParseRequest<NodeStateEnvelope>(req);
if (!request.IsOk || request.OkV == null) { if (!request.IsOk) {
return await _context.RequestHandling.NotOk(req, request.ErrorV, context: "node event"); return await _context.RequestHandling.NotOk(req, request.ErrorV, context: "node event");
} }

View File

@ -39,8 +39,8 @@ public class EndpointAuthorization : IEndpointAuthorization {
if (!tokenResult.IsOk) { if (!tokenResult.IsOk) {
return await _context.RequestHandling.NotOk(req, tokenResult.ErrorV, "token verification", HttpStatusCode.Unauthorized); return await _context.RequestHandling.NotOk(req, tokenResult.ErrorV, "token verification", HttpStatusCode.Unauthorized);
} }
var token = tokenResult.OkV!;
var token = tokenResult.OkV;
if (await IsUser(token)) { if (await IsUser(token)) {
if (!allowUser) { if (!allowUser) {
return await Reject(req, token); return await Reject(req, token);

View File

@ -0,0 +1,52 @@
using System.Net;
using IntegrationTests.Fakes;
using Microsoft.OneFuzz.Service;
using Xunit;
using Xunit.Abstractions;
using Async = System.Threading.Tasks;
namespace IntegrationTests;
[Trait("Category", "Live")]
public class AzureStorageAgentCanScheduleTest : AgentCommandsTestsBase {
public AzureStorageAgentCanScheduleTest(ITestOutputHelper output)
: base(output, Integration.AzureStorage.FromEnvironment()) { }
}
public class AzuriteAgentCanScheduleTest : AgentEventsTestsBase {
public AzuriteAgentCanScheduleTest(ITestOutputHelper output)
: base(output, new Integration.AzuriteStorage()) { }
}
public abstract class AgentCanScheduleTestsBase : FunctionTestBase {
public AgentCanScheduleTestsBase(ITestOutputHelper output, IStorage storage)
: base(output, storage) { }
[Fact]
public async Async.Task Authorization_IsRequired() {
var auth = new TestEndpointAuthorization(RequestType.NoAuthorization, Logger, Context);
var func = new AgentCanSchedule(Logger, auth, Context);
var result = await func.Run(TestHttpRequestData.Empty("POST"));
Assert.Equal(HttpStatusCode.Unauthorized, result.StatusCode);
}
[Fact]
public async Async.Task UserAuthorization_IsNotPermitted() {
var auth = new TestEndpointAuthorization(RequestType.User, Logger, Context);
var func = new AgentCanSchedule(Logger, auth, Context);
var result = await func.Run(TestHttpRequestData.Empty("POST"));
Assert.Equal(HttpStatusCode.Unauthorized, result.StatusCode);
}
[Fact]
public async Async.Task AgentAuthorization_IsAccepted() {
var auth = new TestEndpointAuthorization(RequestType.Agent, Logger, Context);
var func = new AgentCanSchedule(Logger, auth, Context);
var result = await func.Run(TestHttpRequestData.Empty("POST"));
Assert.Equal(HttpStatusCode.BadRequest, result.StatusCode); // BadRequest due to no body, not Unauthorized
}
}

View File

@ -0,0 +1,52 @@
using System.Net;
using IntegrationTests.Fakes;
using Microsoft.OneFuzz.Service;
using Xunit;
using Xunit.Abstractions;
using Async = System.Threading.Tasks;
namespace IntegrationTests;
[Trait("Category", "Live")]
public class AzureStorageAgentCommandsTest : AgentCommandsTestsBase {
public AzureStorageAgentCommandsTest(ITestOutputHelper output)
: base(output, Integration.AzureStorage.FromEnvironment()) { }
}
public class AzuriteAgentCommandsTest : AgentEventsTestsBase {
public AzuriteAgentCommandsTest(ITestOutputHelper output)
: base(output, new Integration.AzuriteStorage()) { }
}
public abstract class AgentCommandsTestsBase : FunctionTestBase {
public AgentCommandsTestsBase(ITestOutputHelper output, IStorage storage)
: 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
}
}

View File

@ -32,9 +32,28 @@ public abstract class AgentEventsTestsBase : FunctionTestBase {
readonly Guid poolId = Guid.NewGuid(); readonly Guid poolId = Guid.NewGuid();
readonly string poolVersion = $"version-{Guid.NewGuid()}"; readonly string poolVersion = $"version-{Guid.NewGuid()}";
[Fact]
public async Async.Task Authorization_IsRequired() {
var auth = new TestEndpointAuthorization(RequestType.NoAuthorization, Logger, Context);
var func = new AgentEvents(Logger, auth, Context);
var result = await func.Run(TestHttpRequestData.Empty("POST"));
Assert.Equal(HttpStatusCode.Unauthorized, result.StatusCode);
}
[Fact]
public async Async.Task UserAuthorization_IsNotPermitted() {
var auth = new TestEndpointAuthorization(RequestType.User, Logger, Context);
var func = new AgentEvents(Logger, auth, Context);
var result = await func.Run(TestHttpRequestData.Empty("POST"));
Assert.Equal(HttpStatusCode.Unauthorized, result.StatusCode);
}
[Fact] [Fact]
public async Async.Task WorkerEventMustHaveDoneOrRunningSet() { public async Async.Task WorkerEventMustHaveDoneOrRunningSet() {
var func = new AgentEvents(Logger, Context); var auth = new TestEndpointAuthorization(RequestType.Agent, Logger, Context);
var func = new AgentEvents(Logger, auth, Context);
var data = new NodeStateEnvelope( var data = new NodeStateEnvelope(
MachineId: Guid.NewGuid(), MachineId: Guid.NewGuid(),
@ -53,7 +72,8 @@ public abstract class AgentEventsTestsBase : FunctionTestBase {
new Task(jobId, taskId, TaskState.Running, Os.Linux, new Task(jobId, taskId, TaskState.Running, Os.Linux,
new TaskConfig(jobId, null, new TaskDetails(TaskType.Coverage, 100)))); new TaskConfig(jobId, null, new TaskDetails(TaskType.Coverage, 100))));
var func = new AgentEvents(Logger, Context); var auth = new TestEndpointAuthorization(RequestType.Agent, Logger, Context);
var func = new AgentEvents(Logger, auth, Context);
var data = new NodeStateEnvelope( var data = new NodeStateEnvelope(
MachineId: machineId, MachineId: machineId,
@ -80,8 +100,8 @@ public abstract class AgentEventsTestsBase : FunctionTestBase {
new Task(jobId, taskId, TaskState.Running, Os.Linux, new Task(jobId, taskId, TaskState.Running, Os.Linux,
new TaskConfig(jobId, null, new TaskDetails(TaskType.Coverage, 100)))); new TaskConfig(jobId, null, new TaskDetails(TaskType.Coverage, 100))));
var auth = new TestEndpointAuthorization(RequestType.Agent, Logger, Context);
var func = new AgentEvents(Logger, Context); var func = new AgentEvents(Logger, auth, Context);
var data = new NodeStateEnvelope( var data = new NodeStateEnvelope(
MachineId: machineId, MachineId: machineId,
@ -107,7 +127,8 @@ public abstract class AgentEventsTestsBase : FunctionTestBase {
new Task(jobId, taskId, TaskState.Scheduled, Os.Linux, new Task(jobId, taskId, TaskState.Scheduled, Os.Linux,
new TaskConfig(jobId, null, new TaskDetails(TaskType.Coverage, 100)))); new TaskConfig(jobId, null, new TaskDetails(TaskType.Coverage, 100))));
var func = new AgentEvents(Logger, Context); var auth = new TestEndpointAuthorization(RequestType.Agent, Logger, Context);
var func = new AgentEvents(Logger, auth, Context);
var data = new NodeStateEnvelope( var data = new NodeStateEnvelope(
MachineId: machineId, MachineId: machineId,
@ -132,7 +153,8 @@ public abstract class AgentEventsTestsBase : FunctionTestBase {
await Context.InsertAll( await Context.InsertAll(
new Node(poolName, machineId, poolId, poolVersion)); new Node(poolName, machineId, poolId, poolVersion));
var func = new AgentEvents(Logger, Context); var auth = new TestEndpointAuthorization(RequestType.Agent, Logger, Context);
var func = new AgentEvents(Logger, auth, Context);
var data = new NodeStateEnvelope( var data = new NodeStateEnvelope(
MachineId: machineId, MachineId: machineId,
Event: new WorkerEvent(Running: new WorkerRunningEvent(taskId))); Event: new WorkerEvent(Running: new WorkerRunningEvent(taskId)));
@ -148,7 +170,8 @@ public abstract class AgentEventsTestsBase : FunctionTestBase {
new Task(jobId, taskId, TaskState.Running, Os.Linux, new Task(jobId, taskId, TaskState.Running, Os.Linux,
new TaskConfig(jobId, null, new TaskDetails(TaskType.Coverage, 0)))); new TaskConfig(jobId, null, new TaskDetails(TaskType.Coverage, 0))));
var func = new AgentEvents(Logger, Context); var auth = new TestEndpointAuthorization(RequestType.Agent, Logger, Context);
var func = new AgentEvents(Logger, auth, Context);
var data = new NodeStateEnvelope( var data = new NodeStateEnvelope(
MachineId: machineId, MachineId: machineId,
Event: new WorkerEvent(Running: new WorkerRunningEvent(taskId))); Event: new WorkerEvent(Running: new WorkerRunningEvent(taskId)));
@ -165,7 +188,8 @@ public abstract class AgentEventsTestsBase : FunctionTestBase {
new Task(jobId, taskId, TaskState.Running, Os.Linux, new Task(jobId, taskId, TaskState.Running, Os.Linux,
new TaskConfig(jobId, null, new TaskDetails(TaskType.Coverage, 0)))); new TaskConfig(jobId, null, new TaskDetails(TaskType.Coverage, 0))));
var func = new AgentEvents(Logger, Context); var auth = new TestEndpointAuthorization(RequestType.Agent, Logger, Context);
var func = new AgentEvents(Logger, auth, Context);
var data = new NodeStateEnvelope( var data = new NodeStateEnvelope(
MachineId: machineId, MachineId: machineId,
Event: new WorkerEvent(Running: new WorkerRunningEvent(taskId))); Event: new WorkerEvent(Running: new WorkerRunningEvent(taskId)));
@ -205,7 +229,8 @@ public abstract class AgentEventsTestsBase : FunctionTestBase {
public async Async.Task NodeStateUpdate_ForMissingNode_IgnoresEvent() { public async Async.Task NodeStateUpdate_ForMissingNode_IgnoresEvent() {
// nothing present in storage // nothing present in storage
var func = new AgentEvents(Logger, Context); var auth = new TestEndpointAuthorization(RequestType.Agent, Logger, Context);
var func = new AgentEvents(Logger, auth, Context);
var data = new NodeStateEnvelope( var data = new NodeStateEnvelope(
MachineId: machineId, MachineId: machineId,
Event: new NodeStateUpdate(NodeState.Init)); Event: new NodeStateUpdate(NodeState.Init));
@ -220,7 +245,8 @@ public abstract class AgentEventsTestsBase : FunctionTestBase {
await Context.InsertAll( await Context.InsertAll(
new Node(poolName, machineId, poolId, poolVersion, State: NodeState.Init)); new Node(poolName, machineId, poolId, poolVersion, State: NodeState.Init));
var func = new AgentEvents(Logger, Context); var auth = new TestEndpointAuthorization(RequestType.Agent, Logger, Context);
var func = new AgentEvents(Logger, auth, Context);
var data = new NodeStateEnvelope( var data = new NodeStateEnvelope(
MachineId: machineId, MachineId: machineId,
Event: new NodeStateUpdate(NodeState.Ready)); Event: new NodeStateUpdate(NodeState.Ready));
@ -237,7 +263,8 @@ public abstract class AgentEventsTestsBase : FunctionTestBase {
await Context.InsertAll( await Context.InsertAll(
new Node(poolName, machineId, poolId, poolVersion, ReimageRequested: true)); new Node(poolName, machineId, poolId, poolVersion, ReimageRequested: true));
var func = new AgentEvents(Logger, Context); var auth = new TestEndpointAuthorization(RequestType.Agent, Logger, Context);
var func = new AgentEvents(Logger, auth, Context);
var data = new NodeStateEnvelope( var data = new NodeStateEnvelope(
MachineId: machineId, MachineId: machineId,
Event: new NodeStateUpdate(NodeState.Free)); Event: new NodeStateUpdate(NodeState.Free));
@ -265,7 +292,8 @@ public abstract class AgentEventsTestsBase : FunctionTestBase {
await Context.InsertAll( await Context.InsertAll(
new Node(poolName, machineId, poolId, poolVersion, DeleteRequested: true)); new Node(poolName, machineId, poolId, poolVersion, DeleteRequested: true));
var func = new AgentEvents(Logger, Context); var auth = new TestEndpointAuthorization(RequestType.Agent, Logger, Context);
var func = new AgentEvents(Logger, auth, Context);
var data = new NodeStateEnvelope( var data = new NodeStateEnvelope(
MachineId: machineId, MachineId: machineId,
Event: new NodeStateUpdate(NodeState.Free)); Event: new NodeStateUpdate(NodeState.Free));