// Copyright Epic Games, Inc. All Rights Reserved.
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Threading;
using System.Threading.Tasks;
using EpicGames.Horde.Acls;
using EpicGames.Horde.Agents;
using HordeServer.Server;
using HordeServer.Utilities;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using MongoDB.Driver;
namespace HordeServer.Acls
{
///
/// Wraps functionality for manipulating permissions
///
public class AclService : IAclService
{
private readonly GlobalsService _globalsService;
private readonly IOptionsMonitor _globalConfig;
///
/// Constructor
///
public AclService(GlobalsService globalsService, IOptionsMonitor globalConfig)
{
_globalsService = globalsService;
_globalConfig = globalConfig;
}
///
/// Issues a bearer token with the given claims
///
/// List of claims to include
/// Time that the token expires
/// Cancellation token for the operation
/// JWT security token with a claim for creating new agents
public async ValueTask IssueBearerTokenAsync(IEnumerable claims, TimeSpan? expiry, CancellationToken cancellationToken = default)
{
IGlobals globals = await _globalsService.GetAsync(cancellationToken);
SigningCredentials signingCredentials = new(globals.JwtSigningKey, SecurityAlgorithms.HmacSha256);
JwtSecurityToken token = new(globals.JwtIssuer, null, claims.DistinctBy(x => (x.Type, x.Value)), null, DateTime.UtcNow + expiry, signingCredentials);
return new JwtSecurityTokenHandler().WriteToken(token);
}
///
/// Gets the agent id associated with a particular user
///
///
///
public static AgentId? GetAgentId(ClaimsPrincipal user)
{
Claim? claim = user.Claims.FirstOrDefault(x => x.Type == HordeClaimTypes.Agent);
if (claim == null)
{
return null;
}
else
{
return new AgentId(claim.Value);
}
}
///
public bool TryGetAclScope(AclScopeName scopeName, [NotNullWhen(true)] out AclConfig? scopeConfig)
=> _globalConfig.CurrentValue.TryGetAclScope(scopeName, out scopeConfig);
}
}