mirror of
https://github.com/bitwarden/server.git
synced 2025-06-06 11:10:32 -05:00

* Add DynamicClientStore * Formatting * Fix Debug assertion * Make Identity internals visible to its unit tests * Add installation client provider tests * Add internal client provider tests * Add DynamicClientStore tests * Fix namespaces after merge * Format * Add docs and remove TODO comments * Use preferred prefix for API keys --------- Co-authored-by: Jared Snider <116684653+JaredSnider-Bitwarden@users.noreply.github.com>
77 lines
2.4 KiB
C#
77 lines
2.4 KiB
C#
using Bit.Core.Identity;
|
|
using Bit.Core.Repositories;
|
|
using Bit.Core.SecretsManager.Models.Data;
|
|
using Bit.Core.SecretsManager.Repositories;
|
|
using Duende.IdentityServer.Models;
|
|
using IdentityModel;
|
|
|
|
namespace Bit.Identity.IdentityServer.ClientProviders;
|
|
|
|
internal class SecretsManagerApiKeyProvider : IClientProvider
|
|
{
|
|
public const string ApiKeyPrefix = "apikey";
|
|
|
|
private readonly IApiKeyRepository _apiKeyRepository;
|
|
private readonly IOrganizationRepository _organizationRepository;
|
|
|
|
public SecretsManagerApiKeyProvider(IApiKeyRepository apiKeyRepository, IOrganizationRepository organizationRepository)
|
|
{
|
|
_apiKeyRepository = apiKeyRepository;
|
|
_organizationRepository = organizationRepository;
|
|
}
|
|
|
|
public async Task<Client> GetAsync(string identifier)
|
|
{
|
|
if (!Guid.TryParse(identifier, out var apiKeyId))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var apiKey = await _apiKeyRepository.GetDetailsByIdAsync(apiKeyId);
|
|
|
|
if (apiKey == null || apiKey.ExpireAt <= DateTime.UtcNow)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
switch (apiKey)
|
|
{
|
|
case ServiceAccountApiKeyDetails key:
|
|
var org = await _organizationRepository.GetByIdAsync(key.ServiceAccountOrganizationId);
|
|
if (!org.UseSecretsManager || !org.Enabled)
|
|
{
|
|
return null;
|
|
}
|
|
break;
|
|
}
|
|
|
|
var client = new Client
|
|
{
|
|
ClientId = identifier,
|
|
RequireClientSecret = true,
|
|
ClientSecrets = { new Secret(apiKey.ClientSecretHash) },
|
|
AllowedScopes = apiKey.GetScopes(),
|
|
AllowedGrantTypes = GrantTypes.ClientCredentials,
|
|
AccessTokenLifetime = 3600 * 1,
|
|
ClientClaimsPrefix = null,
|
|
Properties = new Dictionary<string, string> {
|
|
{"encryptedPayload", apiKey.EncryptedPayload},
|
|
},
|
|
Claims = new List<ClientClaim>
|
|
{
|
|
new(JwtClaimTypes.Subject, apiKey.ServiceAccountId.ToString()),
|
|
new(Claims.Type, IdentityClientType.ServiceAccount.ToString()),
|
|
},
|
|
};
|
|
|
|
switch (apiKey)
|
|
{
|
|
case ServiceAccountApiKeyDetails key:
|
|
client.Claims.Add(new ClientClaim(Claims.Organization, key.ServiceAccountOrganizationId.ToString()));
|
|
break;
|
|
}
|
|
|
|
return client;
|
|
}
|
|
}
|