Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ updates:
schedule:
interval: "weekly"
day: "sunday"
cooldown:
default-days: 7
open-pull-requests-limit: 10
labels:
- "dependencies"
Expand All @@ -14,6 +16,8 @@ updates:
schedule:
interval: "weekly"
day: "sunday"
cooldown:
default-days: 7
open-pull-requests-limit: 5
labels:
- "dependencies"
Expand All @@ -23,6 +27,8 @@ updates:
schedule:
interval: "weekly"
day: "sunday"
cooldown:
default-days: 7
open-pull-requests-limit: 5
labels:
- "dependencies"
Expand All @@ -32,6 +38,8 @@ updates:
schedule:
interval: "weekly"
day: "sunday"
cooldown:
default-days: 7
open-pull-requests-limit: 5
labels:
- "dependencies"
Expand All @@ -41,6 +49,8 @@ updates:
schedule:
interval: "weekly"
day: "sunday"
cooldown:
default-days: 7
open-pull-requests-limit: 5
labels:
- "dependencies"
Expand All @@ -50,6 +60,8 @@ updates:
schedule:
interval: "weekly"
day: "sunday"
cooldown:
default-days: 7
open-pull-requests-limit: 5
labels:
- "dependencies"
32 changes: 32 additions & 0 deletions Integrations/MCP/ScriptBee.MCP/Generated/GatewayApi.g.cs
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,16 @@ public partial interface IGatewayApi
[Get("/api/projects/{projectId}")]
Task<ProjectDetails> ProjectsGet(string projectId, CancellationToken cancellationToken = default);

/// <summary>Get project permissions</summary>
/// <remarks>Get all the permissions for the user associated for the project.</remarks>
/// <param name="projectId">projectId parameter</param>
/// <param name="cancellationToken">The cancellation token to cancel the request.</param>
/// <returns>OK</returns>
/// <exception cref="ApiException">Thrown when the request returns a non-success status code.</exception>
[Headers("Accept: application/json")]
[Get("/api/projects/{projectId}/permissions")]
Task<GetUserPermissionsForProjectResponse> Permissions(string projectId, CancellationToken cancellationToken = default);

/// <summary>Create a new script</summary>
/// <remarks>Creates a new script within the specified project.</remarks>
/// <param name="projectId">projectId parameter</param>
Expand Down Expand Up @@ -1805,6 +1815,28 @@ public IDictionary<string, object> AdditionalProperties

}

[System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.7.1.0 (NJsonSchema v11.6.1.0 (Newtonsoft.Json v13.0.0.0))")]
public partial class GetUserPermissionsForProjectResponse
{

[JsonPropertyName("role")]
public string Role { get; set; }

[JsonPropertyName("permissions")]
[System.ComponentModel.DataAnnotations.Required]
public ICollection<string> Permissions { get; set; } = new System.Collections.ObjectModel.Collection<string>();

private IDictionary<string, object> _additionalProperties;

[JsonExtensionData]
public IDictionary<string, object> AdditionalProperties
{
get { return _additionalProperties ?? (_additionalProperties = new Dictionary<string, object>()); }
set { _additionalProperties = value; }
}

}

[System.CodeDom.Compiler.GeneratedCode("NJsonSchema", "14.7.1.0 (NJsonSchema v11.6.1.0 (Newtonsoft.Json v13.0.0.0))")]
public partial class HttpValidationProblemDetails
{
Expand Down
3 changes: 3 additions & 0 deletions ScriptBeeWebApp/src/Common/Model/User/UserPermissions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
namespace ScriptBee.Domain.Model.User;

public record UserPermissions(UserRole Role, List<string> Permissions);
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public IServiceCollection AddUserManagementAdaptersAdapters(IMongoDatabase mongo
{
return services
.AddMongoCollection<MongodbUser>(mongoDatabase, "Users")
.AddSingleton<UserManagementIndexes>()
.AddSingleton<IIndexCreator, UserManagementIndexes>()
.AddSingleton<IGetOrAddUser, UserManagementPersistenceAdapter>();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,27 +20,6 @@ public class ExternalAuthorizationRequestInput
public required ExternalAuthorizationResource Resource { get; init; }
}

public class ExternalAuthorizationRequestSubject
{
[JsonPropertyName("user_id")]
public required string UserId { get; init; }

[JsonPropertyName("groups")]
public required List<string> Groups { get; init; }
}

public class ExternalAuthorizationResource
{
[JsonPropertyName("type")]
public required string Type { get; init; }

[JsonPropertyName("id")]
public string? Id { get; init; }

[JsonPropertyName("role")]
public string? Role { get; init; }
}

public class ExternalAuthorizationResponse
{
[JsonPropertyName("result")]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System.Text.Json.Serialization;

namespace ScriptBee.Web.Auth.Contracts;

public class ExternalAuthorizationRequestSubject
{
[JsonPropertyName("user_id")]
public required string UserId { get; init; }

[JsonPropertyName("groups")]
public required List<string> Groups { get; init; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
using System.Text.Json.Serialization;

namespace ScriptBee.Web.Auth.Contracts;

public class ExternalAuthorizationResource
{
[JsonPropertyName("type")]
public required string Type { get; init; }

[JsonPropertyName("id")]
public string? Id { get; init; }

[JsonPropertyName("role")]
public string? Role { get; init; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
using System.Text.Json.Serialization;

namespace ScriptBee.Web.Auth.Contracts;

public class PermissionsRequest
{
[JsonPropertyName("input")]
public required PermissionsRequestInput Input { get; init; }
}

public class PermissionsRequestInput
{
[JsonPropertyName("subject")]
public required ExternalAuthorizationRequestSubject Subject { get; init; }

[JsonPropertyName("resource")]
public required ExternalAuthorizationResource Resource { get; init; }
}

public class PermissionsResponse
{
[JsonPropertyName("result")]
public required List<string> Permissions { get; init; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
using System.Text.Json.Serialization;

namespace ScriptBee.Web.Auth.Contracts;

public class RolesResponse
{
[JsonPropertyName("result")]
public required List<RoleEntry> Roles { get; init; }
}

public class RoleEntry
{
[JsonPropertyName("id")]
public required string Id { get; init; }

[JsonPropertyName("description")]
public required string Description { get; init; }
}
8 changes: 5 additions & 3 deletions ScriptBeeWebApp/src/Gateway/Adapters/Web/Auth/CurrentUser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@

namespace ScriptBee.Web.Auth;

public sealed class CurrentUser(UserId id)
public sealed class CurrentUser(UserId id, List<UserGroup> groups)
{
public UserId Id => id;
public List<UserGroup> Groups => groups;

public static async ValueTask<CurrentUser?> BindAsync(HttpContext context)
{
Expand All @@ -22,7 +23,7 @@ public sealed class CurrentUser(UserId id)

if (authConfig.IsDevelopment)
{
return new CurrentUser(new UserId(""));
return new CurrentUser(new UserId(""), []);
}

if (user.Identity?.IsAuthenticated != true)
Expand All @@ -36,8 +37,9 @@ public sealed class CurrentUser(UserId id)
useCase,
context.RequestAborted
);
var groups = ExtractGroupsFromClaims(user, authConfig);

return !userId.HasValue ? null : new CurrentUser(userId.Value);
return !userId.HasValue ? null : new CurrentUser(userId.Value, groups);
}

public static async Task<UserId?> ExtractUserIdFromClaims(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ private async Task<ExternalAuthorizationRequest> GetProjectRequest(
CancellationToken cancellationToken
)
{
// TODO: should cache this mongo query
var resourceRole = await getResourceRole.GetRole(
userId,
groups,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
using ScriptBee.Domain.Model.Project;
using ScriptBee.Domain.Model.User;
using ScriptBee.Ports.Permissions;
using ScriptBee.Web.Auth.Contracts;

namespace ScriptBee.Web.Auth;

public sealed class GetProjectPermissions(IHttpClientFactory httpClientFactory)
: IGetProjectPermissions
{
public const string ClientName = "GetProjectPermissions";

private readonly HttpClient _httpClient = httpClientFactory.CreateClient(ClientName);

public async Task<List<string>> GetPermissions(
ProjectId projectId,
UserId userId,
List<UserGroup> groups,
UserRole userRole,
CancellationToken cancellationToken
)
{
var request = GetProjectRequest(userId, groups, projectId, userRole);
var response = await _httpClient.PostAsJsonAsync(
"",
request,
cancellationToken: cancellationToken
);

if (!response.IsSuccessStatusCode)
{
return [];
}

var result = await response.Content.ReadFromJsonAsync<PermissionsResponse>(
cancellationToken: cancellationToken
);

return result?.Permissions ?? [];
}

private static PermissionsRequest GetProjectRequest(
UserId userId,
List<UserGroup> groups,
ProjectId projectId,
UserRole? role
)
{
return new PermissionsRequest
{
Input = new PermissionsRequestInput
{
Subject = new ExternalAuthorizationRequestSubject
{
UserId = userId.Value,
Groups = [.. groups.Select(g => g.Value)],
},
Resource = new ExternalAuthorizationResource
{
Type = "project",
Id = projectId.Value,
Role = role?.Value,
},
},
};
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ public class AuthenticationConfig
public string? UserIdClaim { get; init; }
public string? GroupsClaim { get; init; }
public string? ExternalAuthorizationUrl { get; init; }
public string? PermissionsUrl { get; init; }
public string? RolesUrl { get; init; }
public string? DefaultCreatorRoleUrl { get; init; }

public bool IsDevelopment =>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
namespace ScriptBee.Web.EndpointDefinitions.Project.Contracts;

public record WebGetUserPermissionsForProjectResponse(
string? Role,
IEnumerable<string> Permissions
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.AspNetCore.Mvc;
using ScriptBee.Common.Web;
using ScriptBee.Domain.Model.Project;
using ScriptBee.Service.Gateway;
using ScriptBee.UseCases.Gateway;
using ScriptBee.Web.Auth;
using ScriptBee.Web.EndpointDefinitions.Project.Contracts;

namespace ScriptBee.Web.EndpointDefinitions.Project;

public class GetProjectPermissionsEndpoint : IEndpointDefinition
{
public void DefineServices(IServiceCollection services)
{
services.AddSingleton<IProjectPermissionsUseCase, ProjectPermissionsService>();
}

public void DefineEndpoints(IEndpointRouteBuilder app)
{
app.MapGet("/api/projects/{projectId}/permissions", GetUserPermissionsForProject)
.WithTags("Projects", "Permissions")
.WithSummary("Get project permissions")
.WithDescription("Get all the permissions for the user associated for the project.")
.RequireAuthorization();
}

private static async Task<
Ok<WebGetUserPermissionsForProjectResponse>
> GetUserPermissionsForProject(
CurrentUser currentUser,
[FromRoute] string projectId,
IProjectPermissionsUseCase useCase,
CancellationToken cancellationToken
)
{
var query = new GetProjectPermissionsQuery(
ProjectId.FromValue(projectId),
currentUser.Id,
currentUser.Groups
);
var userPermissions = await useCase.GetProjectPermissions(query, cancellationToken);

if (userPermissions == null)
{
return TypedResults.Ok(new WebGetUserPermissionsForProjectResponse(null, []));
}

return TypedResults.Ok(
new WebGetUserPermissionsForProjectResponse(
userPermissions.Role.Value,
userPermissions.Permissions
)
);
}
}
Loading
Loading