btcpayserver/BTCPayServer/Security/OpenId/OpenIdExtensions.cs

108 lines
4.5 KiB
C#
Raw Normal View History

using System;
using System.Collections.Generic;
2019-10-08 15:21:30 +09:00
using System.Collections.Immutable;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using BTCPayServer.Data;
using BTCPayServer.Models;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Identity;
using OpenIddict.Abstractions;
using OpenIddict.Core;
using OpenIddict.Server;
2019-10-18 21:36:32 +09:00
namespace BTCPayServer.Security.OpenId
{
public static class OpenIdExtensions
{
2019-10-08 15:21:30 +09:00
public static ImmutableHashSet<string> Restrict(this ImmutableHashSet<string> scopes, ClaimsPrincipal claimsPrincipal)
{
HashSet<string> restricted = new HashSet<string>();
foreach (var scope in scopes)
{
if (scope == BTCPayScopes.ServerManagement && !claimsPrincipal.IsInRole(Roles.ServerAdmin))
continue;
restricted.Add(scope);
}
return restricted.ToImmutableHashSet();
}
public static async Task<ClaimsPrincipal> CreateClaimsPrincipalAsync(OpenIddictApplicationManager<BTCPayOpenIdClient> applicationManager,
OpenIddictAuthorizationManager<BTCPayOpenIdAuthorization> authorizationManager,
IdentityOptions identityOptions,
SignInManager<ApplicationUser> signInManager,
2019-10-08 15:21:30 +09:00
OpenIddictRequest request,
ApplicationUser user)
{
var principal = await signInManager.CreateUserPrincipalAsync(user);
if (!request.IsAuthorizationCodeGrantType() && !request.IsRefreshTokenGrantType())
{
2019-10-08 15:21:30 +09:00
principal.SetScopes(request.GetScopes().Restrict(principal));
}
else if (request.IsAuthorizationCodeGrantType() &&
2019-10-08 15:21:30 +09:00
string.IsNullOrEmpty(principal.GetInternalAuthorizationId()))
{
var app = await applicationManager.FindByClientIdAsync(request.ClientId);
var authorizationId = await IsUserAuthorized(authorizationManager, request, user.Id, app.Id);
if (!string.IsNullOrEmpty(authorizationId))
{
2019-10-08 15:21:30 +09:00
principal.SetInternalAuthorizationId(authorizationId);
}
}
2019-10-08 15:21:30 +09:00
principal.SetDestinations(identityOptions);
return principal;
}
public static void SetDestinations(this ClaimsPrincipal principal, IdentityOptions identityOptions)
{
foreach (var claim in principal.Claims)
{
2019-10-08 15:21:30 +09:00
claim.SetDestinations(GetDestinations(identityOptions, claim, principal));
}
}
private static IEnumerable<string> GetDestinations(IdentityOptions identityOptions, Claim claim,
2019-10-08 15:21:30 +09:00
ClaimsPrincipal principal)
{
switch (claim.Type)
{
case OpenIddictConstants.Claims.Name:
case OpenIddictConstants.Claims.Email:
yield return OpenIddictConstants.Destinations.AccessToken;
yield break;
}
}
public static async Task<string> IsUserAuthorized(
OpenIddictAuthorizationManager<BTCPayOpenIdAuthorization> authorizationManager,
2019-10-08 15:21:30 +09:00
OpenIddictRequest request, string userId, string applicationId)
{
2019-10-08 15:21:30 +09:00
var authorizations = await authorizationManager.ListAsync(queryable =>
queryable.Where(authorization =>
authorization.Subject.Equals(userId, StringComparison.OrdinalIgnoreCase) &&
applicationId.Equals(authorization.Application.Id, StringComparison.OrdinalIgnoreCase) &&
authorization.Status.Equals(OpenIddictConstants.Statuses.Valid,
2019-10-08 15:21:30 +09:00
StringComparison.OrdinalIgnoreCase))).ToArrayAsync();
if (authorizations.Length > 0)
{
var scopeTasks = authorizations.Select(authorization =>
(authorizationManager.GetScopesAsync(authorization).AsTask(), authorization.Id));
await Task.WhenAll(scopeTasks.Select((tuple) => tuple.Item1));
var authorizationsWithSufficientScopes = scopeTasks
.Select((tuple) => (Id: tuple.Id, Scopes: tuple.Item1.Result))
.Where((tuple) => !request.GetScopes().Except(tuple.Scopes).Any());
if (authorizationsWithSufficientScopes.Any())
{
return authorizationsWithSufficientScopes.First().Id;
}
}
return null;
}
}
}