// Copyright Epic Games, Inc. All Rights Reserved. using System; using System.Collections.Generic; using System.Linq; using System.Security.Claims; using System.Threading.Tasks; using Horde.Build.Acls; using Horde.Build.Services; using Horde.Build.Utilities; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Options; namespace Horde.Build.Controllers { /// /// Object containing settings for the server /// public class AdminSettings { /// /// The default perforce server /// public string? DefaultServerAndPort { get; set; } /// /// The default perforce username /// public string? DefaultUserName { get; set; } /// /// The default perforce password /// public string? DefaultPassword { get; set; } } /// /// The conform limit value /// public class ConformSettings { /// /// Maximum number of conforms allowed at once /// public int MaxCount { get; set; } } /// /// Controller managing account status /// [ApiController] [Authorize] [Route("[controller]")] public class AdminController : HordeControllerBase { readonly AclService _aclService; readonly UpgradeService _upgradeService; readonly IOptionsMonitor _settings; /// /// Constructor /// /// The ACL service singleton /// The upgrade service singelton /// Server settings public AdminController(AclService aclService, UpgradeService upgradeService, IOptionsMonitor settings) { _aclService = aclService; _upgradeService = upgradeService; _settings = settings; } /// /// Upgrade the database to the latest schema /// /// The schema version to upgrade from. [HttpPost] [Route("/api/v1/admin/upgradeschema")] public async Task UpgradeSchemaAsync([FromQuery] int? fromVersion = null) { if (!await _aclService.AuthorizeAsync(AclAction.AdminWrite, User)) { return Forbid(AclAction.AdminWrite); } await _upgradeService.UpgradeSchemaAsync(fromVersion); return Ok(); } /// /// Issues a token for the given roles. Issues a token for the current user if not specified. /// /// Administrative settings for the server [HttpGet] [Route("/api/v1/admin/token")] public async Task> GetTokenAsync() { if (!await _aclService.AuthorizeAsync(AclAction.IssueBearerToken, User)) { return Forbid(AclAction.IssueBearerToken); } return _aclService.IssueBearerToken(User.Claims, GetDefaultExpiryTime()); } /// /// Issues a token for the given roles. Issues a token for the current user if not specified. /// /// Roles for the new token /// Administrative settings for the server [HttpGet] [Route("/api/v1/admin/roletoken")] public async Task> GetRoleTokenAsync([FromQuery] string roles) { if (!await _aclService.AuthorizeAsync(AclAction.AdminWrite, User)) { return Forbid(AclAction.AdminWrite); } List claims = new List(); claims.AddRange(roles.Split('+').Select(x => new Claim(ClaimTypes.Role, x))); return _aclService.IssueBearerToken(claims, GetDefaultExpiryTime()); } /// /// Issues a token for the given roles. Issues a token for the current user if not specified. /// /// Administrative settings for the server [HttpGet] [Route("/api/v1/admin/registrationtoken")] public async Task> GetRegistrationTokenAsync() { if (!await _aclService.AuthorizeAsync(AclAction.AdminWrite, User)) { return Forbid(AclAction.AdminWrite); } List claims = new List(); claims.Add(new AclClaim(ClaimTypes.Name, User.Identity?.Name ?? "Unknown")); claims.Add(AclService.AgentRegistrationClaim); return _aclService.IssueBearerToken(claims, null); } /// /// Issues a token valid to upload new versions of the agent software. /// /// Administrative settings for the server [HttpGet] [Route("/api/v1/admin/softwaretoken")] public async Task> GetSoftwareTokenAsync() { if (!await _aclService.AuthorizeAsync(AclAction.AdminWrite, User)) { return Forbid(AclAction.AdminWrite); } List claims = new List(); claims.Add(new AclClaim(ClaimTypes.Name, User.Identity?.Name ?? "Unknown")); claims.Add(AclService.UploadSoftwareClaim); return _aclService.IssueBearerToken(claims, null); } /// /// Issues a token valid to download new versions of the agent software. /// /// Administrative settings for the server [HttpGet] [Route("/api/v1/admin/softwaredownloadtoken")] public async Task> GetSoftwareDownloadTokenAsync() { if (!await _aclService.AuthorizeAsync(AclAction.AdminRead, User)) { return Forbid(AclAction.AdminRead); } List claims = new List(); claims.Add(new AclClaim(ClaimTypes.Name, User.Identity?.Name ?? "Unknown")); claims.Add(AclService.DownloadSoftwareClaim); return _aclService.IssueBearerToken(claims, null); } /// /// Issues a token valid to configure streams and projects /// /// Administrative settings for the server [HttpGet] [Route("/api/v1/admin/configtoken")] public async Task> GetConfigToken() { if (!await _aclService.AuthorizeAsync(AclAction.AdminRead, User)) { return Forbid(AclAction.AdminRead); } List claims = new List(); claims.Add(new AclClaim(ClaimTypes.Name, User.Identity?.Name ?? "Unknown")); claims.Add(AclService.ConfigureProjectsClaim); return _aclService.IssueBearerToken(claims, null); } /// /// Issues a token valid to start chained jobs /// /// Administrative settings for the server [HttpGet] [Route("/api/v1/admin/chainedjobtoken")] public async Task> GetChainedJobToken() { if (!await _aclService.AuthorizeAsync(AclAction.AdminRead, User)) { return Forbid(AclAction.AdminRead); } List claims = new List(); //Claims.Add(new AclClaim(ClaimTypes.Name, User.Identity.Name ?? "Unknown")); claims.Add(AclService.StartChainedJobClaim); return _aclService.IssueBearerToken(claims, null); } /// /// Gets the default expiry time for a token /// /// private TimeSpan? GetDefaultExpiryTime() { TimeSpan? expiryTime = null; if (_settings.CurrentValue.JwtExpiryTimeHours != -1) { expiryTime = TimeSpan.FromHours(_settings.CurrentValue.JwtExpiryTimeHours); } return expiryTime; } } }