mirror of
https://github.com/microsoft/onefuzz.git
synced 2025-06-19 13:03:44 +00:00
Add correct routes and auth to "agents" Functions (#2109)
The routes and auth were missing from the "agents" functions.
This commit is contained in:
@ -5,18 +5,24 @@ namespace Microsoft.OneFuzz.Service;
|
||||
|
||||
public class AgentCanSchedule {
|
||||
private readonly ILogTracer _log;
|
||||
|
||||
private readonly IEndpointAuthorization _auth;
|
||||
private readonly IOnefuzzContext _context;
|
||||
|
||||
public AgentCanSchedule(ILogTracer log, IOnefuzzContext context) {
|
||||
public AgentCanSchedule(ILogTracer log, IEndpointAuthorization auth, IOnefuzzContext context) {
|
||||
_log = log;
|
||||
_auth = auth;
|
||||
_context = context;
|
||||
}
|
||||
|
||||
// [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);
|
||||
if (!request.IsOk || request.OkV == null) {
|
||||
if (!request.IsOk) {
|
||||
return await _context.RequestHandling.NotOk(req, request.ErrorV, typeof(CanScheduleRequest).ToString());
|
||||
}
|
||||
|
||||
|
@ -5,26 +5,28 @@ namespace Microsoft.OneFuzz.Service;
|
||||
|
||||
public class AgentCommands {
|
||||
private readonly ILogTracer _log;
|
||||
|
||||
private readonly IEndpointAuthorization _auth;
|
||||
private readonly IOnefuzzContext _context;
|
||||
|
||||
public AgentCommands(ILogTracer log, IOnefuzzContext context) {
|
||||
public AgentCommands(ILogTracer log, IEndpointAuthorization auth, IOnefuzzContext context) {
|
||||
_log = log;
|
||||
_auth = auth;
|
||||
_context = context;
|
||||
}
|
||||
|
||||
// [Function("AgentCommands")]
|
||||
public async Async.Task<HttpResponseData> Run([HttpTrigger("get", "delete")] HttpRequestData req) {
|
||||
return req.Method switch {
|
||||
"GET" => await Get(req),
|
||||
"DELETE" => await Delete(req),
|
||||
public Async.Task<HttpResponseData> Run(
|
||||
[HttpTrigger(AuthorizationLevel.Anonymous, "GET", "DELETE", Route="agents/commands")]
|
||||
HttpRequestData 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")
|
||||
};
|
||||
}
|
||||
});
|
||||
|
||||
private async Async.Task<HttpResponseData> Get(HttpRequestData 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());
|
||||
}
|
||||
var nodeCommand = request.OkV;
|
||||
|
@ -7,20 +7,26 @@ namespace Microsoft.OneFuzz.Service;
|
||||
|
||||
public class AgentEvents {
|
||||
private readonly ILogTracer _log;
|
||||
|
||||
private readonly IEndpointAuthorization _auth;
|
||||
private readonly IOnefuzzContext _context;
|
||||
|
||||
public AgentEvents(ILogTracer log, IOnefuzzContext context) {
|
||||
public AgentEvents(ILogTracer log, IEndpointAuthorization auth, IOnefuzzContext context) {
|
||||
_log = log;
|
||||
_auth = auth;
|
||||
_context = context;
|
||||
}
|
||||
|
||||
private static readonly EntityConverter _entityConverter = new();
|
||||
|
||||
[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);
|
||||
if (!request.IsOk || request.OkV == null) {
|
||||
if (!request.IsOk) {
|
||||
return await _context.RequestHandling.NotOk(req, request.ErrorV, context: "node event");
|
||||
}
|
||||
|
||||
|
@ -39,8 +39,8 @@ public class EndpointAuthorization : IEndpointAuthorization {
|
||||
if (!tokenResult.IsOk) {
|
||||
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 (!allowUser) {
|
||||
return await Reject(req, token);
|
||||
|
52
src/ApiService/IntegrationTests/AgentCanScheduleTests.cs
Normal file
52
src/ApiService/IntegrationTests/AgentCanScheduleTests.cs
Normal 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
|
||||
}
|
||||
}
|
52
src/ApiService/IntegrationTests/AgentCommandsTests.cs
Normal file
52
src/ApiService/IntegrationTests/AgentCommandsTests.cs
Normal 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
|
||||
}
|
||||
}
|
@ -32,9 +32,28 @@ public abstract class AgentEventsTestsBase : FunctionTestBase {
|
||||
readonly Guid poolId = 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]
|
||||
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(
|
||||
MachineId: Guid.NewGuid(),
|
||||
@ -53,7 +72,8 @@ public abstract class AgentEventsTestsBase : FunctionTestBase {
|
||||
new Task(jobId, taskId, TaskState.Running, Os.Linux,
|
||||
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(
|
||||
MachineId: machineId,
|
||||
@ -80,8 +100,8 @@ public abstract class AgentEventsTestsBase : FunctionTestBase {
|
||||
new Task(jobId, taskId, TaskState.Running, Os.Linux,
|
||||
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(
|
||||
MachineId: machineId,
|
||||
@ -107,7 +127,8 @@ public abstract class AgentEventsTestsBase : FunctionTestBase {
|
||||
new Task(jobId, taskId, TaskState.Scheduled, Os.Linux,
|
||||
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(
|
||||
MachineId: machineId,
|
||||
@ -132,7 +153,8 @@ public abstract class AgentEventsTestsBase : FunctionTestBase {
|
||||
await Context.InsertAll(
|
||||
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(
|
||||
MachineId: machineId,
|
||||
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 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(
|
||||
MachineId: machineId,
|
||||
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 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(
|
||||
MachineId: machineId,
|
||||
Event: new WorkerEvent(Running: new WorkerRunningEvent(taskId)));
|
||||
@ -205,7 +229,8 @@ public abstract class AgentEventsTestsBase : FunctionTestBase {
|
||||
public async Async.Task NodeStateUpdate_ForMissingNode_IgnoresEvent() {
|
||||
// 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(
|
||||
MachineId: machineId,
|
||||
Event: new NodeStateUpdate(NodeState.Init));
|
||||
@ -220,7 +245,8 @@ public abstract class AgentEventsTestsBase : FunctionTestBase {
|
||||
await Context.InsertAll(
|
||||
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(
|
||||
MachineId: machineId,
|
||||
Event: new NodeStateUpdate(NodeState.Ready));
|
||||
@ -237,7 +263,8 @@ public abstract class AgentEventsTestsBase : FunctionTestBase {
|
||||
await Context.InsertAll(
|
||||
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(
|
||||
MachineId: machineId,
|
||||
Event: new NodeStateUpdate(NodeState.Free));
|
||||
@ -265,7 +292,8 @@ public abstract class AgentEventsTestsBase : FunctionTestBase {
|
||||
await Context.InsertAll(
|
||||
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(
|
||||
MachineId: machineId,
|
||||
Event: new NodeStateUpdate(NodeState.Free));
|
||||
|
Reference in New Issue
Block a user