From a7331cfa128c00a9235b90a6ed363534f2f74e50 Mon Sep 17 00:00:00 2001 From: Teo Voinea Date: Mon, 9 May 2022 13:00:11 +0000 Subject: [PATCH 1/5] Checkpoint --- src/ApiService/ApiService/AgentCanSchedule.cs | 36 +++----- src/ApiService/ApiService/AgentCommands.cs | 58 ++++++++++++ .../ApiService/OneFuzzTypes/Model.cs | 5 + .../ApiService/OneFuzzTypes/Requests.cs | 9 ++ .../ApiService/OneFuzzTypes/Responses.cs | 8 ++ src/ApiService/ApiService/Program.cs | 1 + src/ApiService/ApiService/UserCredentials.cs | 8 +- src/ApiService/ApiService/onefuzzlib/Creds.cs | 17 ++++ .../onefuzzlib/EndpointAuthorization.cs | 91 +++++++++++++++++++ .../ApiService/onefuzzlib/OnefuzzContext.cs | 4 + .../ApiService/onefuzzlib/PoolOperations.cs | 5 + .../ApiService/onefuzzlib/Request.cs | 17 +++- .../onefuzzlib/ScalesetOperations.cs | 5 + 13 files changed, 232 insertions(+), 32 deletions(-) create mode 100644 src/ApiService/ApiService/AgentCommands.cs create mode 100644 src/ApiService/ApiService/onefuzzlib/EndpointAuthorization.cs diff --git a/src/ApiService/ApiService/AgentCanSchedule.cs b/src/ApiService/ApiService/AgentCanSchedule.cs index 7d57e5eb4c..001221f9c0 100644 --- a/src/ApiService/ApiService/AgentCanSchedule.cs +++ b/src/ApiService/ApiService/AgentCanSchedule.cs @@ -6,34 +6,25 @@ namespace Microsoft.OneFuzz.Service; public class AgentCanSchedule { private readonly ILogTracer _log; - private readonly IStorage _storage; + private readonly IOnefuzzContext _context; - private readonly INodeOperations _nodeOperations; - - private readonly ITaskOperations _taskOperations; - - private readonly IScalesetOperations _scalesetOperations; - - public AgentCanSchedule(ILogTracer log, IStorage storage, INodeOperations nodeOperations, ITaskOperations taskOperations, IScalesetOperations scalesetOperations) { + public AgentCanSchedule(ILogTracer log, IOnefuzzContext context) { _log = log; - _storage = storage; - _nodeOperations = nodeOperations; - _taskOperations = taskOperations; - _scalesetOperations = scalesetOperations; + _context = context; } [Function("AgentCanSchedule")] public async Async.Task Run([HttpTrigger] HttpRequestData req) { var request = await RequestHandling.ParseRequest(req); if (!request.IsOk || request.OkV == null) { - return await RequestHandling.NotOk(req, request.ErrorV, typeof(CanScheduleRequest).ToString(), _log); + return await _context.RequestHandling.NotOk(req, request.ErrorV, typeof(CanScheduleRequest).ToString()); } var canScheduleRequest = request.OkV; - var node = await _nodeOperations.GetByMachineId(canScheduleRequest.MachineId); + var node = await _context.NodeOperations.GetByMachineId(canScheduleRequest.MachineId); if (node == null) { - return await RequestHandling.NotOk( + return await _context.RequestHandling.NotOk( req, new Error( ErrorCode.UNABLE_TO_FIND, @@ -41,29 +32,24 @@ public async Async.Task Run([HttpTrigger] HttpRequestData req) "unable to find node" } ), - canScheduleRequest.MachineId.ToString(), - _log + canScheduleRequest.MachineId.ToString() ); } var allowed = true; var workStopped = false; - if (!await _nodeOperations.CanProcessNewWork(node)) { + if (!await _context.NodeOperations.CanProcessNewWork(node)) { allowed = false; } - var task = await _taskOperations.GetByTaskId(canScheduleRequest.TaskId); + var task = await _context.TaskOperations.GetByTaskId(canScheduleRequest.TaskId); workStopped = task == null || TaskStateHelper.ShuttingDown.Contains(task.State); if (allowed) { - allowed = (await _nodeOperations.AcquireScaleInProtection(node)).IsOk; + allowed = (await _context.NodeOperations.AcquireScaleInProtection(node)).IsOk; } - return await RequestHandling.Ok( - req, - new BaseResponse[] { - new CanSchedule(allowed, workStopped) - }); + return await RequestHandling.Ok(req, new CanSchedule(allowed, workStopped)); } } diff --git a/src/ApiService/ApiService/AgentCommands.cs b/src/ApiService/ApiService/AgentCommands.cs new file mode 100644 index 0000000000..3ba0021240 --- /dev/null +++ b/src/ApiService/ApiService/AgentCommands.cs @@ -0,0 +1,58 @@ +using Microsoft.Azure.Functions.Worker; +using Microsoft.Azure.Functions.Worker.Http; + +namespace Microsoft.OneFuzz.Service; + +public class AgentCommands { + private readonly ILogTracer _log; + + private readonly IOnefuzzContext _context; + + public AgentCommands(ILogTracer log, IOnefuzzContext context) { + _log = log; + _context = context; + } + + [Function("AgentCommands")] + public async Async.Task Run([HttpTrigger("get", "delete")] HttpRequestData req) { + return req.Method switch { + "get" => await Get(req), + "delete" => await Delete(req), + _ => throw new NotImplementedException($"HTTP Method {req.Method} is not supported for this method") + }; + } + + private async Async.Task Get(HttpRequestData req) { + var request = await RequestHandling.ParseRequest(req); + if (!request.IsOk || request.OkV == null) { + return await _context.RequestHandling.NotOk(req, request.ErrorV, typeof(NodeCommandGet).ToString()); + } + var nodeCommand = request.OkV; + + var message = await _context.NodeMessageOperations.GetMessage(nodeCommand.MachineId).FirstAsync(); + + if (message != null) { + var command = message.Message; + var messageId = message.MessageId; + var envelope = new NodeCommandEvenlope(command, messageId); + return await RequestHandling.Ok(req, new PendingNodeCommand(envelope)); + } else { + return await RequestHandling.Ok(req, new PendingNodeCommand(null)); + } + } + + private async Async.Task Delete(HttpRequestData req) { + var request = await RequestHandling.ParseRequest(req); + if (!request.IsOk || request.OkV == null) { + return await _context.RequestHandling.NotOk(req, request.ErrorV, typeof(NodeCommandDelete).ToString()); + } + var nodeCommand = request.OkV; + + var message = await _context.NodeMessageOperations.GetEntityAsync(nodeCommand.MachineId.ToString(), nodeCommand.MessageId); + if (message != null) { + await _context.NodeMessageOperations.Delete(message); + } + + return await RequestHandling.Ok(req, new BoolResult(true)); + } +} diff --git a/src/ApiService/ApiService/OneFuzzTypes/Model.cs b/src/ApiService/ApiService/OneFuzzTypes/Model.cs index 6b08b3a373..b03a60fa90 100644 --- a/src/ApiService/ApiService/OneFuzzTypes/Model.cs +++ b/src/ApiService/ApiService/OneFuzzTypes/Model.cs @@ -695,3 +695,8 @@ Uri HeartbeatQueue public IContainerDef? RegressionReport { get; set; } } + +public record NodeCommandEvenlope( + NodeCommand Command, + string MessageId +); diff --git a/src/ApiService/ApiService/OneFuzzTypes/Requests.cs b/src/ApiService/ApiService/OneFuzzTypes/Requests.cs index f03defea88..3b88a21b03 100644 --- a/src/ApiService/ApiService/OneFuzzTypes/Requests.cs +++ b/src/ApiService/ApiService/OneFuzzTypes/Requests.cs @@ -6,3 +6,12 @@ public record CanScheduleRequest( Guid MachineId, Guid TaskId ) : BaseRequest; + +public record NodeCommandGet( + Guid MachineId +) : BaseRequest; + +public record NodeCommandDelete( + Guid MachineId, + string MessageId +) : BaseRequest; diff --git a/src/ApiService/ApiService/OneFuzzTypes/Responses.cs b/src/ApiService/ApiService/OneFuzzTypes/Responses.cs index c1ec1f492f..d0d202183f 100644 --- a/src/ApiService/ApiService/OneFuzzTypes/Responses.cs +++ b/src/ApiService/ApiService/OneFuzzTypes/Responses.cs @@ -6,3 +6,11 @@ public record CanSchedule( bool Allowed, bool WorkStopped ) : BaseResponse; + +public record PendingNodeCommand( + NodeCommandEvenlope? Enveleope +) : BaseResponse; + +public record BoolResult( + bool Result +) : BaseResponse; diff --git a/src/ApiService/ApiService/Program.cs b/src/ApiService/ApiService/Program.cs index 806bc2bb57..08665aeb9a 100644 --- a/src/ApiService/ApiService/Program.cs +++ b/src/ApiService/ApiService/Program.cs @@ -103,6 +103,7 @@ public static void Main() { .AddScoped() .AddScoped() .AddScoped() + .AddScoped() .AddScoped() .AddSingleton() diff --git a/src/ApiService/ApiService/UserCredentials.cs b/src/ApiService/ApiService/UserCredentials.cs index ec79e205fb..d1e7aa8d1a 100644 --- a/src/ApiService/ApiService/UserCredentials.cs +++ b/src/ApiService/ApiService/UserCredentials.cs @@ -9,7 +9,7 @@ namespace Microsoft.OneFuzz.Service; public interface IUserCredentials { public string? GetBearerToken(HttpRequestData req); public string? GetAuthToken(HttpRequestData req); - public Task> ParseJwtToken(LogTracer log, HttpRequestData req); + public Task> ParseJwtToken(HttpRequestData req); } public class UserCredentials : IUserCredentials { @@ -58,7 +58,7 @@ from t in r.AllowedAadTenants return OneFuzzResult.Ok(allowedAddTenantsQuery.ToArray()); } - public async Task> ParseJwtToken(LogTracer log, HttpRequestData req) { + public async Task> ParseJwtToken(HttpRequestData req) { var authToken = GetAuthToken(req); if (authToken is null) { return OneFuzzResult.Error(ErrorCode.INVALID_REQUEST, new[] { "unable to find authorization token" }); @@ -84,11 +84,11 @@ from t in token.Claims return OneFuzzResult.Ok(new(applicationId, objectId, upn)); } else { - log.Error($"issuer not from allowed tenant: {token.Issuer} - {allowedTenants}"); + _log.Error($"issuer not from allowed tenant: {token.Issuer} - {allowedTenants}"); return OneFuzzResult.Error(ErrorCode.INVALID_REQUEST, new[] { "unauthorized AAD issuer" }); } } else { - log.Error("Failed to get allowed tenants"); + _log.Error("Failed to get allowed tenants"); return OneFuzzResult.Error(allowedTenants.ErrorV); } } diff --git a/src/ApiService/ApiService/onefuzzlib/Creds.cs b/src/ApiService/ApiService/onefuzzlib/Creds.cs index 3eae735e14..bb41d9eb86 100644 --- a/src/ApiService/ApiService/onefuzzlib/Creds.cs +++ b/src/ApiService/ApiService/onefuzzlib/Creds.cs @@ -2,6 +2,7 @@ using Azure.Identity; using Azure.ResourceManager; using Azure.ResourceManager.Resources; +using System.Text.Json; namespace Microsoft.OneFuzz.Service; @@ -23,6 +24,7 @@ public interface ICreds { public Async.Task GetBaseRegion(); public Uri GetInstanceUrl(); + Guid GetScalesetPrincipalId(); } public class Creds : ICreds { @@ -85,4 +87,19 @@ public async Async.Task GetBaseRegion() { public Uri GetInstanceUrl() { return new Uri($"https://{GetInstanceName()}.azurewebsites.net"); } + + public Guid GetScalesetPrincipalId() { + var uid = ArmClient.GetGenericResource( + new ResourceIdentifier(GetScalesetIdentityResourcePath()) + ); + var principalId = JsonSerializer.Deserialize(uid.Data.Properties.ToString())?.RootElement.GetProperty("principalId").GetString(); + return new Guid(principalId); + } + + public string GetScalesetIdentityResourcePath() { + var scalesetIdName = $"{GetInstanceName()}-scalesetid"; + var resourceGroupPath = $"/subscriptions/{GetSubscription()}/resourceGroups/{GetBaseResourceGroup()}/providers"; + + return $"{resourceGroupPath}/Microsoft.ManagedIdentity/userAssignedIdentities/{scalesetIdName}"; + } } diff --git a/src/ApiService/ApiService/onefuzzlib/EndpointAuthorization.cs b/src/ApiService/ApiService/onefuzzlib/EndpointAuthorization.cs new file mode 100644 index 0000000000..c8bb563b7c --- /dev/null +++ b/src/ApiService/ApiService/onefuzzlib/EndpointAuthorization.cs @@ -0,0 +1,91 @@ +using System.Net; +using Microsoft.Azure.Functions.Worker.Http; + +namespace Microsoft.OneFuzz.Service; + +public class EndpointAuthorization { + private readonly IOnefuzzContext _context; + private readonly ILogTracer _log; + + public EndpointAuthorization(IOnefuzzContext context, ILogTracer log) { + _context = context; + _log = log; + } + public async Async.Task CallIfAgent(HttpRequestData req, Func> method) { + return await CallIf(req, method, allowAgent: true); + } + + public async Async.Task CallIf(HttpRequestData req, Func> method, bool allowUser = false, bool allowAgent = false) { + var tokenResult = await _context.UserCredentials.ParseJwtToken(req); + + if (!tokenResult.IsOk) { + return await _context.RequestHandling.NotOk(req, tokenResult.ErrorV, "token verification", HttpStatusCode.Unauthorized); + } + var token = tokenResult.OkV!; + + if (await IsUser(token)) { + if (!allowUser) { + return await Reject(req, token); + } + + var access = CheckAccess(req); + if (!access.IsOk) { + return await _context.RequestHandling.NotOk(req, access.ErrorV, "access control", HttpStatusCode.Unauthorized); + } + } + + + if (await IsAgent(token) && !allowAgent) { + return await Reject(req, token); + } + + return await method(req); + } + + public async Async.Task IsUser(UserInfo tokenData) { + return !await IsAgent(tokenData); + } + + public async Async.Task Reject(HttpRequestData req, UserInfo token) { + _log.Error( + $"reject token. url:{req.Url} token:{token} body:{await req.ReadAsStringAsync()}" + ); + + return await _context.RequestHandling.NotOk( + req, + new Error( + ErrorCode.UNAUTHORIZED, + new string[] { "Unrecognized agent" } + ), + "token verification", + HttpStatusCode.Unauthorized + ); + } + + public OneFuzzResultVoid CheckAccess(HttpRequestData req) { + throw new NotImplementedException(); + } + + public async Async.Task IsAgent(UserInfo tokenData) { + if (tokenData.ObjectId != null) { + var scalesets = _context.ScalesetOperations.GetByObjectId(tokenData.ObjectId.Value); + if (await scalesets.AnyAsync()) { + return true; + } + + var principalId = _context.Creds.GetScalesetPrincipalId(); + return principalId == tokenData.ObjectId; + } + + if (!tokenData.ApplicationId.HasValue) { + return false; + } + + var pools = _context.PoolOperations.GetByClientId(tokenData.ApplicationId.Value); + if (await pools.AnyAsync()) { + return true; + } + + return false; + } +} diff --git a/src/ApiService/ApiService/onefuzzlib/OnefuzzContext.cs b/src/ApiService/ApiService/onefuzzlib/OnefuzzContext.cs index a2f2caf159..9c7f681628 100644 --- a/src/ApiService/ApiService/onefuzzlib/OnefuzzContext.cs +++ b/src/ApiService/ApiService/onefuzzlib/OnefuzzContext.cs @@ -34,6 +34,8 @@ public interface IOnefuzzContext { IVmssOperations VmssOperations { get; } IWebhookMessageLogOperations WebhookMessageLogOperations { get; } IWebhookOperations WebhookOperations { get; } + + IRequestHandling RequestHandling { get; } } public class OnefuzzContext : IOnefuzzContext { @@ -71,6 +73,8 @@ public class OnefuzzContext : IOnefuzzContext { public ICreds Creds { get => _serviceProvider.GetService() ?? throw new Exception("No ICreds service"); } public IServiceConfig ServiceConfiguration { get => _serviceProvider.GetService() ?? throw new Exception("No IServiceConfiguration service"); } + public IRequestHandling RequestHandling { get => _serviceProvider.GetService() ?? throw new Exception("No IRequestHandling service"); } + public OnefuzzContext(IServiceProvider serviceProvider) { _serviceProvider = serviceProvider; } diff --git a/src/ApiService/ApiService/onefuzzlib/PoolOperations.cs b/src/ApiService/ApiService/onefuzzlib/PoolOperations.cs index a8089b7733..3e08b48769 100644 --- a/src/ApiService/ApiService/onefuzzlib/PoolOperations.cs +++ b/src/ApiService/ApiService/onefuzzlib/PoolOperations.cs @@ -6,6 +6,7 @@ namespace Microsoft.OneFuzz.Service; public interface IPoolOperations { public Async.Task> GetByName(string poolName); Task ScheduleWorkset(Pool pool, WorkSet workSet); + IAsyncEnumerable GetByClientId(Guid clientId); } public class PoolOperations : StatefulOrm, IPoolOperations { @@ -37,6 +38,10 @@ public async Task ScheduleWorkset(Pool pool, WorkSet workSet) { return await _context.Queue.QueueObject(GetPoolQueue(pool), workSet, StorageType.Corpus); } + public IAsyncEnumerable GetByClientId(Guid clientId) { + return QueryAsync(filter: $"client_id eq '{clientId.ToString()}'"); + } + private string GetPoolQueue(Pool pool) { return $"pool-{pool.PoolId.ToString("N")}"; } diff --git a/src/ApiService/ApiService/onefuzzlib/Request.cs b/src/ApiService/ApiService/onefuzzlib/Request.cs index 608e17c213..62877c6992 100644 --- a/src/ApiService/ApiService/onefuzzlib/Request.cs +++ b/src/ApiService/ApiService/onefuzzlib/Request.cs @@ -4,11 +4,19 @@ namespace Microsoft.OneFuzz.Service; -public class RequestHandling { - public static async Async.Task NotOk(HttpRequestData request, Error error, string context, ILogTracer log, HttpStatusCode statusCode = HttpStatusCode.BadRequest) { +public interface IRequestHandling { + Async.Task NotOk(HttpRequestData request, Error error, string context, HttpStatusCode statusCode = HttpStatusCode.BadRequest); +} + +public class RequestHandling : IRequestHandling { + private readonly ILogTracer _log; + public RequestHandling(ILogTracer log) { + _log = log; + } + public async Async.Task NotOk(HttpRequestData request, Error error, string context, HttpStatusCode statusCode = HttpStatusCode.BadRequest) { var statusNum = (int)statusCode; if (statusNum >= 400 && statusNum <= 599) { - log.Error($"request error - {context}: {error}"); + _log.Error($"request error - {context}: {error}"); var response = HttpResponseData.CreateResponse(request); await response.WriteAsJsonAsync(error); @@ -65,5 +73,8 @@ public async static Async.Task Ok(HttpRequestData req, IEnumer return resp; } + public async static Async.Task Ok(HttpRequestData req, BaseResponse response) { + return await Ok(req, new BaseResponse[] { response }); + } } diff --git a/src/ApiService/ApiService/onefuzzlib/ScalesetOperations.cs b/src/ApiService/ApiService/onefuzzlib/ScalesetOperations.cs index 535fa9bdc5..5626179c4d 100644 --- a/src/ApiService/ApiService/onefuzzlib/ScalesetOperations.cs +++ b/src/ApiService/ApiService/onefuzzlib/ScalesetOperations.cs @@ -11,6 +11,7 @@ public interface IScalesetOperations : IOrm { public Async.Task UpdateConfigs(Scaleset scaleSet); public Async.Task> GetById(Guid scalesetId); + IAsyncEnumerable GetByObjectId(Guid objectId); } public class ScalesetOperations : StatefulOrm, IScalesetOperations { @@ -327,4 +328,8 @@ public async Task> GetById(Guid scalesetId) { return OneFuzzResult.Ok(await data.SingleAsync()); } + + public IAsyncEnumerable GetByObjectId(Guid objectId) { + return QueryAsync(filter: $"client_object_id eq '{objectId}'"); + } } From 58b26d20eabf5be4e3f332dfe3ec6250462e3841 Mon Sep 17 00:00:00 2001 From: Teo Voinea Date: Mon, 9 May 2022 13:12:34 +0000 Subject: [PATCH 2/5] Disable the function for now --- src/ApiService/ApiService/AgentCommands.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ApiService/ApiService/AgentCommands.cs b/src/ApiService/ApiService/AgentCommands.cs index 3ba0021240..d2bea6a743 100644 --- a/src/ApiService/ApiService/AgentCommands.cs +++ b/src/ApiService/ApiService/AgentCommands.cs @@ -13,7 +13,7 @@ public AgentCommands(ILogTracer log, IOnefuzzContext context) { _context = context; } - [Function("AgentCommands")] + // [Function("AgentCommands")] public async Async.Task Run([HttpTrigger("get", "delete")] HttpRequestData req) { return req.Method switch { "get" => await Get(req), From b1e04ef49461140bad2498739e5da0afe3eb18c7 Mon Sep 17 00:00:00 2001 From: Teo Voinea Date: Mon, 9 May 2022 18:07:43 +0000 Subject: [PATCH 3/5] snapshot --- src/ApiService/ApiService/AgentCanSchedule.cs | 3 ++- src/ApiService/ApiService/AgentCommands.cs | 19 ++++++++++--------- .../ApiService/OneFuzzTypes/Model.cs | 2 +- .../ApiService/OneFuzzTypes/Responses.cs | 8 ++++---- .../ApiService/onefuzzlib/NodeOperations.cs | 2 +- .../ApiService/onefuzzlib/Request.cs | 15 ++++++++------- 6 files changed, 26 insertions(+), 23 deletions(-) diff --git a/src/ApiService/ApiService/AgentCanSchedule.cs b/src/ApiService/ApiService/AgentCanSchedule.cs index 001221f9c0..d162b6e782 100644 --- a/src/ApiService/ApiService/AgentCanSchedule.cs +++ b/src/ApiService/ApiService/AgentCanSchedule.cs @@ -50,6 +50,7 @@ public async Async.Task Run([HttpTrigger] HttpRequestData req) allowed = (await _context.NodeOperations.AcquireScaleInProtection(node)).IsOk; } - return await RequestHandling.Ok(req, new CanSchedule(allowed, workStopped)); + var resp = req.CreateResponse(); + return await RequestHandling.Ok(resp, new CanSchedule(allowed, workStopped)); } } diff --git a/src/ApiService/ApiService/AgentCommands.cs b/src/ApiService/ApiService/AgentCommands.cs index d2bea6a743..fbc1b03bec 100644 --- a/src/ApiService/ApiService/AgentCommands.cs +++ b/src/ApiService/ApiService/AgentCommands.cs @@ -13,11 +13,11 @@ public AgentCommands(ILogTracer log, IOnefuzzContext context) { _context = context; } - // [Function("AgentCommands")] + [Function("AgentCommands")] public async Async.Task Run([HttpTrigger("get", "delete")] HttpRequestData req) { return req.Method switch { - "get" => await Get(req), - "delete" => await Delete(req), + "GET" => await Get(req), + "DELETE" => await Delete(req), _ => throw new NotImplementedException($"HTTP Method {req.Method} is not supported for this method") }; } @@ -29,15 +29,15 @@ private async Async.Task Get(HttpRequestData req) { } var nodeCommand = request.OkV; - var message = await _context.NodeMessageOperations.GetMessage(nodeCommand.MachineId).FirstAsync(); - + var message = await _context.NodeMessageOperations.GetMessage(nodeCommand.MachineId).FirstOrDefaultAsync(); + var resp = req.CreateResponse(); if (message != null) { var command = message.Message; var messageId = message.MessageId; - var envelope = new NodeCommandEvenlope(command, messageId); - return await RequestHandling.Ok(req, new PendingNodeCommand(envelope)); + var envelope = new NodeCommandEnvelope(command, messageId); + return await RequestHandling.Ok(resp, new PendingNodeCommand(envelope)); } else { - return await RequestHandling.Ok(req, new PendingNodeCommand(null)); + return await RequestHandling.Ok(resp, new PendingNodeCommand(null)); } } @@ -53,6 +53,7 @@ private async Async.Task Delete(HttpRequestData req) { await _context.NodeMessageOperations.Delete(message); } - return await RequestHandling.Ok(req, new BoolResult(true)); + var resp = req.CreateResponse(); + return await RequestHandling.Ok(resp, new BoolResult(true)); } } diff --git a/src/ApiService/ApiService/OneFuzzTypes/Model.cs b/src/ApiService/ApiService/OneFuzzTypes/Model.cs index b03a60fa90..b6de489a06 100644 --- a/src/ApiService/ApiService/OneFuzzTypes/Model.cs +++ b/src/ApiService/ApiService/OneFuzzTypes/Model.cs @@ -696,7 +696,7 @@ Uri HeartbeatQueue } -public record NodeCommandEvenlope( +public record NodeCommandEnvelope( NodeCommand Command, string MessageId ); diff --git a/src/ApiService/ApiService/OneFuzzTypes/Responses.cs b/src/ApiService/ApiService/OneFuzzTypes/Responses.cs index d0d202183f..f3339c96c5 100644 --- a/src/ApiService/ApiService/OneFuzzTypes/Responses.cs +++ b/src/ApiService/ApiService/OneFuzzTypes/Responses.cs @@ -5,12 +5,12 @@ public record BaseResponse(); public record CanSchedule( bool Allowed, bool WorkStopped -) : BaseResponse; +) : BaseResponse(); public record PendingNodeCommand( - NodeCommandEvenlope? Enveleope -) : BaseResponse; + NodeCommandEnvelope? Envelope +) : BaseResponse(); public record BoolResult( bool Result -) : BaseResponse; +) : BaseResponse(); diff --git a/src/ApiService/ApiService/onefuzzlib/NodeOperations.cs b/src/ApiService/ApiService/onefuzzlib/NodeOperations.cs index 39c87436d9..c3456a0a43 100644 --- a/src/ApiService/ApiService/onefuzzlib/NodeOperations.cs +++ b/src/ApiService/ApiService/onefuzzlib/NodeOperations.cs @@ -468,7 +468,7 @@ public NodeMessageOperations(ILogTracer log, IOnefuzzContext context) : base(log } public IAsyncEnumerable GetMessage(Guid machineId) { - return QueryAsync($"machine_id eq '{machineId}'"); + return QueryAsync($"PartitionKey eq '{machineId}'"); } public async Async.Task ClearMessages(Guid machineId) { diff --git a/src/ApiService/ApiService/onefuzzlib/Request.cs b/src/ApiService/ApiService/onefuzzlib/Request.cs index 62877c6992..b6a44566be 100644 --- a/src/ApiService/ApiService/onefuzzlib/Request.cs +++ b/src/ApiService/ApiService/onefuzzlib/Request.cs @@ -1,6 +1,7 @@ using System.Net; +using System.Text.Json; using Microsoft.Azure.Functions.Worker.Http; - +using Microsoft.OneFuzz.Service.OneFuzzLib.Orm; namespace Microsoft.OneFuzz.Service; @@ -58,23 +59,23 @@ public static Error ConvertError(Exception exception) { ); } - public async static Async.Task Ok(HttpRequestData req, IEnumerable response) { - var resp = req.CreateResponse(); + public async static Async.Task Ok(HttpResponseData resp, IEnumerable response) { + // var resp = req.CreateResponse(); resp.StatusCode = HttpStatusCode.OK; if (response.Count() > 1) { await resp.WriteAsJsonAsync(response); return resp; } else if (response.Any()) { - await resp.WriteAsJsonAsync(response.Single()); + var t = JsonSerializer.Serialize(response.Single(), EntityConverter.GetJsonSerializerOptions()); + await resp.WriteStringAsync(t); } - // TODO: ModelMixin stuff return resp; } - public async static Async.Task Ok(HttpRequestData req, BaseResponse response) { - return await Ok(req, new BaseResponse[] { response }); + public async static Async.Task Ok(HttpResponseData resp, BaseResponse response) { + return await Ok(resp, new BaseResponse[] { response }); } } From cf4e653f65790db0f13e9e7b3ae446aff1e78be6 Mon Sep 17 00:00:00 2001 From: Teo Voinea Date: Tue, 10 May 2022 16:57:23 +0000 Subject: [PATCH 4/5] Tested locally --- src/ApiService/ApiService/AgentCanSchedule.cs | 3 +-- src/ApiService/ApiService/AgentCommands.cs | 10 ++++------ .../ApiService/OneFuzzTypes/Responses.cs | 20 +++++++++++++++++-- .../ApiService/onefuzzlib/Request.cs | 11 +++++----- 4 files changed, 28 insertions(+), 16 deletions(-) diff --git a/src/ApiService/ApiService/AgentCanSchedule.cs b/src/ApiService/ApiService/AgentCanSchedule.cs index d162b6e782..001221f9c0 100644 --- a/src/ApiService/ApiService/AgentCanSchedule.cs +++ b/src/ApiService/ApiService/AgentCanSchedule.cs @@ -50,7 +50,6 @@ public async Async.Task Run([HttpTrigger] HttpRequestData req) allowed = (await _context.NodeOperations.AcquireScaleInProtection(node)).IsOk; } - var resp = req.CreateResponse(); - return await RequestHandling.Ok(resp, new CanSchedule(allowed, workStopped)); + return await RequestHandling.Ok(req, new CanSchedule(allowed, workStopped)); } } diff --git a/src/ApiService/ApiService/AgentCommands.cs b/src/ApiService/ApiService/AgentCommands.cs index fbc1b03bec..b0b336f611 100644 --- a/src/ApiService/ApiService/AgentCommands.cs +++ b/src/ApiService/ApiService/AgentCommands.cs @@ -13,7 +13,7 @@ public AgentCommands(ILogTracer log, IOnefuzzContext context) { _context = context; } - [Function("AgentCommands")] + // [Function("AgentCommands")] public async Async.Task Run([HttpTrigger("get", "delete")] HttpRequestData req) { return req.Method switch { "GET" => await Get(req), @@ -30,14 +30,13 @@ private async Async.Task Get(HttpRequestData req) { var nodeCommand = request.OkV; var message = await _context.NodeMessageOperations.GetMessage(nodeCommand.MachineId).FirstOrDefaultAsync(); - var resp = req.CreateResponse(); if (message != null) { var command = message.Message; var messageId = message.MessageId; var envelope = new NodeCommandEnvelope(command, messageId); - return await RequestHandling.Ok(resp, new PendingNodeCommand(envelope)); + return await RequestHandling.Ok(req, new PendingNodeCommand(envelope)); } else { - return await RequestHandling.Ok(resp, new PendingNodeCommand(null)); + return await RequestHandling.Ok(req, new PendingNodeCommand(null)); } } @@ -53,7 +52,6 @@ private async Async.Task Delete(HttpRequestData req) { await _context.NodeMessageOperations.Delete(message); } - var resp = req.CreateResponse(); - return await RequestHandling.Ok(resp, new BoolResult(true)); + return await RequestHandling.Ok(req, new BoolResult(true)); } } diff --git a/src/ApiService/ApiService/OneFuzzTypes/Responses.cs b/src/ApiService/ApiService/OneFuzzTypes/Responses.cs index f3339c96c5..cf4d267235 100644 --- a/src/ApiService/ApiService/OneFuzzTypes/Responses.cs +++ b/src/ApiService/ApiService/OneFuzzTypes/Responses.cs @@ -1,6 +1,10 @@ -namespace Microsoft.OneFuzz.Service; +using System.Text.Json; +using System.Text.Json.Serialization; -public record BaseResponse(); +namespace Microsoft.OneFuzz.Service; + +[JsonConverter(typeof(BaseResponseConverter))] +public abstract record BaseResponse(); public record CanSchedule( bool Allowed, @@ -14,3 +18,15 @@ public record PendingNodeCommand( public record BoolResult( bool Result ) : BaseResponse(); + + +public class BaseResponseConverter : JsonConverter { + public override BaseResponse? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { + return null; + } + + public override void Write(Utf8JsonWriter writer, BaseResponse value, JsonSerializerOptions options) { + var eventType = value.GetType(); + JsonSerializer.Serialize(writer, value, eventType, options); + } +} diff --git a/src/ApiService/ApiService/onefuzzlib/Request.cs b/src/ApiService/ApiService/onefuzzlib/Request.cs index b6a44566be..b295096522 100644 --- a/src/ApiService/ApiService/onefuzzlib/Request.cs +++ b/src/ApiService/ApiService/onefuzzlib/Request.cs @@ -59,23 +59,22 @@ public static Error ConvertError(Exception exception) { ); } - public async static Async.Task Ok(HttpResponseData resp, IEnumerable response) { - // var resp = req.CreateResponse(); + public async static Async.Task Ok(HttpRequestData req, IEnumerable response) { + var resp = req.CreateResponse(); resp.StatusCode = HttpStatusCode.OK; if (response.Count() > 1) { await resp.WriteAsJsonAsync(response); return resp; } else if (response.Any()) { - var t = JsonSerializer.Serialize(response.Single(), EntityConverter.GetJsonSerializerOptions()); - await resp.WriteStringAsync(t); + await resp.WriteAsJsonAsync(response.Single()); } // TODO: ModelMixin stuff return resp; } - public async static Async.Task Ok(HttpResponseData resp, BaseResponse response) { - return await Ok(resp, new BaseResponse[] { response }); + public async static Async.Task Ok(HttpRequestData req, BaseResponse response) { + return await Ok(req, new BaseResponse[] { response }); } } From 63d25381aca99946de83786fc3ef35b836f8ddc9 Mon Sep 17 00:00:00 2001 From: Teo Voinea Date: Tue, 10 May 2022 17:01:02 +0000 Subject: [PATCH 5/5] fmt --- src/ApiService/ApiService/onefuzzlib/Creds.cs | 6 +++--- .../ApiService/onefuzzlib/EndpointAuthorization.cs | 2 +- src/ApiService/ApiService/onefuzzlib/Request.cs | 2 -- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/src/ApiService/ApiService/onefuzzlib/Creds.cs b/src/ApiService/ApiService/onefuzzlib/Creds.cs index bb41d9eb86..c1e21de819 100644 --- a/src/ApiService/ApiService/onefuzzlib/Creds.cs +++ b/src/ApiService/ApiService/onefuzzlib/Creds.cs @@ -1,8 +1,8 @@ -using Azure.Core; +using System.Text.Json; +using Azure.Core; using Azure.Identity; using Azure.ResourceManager; using Azure.ResourceManager.Resources; -using System.Text.Json; namespace Microsoft.OneFuzz.Service; @@ -92,7 +92,7 @@ public Guid GetScalesetPrincipalId() { var uid = ArmClient.GetGenericResource( new ResourceIdentifier(GetScalesetIdentityResourcePath()) ); - var principalId = JsonSerializer.Deserialize(uid.Data.Properties.ToString())?.RootElement.GetProperty("principalId").GetString(); + var principalId = JsonSerializer.Deserialize(uid.Data.Properties.ToString())?.RootElement.GetProperty("principalId").GetString()!; return new Guid(principalId); } diff --git a/src/ApiService/ApiService/onefuzzlib/EndpointAuthorization.cs b/src/ApiService/ApiService/onefuzzlib/EndpointAuthorization.cs index c8bb563b7c..821f007060 100644 --- a/src/ApiService/ApiService/onefuzzlib/EndpointAuthorization.cs +++ b/src/ApiService/ApiService/onefuzzlib/EndpointAuthorization.cs @@ -22,7 +22,7 @@ public async Async.Task CallIf(HttpRequestData req, Func