// Copyright Epic Games, Inc. All Rights Reserved. using System; using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using EpicGames.Horde.ServiceAccounts; using HordeServer.Acls; using HordeServer.Users; namespace HordeServer.ServiceAccounts { /// /// An internal Horde account representing a service /// /// Service-to-service authentication always use this for authentication (for example, Robomerge accessing Horde). /// public interface IServiceAccount { /// /// Unique internal ID for this Horde account /// ServiceAccountId Id { get; } /// /// Human-readable name for identifying this account /// string Name { get; } /// /// Description of the account (who is it for, is there an owner etc) /// string Description { get; } /// /// Get list of claims /// /// List of claims IReadOnlyList Claims { get; } /// /// If the account is active /// bool Enabled { get; } /// /// Get the latest version of this account /// /// Cancellation token for the operation /// New account object Task RefreshAsync(CancellationToken cancellationToken = default); /// /// Attempt to update settings for the account /// /// Options for the update /// Cancellation token for the operation /// On success, returns the updated account object Task<(IServiceAccount?, string?)> TryUpdateAsync(UpdateServiceAccountOptions options, CancellationToken cancellationToken = default); } /// /// Options for updating an account /// /// If set, name to update /// If set, description to update /// If set, claims to update /// Whether to reset the secret token for this account /// If set, enabled flag to update public record UpdateServiceAccountOptions( string? Name = null, string? Description = null, IReadOnlyList? Claims = null, bool? ResetToken = null, bool? Enabled = null); /// /// Extension methods for accounts /// public static class AccountExtensions { /// /// Test whether a user has a particular claim /// /// Account to test /// Claim type to check for /// Claim value to check for /// True if the user has the claim public static bool HasClaim(this IServiceAccount account, string type, string value) { foreach (IUserClaim claim in account.Claims) { if (claim.Type.Equals(type, StringComparison.OrdinalIgnoreCase) && claim.Value.Equals(value, StringComparison.Ordinal)) { return true; } } return false; } /// /// Test whether a user has a particular claim /// /// Account to test /// Claim to test for /// True if the user has the claim public static bool HasClaim(this IServiceAccount account, AclClaimConfig claim) => HasClaim(account, claim.Type, claim.Value); /// /// Update settings for the account, retrying if the account object has changed /// /// The account to update /// Options for the update /// Cancellation token for the operation /// On success, returns the updated account object public static async Task<(IServiceAccount?, string?)> UpdateAsync(this IServiceAccount account, UpdateServiceAccountOptions options, CancellationToken cancellationToken = default) { IServiceAccount? updatedAccount = account; while (updatedAccount != null) { (IServiceAccount? newAccount, string? newToken) = await updatedAccount.TryUpdateAsync(options, cancellationToken); if (newAccount != null) { return (newAccount, newToken); } updatedAccount = await account.RefreshAsync(cancellationToken); } return (null, null); } } }