2019-08-29 09:25:16 +02:00
|
|
|
using System;
|
|
|
|
using System.Collections.Generic;
|
2019-10-08 15:21:30 +09:00
|
|
|
using System.Collections.Immutable;
|
2019-08-29 09:25:16 +02:00
|
|
|
using System.Linq;
|
|
|
|
using System.Security.Claims;
|
|
|
|
using System.Threading.Tasks;
|
2019-08-30 00:24:42 +09:00
|
|
|
using BTCPayServer.Data;
|
2019-08-29 09:25:16 +02:00
|
|
|
using BTCPayServer.Models;
|
|
|
|
using Microsoft.AspNetCore.Authentication;
|
|
|
|
using Microsoft.AspNetCore.Identity;
|
|
|
|
using OpenIddict.Abstractions;
|
|
|
|
using OpenIddict.Core;
|
|
|
|
using OpenIddict.Server;
|
2019-10-08 15:21:30 +09:00
|
|
|
using static BTCPayServer.Authentication.RestAPIPolicies;
|
2019-08-29 09:25:16 +02:00
|
|
|
|
|
|
|
namespace BTCPayServer.Authentication.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,
|
2019-08-29 09:25:16 +02:00
|
|
|
OpenIddictAuthorizationManager<BTCPayOpenIdAuthorization> authorizationManager,
|
|
|
|
IdentityOptions identityOptions,
|
|
|
|
SignInManager<ApplicationUser> signInManager,
|
2019-10-08 15:21:30 +09:00
|
|
|
OpenIddictRequest request,
|
|
|
|
ApplicationUser user)
|
2019-08-29 09:25:16 +02:00
|
|
|
{
|
|
|
|
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));
|
2019-08-29 09:25:16 +02:00
|
|
|
}
|
|
|
|
else if (request.IsAuthorizationCodeGrantType() &&
|
2019-10-08 15:21:30 +09:00
|
|
|
string.IsNullOrEmpty(principal.GetInternalAuthorizationId()))
|
2019-08-29 09:25:16 +02:00
|
|
|
{
|
|
|
|
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-08-29 09:25:16 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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-08-29 09:25:16 +02:00
|
|
|
{
|
2019-10-08 15:21:30 +09:00
|
|
|
claim.SetDestinations(GetDestinations(identityOptions, claim, principal));
|
2019-08-29 09:25:16 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private static IEnumerable<string> GetDestinations(IdentityOptions identityOptions, Claim claim,
|
2019-10-08 15:21:30 +09:00
|
|
|
ClaimsPrincipal principal)
|
2019-08-29 09:25:16 +02:00
|
|
|
{
|
|
|
|
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-08-29 09:25:16 +02:00
|
|
|
{
|
2019-10-08 15:21:30 +09:00
|
|
|
var authorizations = await authorizationManager.ListAsync(queryable =>
|
2019-08-29 09:25:16 +02:00
|
|
|
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();
|
2019-08-29 09:25:16 +02:00
|
|
|
|
|
|
|
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) => (tuple.Id, Scopes: tuple.Item1.Result))
|
|
|
|
.Where((tuple) => !request.GetScopes().Except(tuple.Scopes).Any());
|
|
|
|
|
|
|
|
if (authorizationsWithSufficientScopes.Any())
|
|
|
|
{
|
|
|
|
return authorizationsWithSufficientScopes.First().Id;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|