// Copyright Epic Games, Inc. All Rights Reserved. using System.Collections.Generic; using System.Security.Claims; using System.Threading.Tasks; using Horde.Build.Acls; using Horde.Build.Utilities; namespace Horde.Build.Projects { using ProjectId = StringId; /// /// Cache of information about job ACLs /// public class ProjectPermissionsCache : GlobalPermissionsCache { /// /// Map of project id to permissions for that project /// public Dictionary Projects { get; } = new Dictionary(); } /// /// Wraps functionality for manipulating projects /// public class ProjectService { /// /// The ACL service /// readonly AclService _aclService; /// /// Collection of project documents /// readonly IProjectCollection _projects; /// /// Accessor for the collection of project documents /// public IProjectCollection Collection => _projects; /// /// Constructor /// /// The ACL service /// Collection of project documents public ProjectService(AclService aclService, IProjectCollection projects) { _aclService = aclService; _projects = projects; } /// /// Gets all the available projects /// /// List of project documents public Task> GetProjectsAsync() { return _projects.FindAllAsync(); } /// /// Gets a project by ID /// /// Unique id of the project /// The project document public Task GetProjectAsync(ProjectId projectId) { return _projects.GetAsync(projectId); } /// /// Gets a project's permissions info by ID /// /// Unique id of the project /// The project document public Task GetProjectPermissionsAsync(ProjectId projectId) { return _projects.GetPermissionsAsync(projectId); } /// /// Deletes a project by id /// /// Unique id of the project public async Task DeleteProjectAsync(ProjectId projectId) { await _projects.DeleteAsync(projectId); } /// /// Determines if the user is authorized to perform an action on a particular project /// /// Acl for the project to check /// The action being performed /// The principal to authorize /// Cache for the scope table /// True if the action is authorized private Task AuthorizeAsync(Acl? acl, AclAction action, ClaimsPrincipal user, GlobalPermissionsCache? cache) { bool? result = acl?.Authorize(action, user); if (result == null) { return _aclService.AuthorizeAsync(action, user, cache); } else { return Task.FromResult(result.Value); } } /// /// Determines if the user is authorized to perform an action on a particular project /// /// The project to check /// The action being performed /// The principal to authorize /// Cache for the scope table /// True if the action is authorized public Task AuthorizeAsync(IProject project, AclAction action, ClaimsPrincipal user, GlobalPermissionsCache? cache) { return AuthorizeAsync(project.Acl, action, user, cache); } /// /// Determines if the user is authorized to perform an action on a particular project /// /// The project id to check /// The action being performed /// The principal to authorize /// Cache for project permissions /// True if the action is authorized public async Task AuthorizeAsync(ProjectId projectId, AclAction action, ClaimsPrincipal user, ProjectPermissionsCache? cache) { IProjectPermissions? permissions; if (cache == null) { permissions = await GetProjectPermissionsAsync(projectId); } else if (!cache.Projects.TryGetValue(projectId, out permissions)) { permissions = await GetProjectPermissionsAsync(projectId); cache.Projects.Add(projectId, permissions); } return await AuthorizeAsync(permissions?.Acl, action, user, cache); } } }