mirror of https://github.com/abpframework/abp.git
committed by
GitHub
29 changed files with 687 additions and 17 deletions
@ -0,0 +1,24 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Security.Claims; |
|||
|
|||
namespace Volo.Abp.Security.Claims |
|||
{ |
|||
public static class CurrentPrincipalAccessorExtensions |
|||
{ |
|||
public static IDisposable Change(this ICurrentPrincipalAccessor currentPrincipalAccessor, Claim claim) |
|||
{ |
|||
return currentPrincipalAccessor.Change(new[] {claim}); |
|||
} |
|||
|
|||
public static IDisposable Change(this ICurrentPrincipalAccessor currentPrincipalAccessor, IEnumerable<Claim> claims) |
|||
{ |
|||
return currentPrincipalAccessor.Change(new ClaimsIdentity(claims)); |
|||
} |
|||
|
|||
public static IDisposable Change(this ICurrentPrincipalAccessor currentPrincipalAccessor, ClaimsIdentity claimsIdentity) |
|||
{ |
|||
return currentPrincipalAccessor.Change(new ClaimsPrincipal(claimsIdentity)); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,20 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Domain.Repositories; |
|||
|
|||
namespace Volo.Abp.Identity |
|||
{ |
|||
public interface IIdentityLinkUserRepository : IBasicRepository<IdentityLinkUser, Guid> |
|||
{ |
|||
Task<IdentityLinkUser> FindAsync( |
|||
IdentityLinkUserInfo sourceLinkUserInfo, |
|||
IdentityLinkUserInfo targetLinkUserInfo, |
|||
CancellationToken cancellationToken = default); |
|||
|
|||
Task<List<IdentityLinkUser>> GetListAsync( |
|||
IdentityLinkUserInfo linkUserInfo, |
|||
CancellationToken cancellationToken = default); |
|||
} |
|||
} |
|||
@ -0,0 +1,44 @@ |
|||
using System; |
|||
using Volo.Abp.Domain.Entities; |
|||
|
|||
namespace Volo.Abp.Identity |
|||
{ |
|||
public class IdentityLinkUser : BasicAggregateRoot<Guid> |
|||
{ |
|||
public virtual Guid SourceUserId { get; protected set; } |
|||
|
|||
public virtual Guid? SourceTenantId { get; protected set; } |
|||
|
|||
public virtual Guid TargetUserId { get; protected set; } |
|||
|
|||
public virtual Guid? TargetTenantId { get; protected set; } |
|||
|
|||
/// <summary>
|
|||
/// Initializes a new instance of <see cref="IdentityLinkUser"/>.
|
|||
/// </summary>
|
|||
protected IdentityLinkUser() |
|||
{ |
|||
|
|||
} |
|||
|
|||
public IdentityLinkUser(Guid id, IdentityLinkUserInfo sourceUser, IdentityLinkUserInfo targetUser) |
|||
: base(id) |
|||
{ |
|||
SourceUserId = sourceUser.UserId; |
|||
SourceTenantId = sourceUser.TenantId; |
|||
|
|||
TargetUserId = targetUser.UserId; |
|||
TargetTenantId = targetUser.TenantId; |
|||
} |
|||
|
|||
public IdentityLinkUser(Guid id, Guid sourceUserId, Guid? sourceTenantId, Guid targetUserId, Guid? targetTenantId) |
|||
: base(id) |
|||
{ |
|||
SourceUserId = sourceUserId; |
|||
SourceTenantId = sourceTenantId; |
|||
|
|||
TargetUserId = targetUserId; |
|||
TargetTenantId = targetTenantId; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,17 @@ |
|||
using System; |
|||
|
|||
namespace Volo.Abp.Identity |
|||
{ |
|||
public class IdentityLinkUserInfo |
|||
{ |
|||
public virtual Guid UserId { get; set; } |
|||
|
|||
public virtual Guid? TenantId { get; set; } |
|||
|
|||
public IdentityLinkUserInfo(Guid userId, Guid? tenantId) |
|||
{ |
|||
UserId = userId; |
|||
TenantId = tenantId; |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,86 @@ |
|||
using System; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Domain.Services; |
|||
using Volo.Abp.MultiTenancy; |
|||
|
|||
namespace Volo.Abp.Identity |
|||
{ |
|||
public class IdentityLinkUserManager : DomainService |
|||
{ |
|||
protected IIdentityLinkUserRepository IdentityLinkUserRepository { get; } |
|||
|
|||
protected IdentityUserManager UserManager { get; } |
|||
|
|||
protected new ICurrentTenant CurrentTenant { get; } |
|||
|
|||
public IdentityLinkUserManager(IIdentityLinkUserRepository identityLinkUserRepository, IdentityUserManager userManager, ICurrentTenant currentTenant) |
|||
{ |
|||
IdentityLinkUserRepository = identityLinkUserRepository; |
|||
UserManager = userManager; |
|||
CurrentTenant = currentTenant; |
|||
} |
|||
|
|||
public virtual async Task LinkAsync(IdentityLinkUserInfo sourceLinkUser, IdentityLinkUserInfo targetLinkUser) |
|||
{ |
|||
if (sourceLinkUser.UserId == targetLinkUser.UserId && sourceLinkUser.TenantId == targetLinkUser.TenantId) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
if (await IsLinkedAsync(sourceLinkUser, targetLinkUser)) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
var userLink = new IdentityLinkUser( |
|||
GuidGenerator.Create(), |
|||
sourceLinkUser, |
|||
targetLinkUser); |
|||
await IdentityLinkUserRepository.InsertAsync(userLink, true); |
|||
} |
|||
|
|||
public virtual async Task<bool> IsLinkedAsync(IdentityLinkUserInfo sourceLinkUser, IdentityLinkUserInfo targetLinkUser) |
|||
{ |
|||
return await IdentityLinkUserRepository.FindAsync(sourceLinkUser, targetLinkUser) != null; |
|||
} |
|||
|
|||
public virtual async Task UnlinkAsync(IdentityLinkUserInfo sourceLinkUser, IdentityLinkUserInfo targetLinkUser) |
|||
{ |
|||
if (!await IsLinkedAsync(sourceLinkUser, targetLinkUser)) |
|||
{ |
|||
return; |
|||
} |
|||
|
|||
var linkedUser = await IdentityLinkUserRepository.FindAsync(sourceLinkUser, targetLinkUser); |
|||
if (linkedUser != null) |
|||
{ |
|||
await IdentityLinkUserRepository.DeleteAsync(linkedUser); |
|||
} |
|||
} |
|||
|
|||
public virtual async Task<string> GenerateLinkTokenAsync(IdentityLinkUserInfo targetLinkUser) |
|||
{ |
|||
using (CurrentTenant.Change(targetLinkUser.TenantId)) |
|||
{ |
|||
var user = await UserManager.GetByIdAsync(targetLinkUser.UserId); |
|||
return await UserManager.GenerateUserTokenAsync( |
|||
user, |
|||
LinkUserTokenProvider.LinkUserTokenProviderName, |
|||
LinkUserTokenProvider.LinkUserTokenPurpose); |
|||
} |
|||
} |
|||
|
|||
public virtual async Task<bool> VerifyLinkTokenAsync(IdentityLinkUserInfo targetLinkUser, string token) |
|||
{ |
|||
using (CurrentTenant.Change(targetLinkUser.TenantId)) |
|||
{ |
|||
var user = await UserManager.GetByIdAsync(targetLinkUser.UserId); |
|||
return await UserManager.VerifyUserTokenAsync( |
|||
user, |
|||
LinkUserTokenProvider.LinkUserTokenProviderName, |
|||
LinkUserTokenProvider.LinkUserTokenPurpose, |
|||
token); |
|||
} |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,17 @@ |
|||
using System.Threading.Tasks; |
|||
using Microsoft.AspNetCore.Identity; |
|||
|
|||
namespace Volo.Abp.Identity |
|||
{ |
|||
public class LinkUserTokenProvider : TotpSecurityStampBasedTokenProvider<IdentityUser> |
|||
{ |
|||
public const string LinkUserTokenProviderName = "AbpLinkUser"; |
|||
|
|||
public const string LinkUserTokenPurpose = "AbpLinkUserLogin"; |
|||
|
|||
public override Task<bool> CanGenerateTwoFactorTokenAsync(UserManager<IdentityUser> manager, IdentityUser user) |
|||
{ |
|||
return Task.FromResult(false); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,39 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Linq.Dynamic.Core; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using Microsoft.EntityFrameworkCore; |
|||
using Volo.Abp.Domain.Repositories.EntityFrameworkCore; |
|||
using Volo.Abp.EntityFrameworkCore; |
|||
|
|||
namespace Volo.Abp.Identity.EntityFrameworkCore |
|||
{ |
|||
public class EfCoreIdentityLinkUserRepository : EfCoreRepository<IIdentityDbContext, IdentityLinkUser, Guid>, IIdentityLinkUserRepository |
|||
{ |
|||
public EfCoreIdentityLinkUserRepository(IDbContextProvider<IIdentityDbContext> dbContextProvider) |
|||
: base(dbContextProvider) |
|||
{ |
|||
|
|||
} |
|||
|
|||
public async Task<IdentityLinkUser> FindAsync(IdentityLinkUserInfo sourceLinkUserInfo, IdentityLinkUserInfo targetLinkUserInfo, CancellationToken cancellationToken = default) |
|||
{ |
|||
return await DbSet.FirstOrDefaultAsync(x => |
|||
x.SourceUserId == sourceLinkUserInfo.UserId && x.SourceTenantId == sourceLinkUserInfo.TenantId && |
|||
x.TargetUserId == targetLinkUserInfo.UserId && x.TargetTenantId == targetLinkUserInfo.TenantId || |
|||
x.TargetUserId == sourceLinkUserInfo.UserId && x.TargetTenantId == sourceLinkUserInfo.TenantId && |
|||
x.SourceUserId == targetLinkUserInfo.UserId && x.SourceTenantId == targetLinkUserInfo.TenantId |
|||
, cancellationToken: GetCancellationToken(cancellationToken)); |
|||
} |
|||
|
|||
public async Task<List<IdentityLinkUser>> GetListAsync(IdentityLinkUserInfo linkUserInfo, CancellationToken cancellationToken = default) |
|||
{ |
|||
return await DbSet.Where(x => |
|||
x.SourceUserId == linkUserInfo.UserId && x.SourceTenantId == linkUserInfo.TenantId || |
|||
x.TargetUserId == linkUserInfo.UserId && x.TargetTenantId == linkUserInfo.TenantId) |
|||
.ToListAsync(cancellationToken: GetCancellationToken(cancellationToken)); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,38 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Linq.Dynamic.Core; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using MongoDB.Driver; |
|||
using MongoDB.Driver.Linq; |
|||
using Volo.Abp.Domain.Repositories.MongoDB; |
|||
using Volo.Abp.MongoDB; |
|||
|
|||
namespace Volo.Abp.Identity.MongoDB |
|||
{ |
|||
public class MongoIdentityLinkUserRepository : MongoDbRepository<IAbpIdentityMongoDbContext, IdentityLinkUser, Guid>, IIdentityLinkUserRepository |
|||
{ |
|||
public MongoIdentityLinkUserRepository(IMongoDbContextProvider<IAbpIdentityMongoDbContext> dbContextProvider) : base(dbContextProvider) |
|||
{ |
|||
} |
|||
|
|||
public async Task<IdentityLinkUser> FindAsync(IdentityLinkUserInfo sourceLinkUserInfo, IdentityLinkUserInfo targetLinkUserInfo, CancellationToken cancellationToken = default) |
|||
{ |
|||
return await GetMongoQueryable().FirstOrDefaultAsync(x => |
|||
x.SourceUserId == sourceLinkUserInfo.UserId && x.SourceTenantId == sourceLinkUserInfo.TenantId && |
|||
x.TargetUserId == targetLinkUserInfo.UserId && x.TargetTenantId == targetLinkUserInfo.TenantId || |
|||
x.TargetUserId == sourceLinkUserInfo.UserId && x.TargetTenantId == sourceLinkUserInfo.TenantId && |
|||
x.SourceUserId == targetLinkUserInfo.UserId && x.SourceTenantId == targetLinkUserInfo.TenantId |
|||
, cancellationToken: GetCancellationToken(cancellationToken)); |
|||
} |
|||
|
|||
public async Task<List<IdentityLinkUser>> GetListAsync(IdentityLinkUserInfo linkUserInfo, CancellationToken cancellationToken = default) |
|||
{ |
|||
return await GetMongoQueryable().Where(x => |
|||
x.SourceUserId == linkUserInfo.UserId && x.SourceTenantId == linkUserInfo.TenantId || |
|||
x.TargetUserId == linkUserInfo.UserId && x.TargetTenantId == linkUserInfo.TenantId) |
|||
.ToListAsync(cancellationToken: GetCancellationToken(cancellationToken)); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,20 @@ |
|||
using Microsoft.AspNetCore.Identity; |
|||
using Microsoft.Extensions.Options; |
|||
using Shouldly; |
|||
using Xunit; |
|||
|
|||
namespace Volo.Abp.Identity.AspNetCore |
|||
{ |
|||
public class LinkUserTokenProvider_Tests : AbpIdentityAspNetCoreTestBase |
|||
{ |
|||
[Fact] |
|||
public void LinkUserTokenProvider_Should_Be_Register() |
|||
{ |
|||
var identityOptions = GetRequiredService<IOptions<IdentityOptions>>().Value; |
|||
|
|||
identityOptions.Tokens.ProviderMap.ShouldContain(x => |
|||
x.Key == LinkUserTokenProvider.LinkUserTokenProviderName && |
|||
x.Value.ProviderType == typeof(LinkUserTokenProvider)); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,86 @@ |
|||
using System.Threading.Tasks; |
|||
using Shouldly; |
|||
using Xunit; |
|||
|
|||
namespace Volo.Abp.Identity |
|||
{ |
|||
public class IdentityLinkUserManager_Tests : AbpIdentityDomainTestBase |
|||
{ |
|||
protected IIdentityUserRepository UserRepository { get; } |
|||
protected IIdentityLinkUserRepository IdentityLinkUserRepository { get; } |
|||
protected IdentityLinkUserManager IdentityLinkUserManager { get; } |
|||
protected IdentityTestData TestData { get; } |
|||
|
|||
public IdentityLinkUserManager_Tests() |
|||
{ |
|||
UserRepository = GetRequiredService<IIdentityUserRepository>(); |
|||
IdentityLinkUserRepository = GetRequiredService<IIdentityLinkUserRepository>(); |
|||
IdentityLinkUserManager = GetRequiredService<IdentityLinkUserManager>(); |
|||
TestData = GetRequiredService<IdentityTestData>(); |
|||
} |
|||
|
|||
[Fact] |
|||
public virtual async Task LinkAsync() |
|||
{ |
|||
var john = await UserRepository.GetAsync(TestData.UserJohnId); |
|||
var neo = await UserRepository.GetAsync(TestData.UserNeoId); |
|||
|
|||
(await IdentityLinkUserRepository.FindAsync(new IdentityLinkUserInfo(john.Id, john.TenantId), |
|||
new IdentityLinkUserInfo(neo.Id, neo.TenantId))).ShouldBeNull(); |
|||
|
|||
await IdentityLinkUserManager.LinkAsync(new IdentityLinkUserInfo(john.Id, john.TenantId), |
|||
new IdentityLinkUserInfo(neo.Id, neo.TenantId)); |
|||
|
|||
var linkUser = await IdentityLinkUserRepository.FindAsync(new IdentityLinkUserInfo(john.Id, john.TenantId), |
|||
new IdentityLinkUserInfo(neo.Id, neo.TenantId)); |
|||
|
|||
linkUser.ShouldNotBeNull(); |
|||
linkUser.SourceUserId.ShouldBe(john.Id); |
|||
linkUser.SourceTenantId.ShouldBe(john.TenantId); |
|||
|
|||
linkUser.TargetUserId.ShouldBe(neo.Id); |
|||
linkUser.TargetTenantId.ShouldBe(neo.TenantId); |
|||
} |
|||
|
|||
[Fact] |
|||
public virtual async Task UnlinkAsync() |
|||
{ |
|||
var john = await UserRepository.GetAsync(TestData.UserJohnId); |
|||
var david = await UserRepository.GetAsync(TestData.UserDavidId); |
|||
|
|||
(await IdentityLinkUserRepository.FindAsync(new IdentityLinkUserInfo(john.Id, john.TenantId), |
|||
new IdentityLinkUserInfo(david.Id, david.TenantId))).ShouldNotBeNull(); |
|||
|
|||
await IdentityLinkUserManager.UnlinkAsync(new IdentityLinkUserInfo(john.Id, john.TenantId), |
|||
new IdentityLinkUserInfo(david.Id, david.TenantId)); |
|||
|
|||
(await IdentityLinkUserRepository.FindAsync(new IdentityLinkUserInfo(john.Id, john.TenantId), |
|||
new IdentityLinkUserInfo(david.Id, david.TenantId))).ShouldBeNull(); |
|||
} |
|||
|
|||
[Fact] |
|||
public virtual async Task IsLinkedAsync() |
|||
{ |
|||
var john = await UserRepository.GetAsync(TestData.UserJohnId); |
|||
var david = await UserRepository.GetAsync(TestData.UserDavidId); |
|||
var neo = await UserRepository.GetAsync(TestData.UserNeoId); |
|||
|
|||
(await IdentityLinkUserManager.IsLinkedAsync(new IdentityLinkUserInfo(john.Id, john.TenantId), |
|||
new IdentityLinkUserInfo(david.Id, david.TenantId))).ShouldBeTrue(); |
|||
|
|||
(await IdentityLinkUserManager.IsLinkedAsync(new IdentityLinkUserInfo(john.Id, john.TenantId), |
|||
new IdentityLinkUserInfo(neo.Id, neo.TenantId))).ShouldBeFalse(); |
|||
} |
|||
|
|||
[Fact] |
|||
public virtual async Task GenerateAndVerifyLinkTokenAsync() |
|||
{ |
|||
var john = await UserRepository.GetAsync(TestData.UserJohnId); |
|||
var token = await IdentityLinkUserManager.GenerateLinkTokenAsync(new IdentityLinkUserInfo(john.Id, john.TenantId)); |
|||
(await IdentityLinkUserManager.VerifyLinkTokenAsync(new IdentityLinkUserInfo(john.Id, john.TenantId), token)).ShouldBeTrue(); |
|||
|
|||
(await IdentityLinkUserManager.VerifyLinkTokenAsync(new IdentityLinkUserInfo(john.Id, john.TenantId), "123123")).ShouldBeFalse(); |
|||
} |
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,7 @@ |
|||
namespace Volo.Abp.Identity.EntityFrameworkCore |
|||
{ |
|||
public class IdentityLinkUserRepository_Tests : IdentityLinkUserRepository_Tests<AbpIdentityEntityFrameworkCoreTestModule> |
|||
{ |
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,10 @@ |
|||
using Xunit; |
|||
|
|||
namespace Volo.Abp.Identity.MongoDB |
|||
{ |
|||
[Collection(MongoTestCollection.Name)] |
|||
public class IdentityLinkUserRepository_Tests : IdentityLinkUserRepository_Tests<AbpIdentityMongoDbTestModule> |
|||
{ |
|||
|
|||
} |
|||
} |
|||
@ -0,0 +1,58 @@ |
|||
using System.Threading.Tasks; |
|||
using Shouldly; |
|||
using Volo.Abp.Modularity; |
|||
using Xunit; |
|||
|
|||
namespace Volo.Abp.Identity |
|||
{ |
|||
public abstract class IdentityLinkUserRepository_Tests<TStartupModule> : AbpIdentityTestBase<TStartupModule> |
|||
where TStartupModule : IAbpModule |
|||
{ |
|||
protected IIdentityUserRepository UserRepository { get; } |
|||
protected IIdentityLinkUserRepository IdentityLinkUserRepository { get; } |
|||
protected IdentityTestData TestData { get; } |
|||
|
|||
public IdentityLinkUserRepository_Tests() |
|||
{ |
|||
UserRepository = GetRequiredService<IIdentityUserRepository>(); |
|||
IdentityLinkUserRepository = GetRequiredService<IIdentityLinkUserRepository>(); |
|||
TestData = GetRequiredService<IdentityTestData>(); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task FindAsync() |
|||
{ |
|||
var john = await UserRepository.GetAsync(TestData.UserJohnId); |
|||
var david = await UserRepository.GetAsync(TestData.UserDavidId); |
|||
var neo = await UserRepository.GetAsync(TestData.UserNeoId); |
|||
|
|||
var johnAndDavidLinkUser = await IdentityLinkUserRepository.FindAsync( |
|||
new IdentityLinkUserInfo(john.Id, john.TenantId), |
|||
new IdentityLinkUserInfo(david.Id, david.TenantId)); |
|||
|
|||
johnAndDavidLinkUser.ShouldNotBeNull(); |
|||
johnAndDavidLinkUser.SourceUserId.ShouldBe(john.Id); |
|||
johnAndDavidLinkUser.SourceTenantId.ShouldBe(john.TenantId); |
|||
johnAndDavidLinkUser.TargetUserId.ShouldBe(david.Id); |
|||
johnAndDavidLinkUser.TargetTenantId.ShouldBe(david.TenantId); |
|||
|
|||
(await IdentityLinkUserRepository.FindAsync( |
|||
new IdentityLinkUserInfo(john.Id, john.TenantId), |
|||
new IdentityLinkUserInfo(neo.Id, neo.TenantId))).ShouldBeNull(); |
|||
} |
|||
|
|||
[Fact] |
|||
public async Task GetListAsync() |
|||
{ |
|||
var john = await UserRepository.GetAsync(TestData.UserJohnId); |
|||
var david = await UserRepository.GetAsync(TestData.UserDavidId); |
|||
var neo = await UserRepository.GetAsync(TestData.UserNeoId); |
|||
|
|||
var davidLinkUsers = await IdentityLinkUserRepository.GetListAsync(new IdentityLinkUserInfo(david.Id, david.TenantId)); |
|||
davidLinkUsers.ShouldNotBeNull(); |
|||
|
|||
davidLinkUsers.ShouldContain(x => x.SourceUserId == john.Id && x.SourceTenantId == john.TenantId); |
|||
davidLinkUsers.ShouldContain(x => x.TargetUserId == neo.Id && x.TargetTenantId == neo.TenantId); |
|||
} |
|||
} |
|||
} |
|||
@ -0,0 +1,151 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Linq; |
|||
using System.Security.Claims; |
|||
using System.Threading.Tasks; |
|||
using IdentityServer4.Validation; |
|||
using Microsoft.Extensions.Localization; |
|||
using Microsoft.Extensions.Logging; |
|||
using Volo.Abp.Identity; |
|||
using Volo.Abp.IdentityServer.Localization; |
|||
using Volo.Abp.MultiTenancy; |
|||
using Volo.Abp.Security.Claims; |
|||
using Volo.Abp.Users; |
|||
using IdentityUser = Volo.Abp.Identity.IdentityUser; |
|||
|
|||
namespace Volo.Abp.IdentityServer.AspNetIdentity |
|||
{ |
|||
public class LinkLoginExtensionGrantValidator : IExtensionGrantValidator |
|||
{ |
|||
public const string ExtensionGrantType = "LinkLogin"; |
|||
|
|||
public string GrantType => ExtensionGrantType; |
|||
|
|||
protected ITokenValidator TokenValidator { get; } |
|||
protected IdentityLinkUserManager IdentityLinkUserManager { get; } |
|||
protected ICurrentTenant CurrentTenant { get; } |
|||
protected ICurrentUser CurrentUser { get; } |
|||
protected ICurrentPrincipalAccessor CurrentPrincipalAccessor { get; } |
|||
protected IdentityUserManager UserManager { get; } |
|||
protected IdentitySecurityLogManager IdentitySecurityLogManager { get; } |
|||
protected ILogger<LinkLoginExtensionGrantValidator> Logger { get; } |
|||
protected IStringLocalizer<AbpIdentityServerResource> Localizer { get; } |
|||
|
|||
public LinkLoginExtensionGrantValidator( |
|||
ITokenValidator tokenValidator, |
|||
IdentityLinkUserManager identityLinkUserManager, |
|||
ICurrentTenant currentTenant, |
|||
ICurrentUser currentUser, |
|||
IdentityUserManager userManager, |
|||
ICurrentPrincipalAccessor currentPrincipalAccessor, |
|||
IdentitySecurityLogManager identitySecurityLogManager, |
|||
ILogger<LinkLoginExtensionGrantValidator> logger, |
|||
IStringLocalizer<AbpIdentityServerResource> localizer) |
|||
{ |
|||
TokenValidator = tokenValidator; |
|||
IdentityLinkUserManager = identityLinkUserManager; |
|||
CurrentTenant = currentTenant; |
|||
CurrentUser = currentUser; |
|||
UserManager = userManager; |
|||
CurrentPrincipalAccessor = currentPrincipalAccessor; |
|||
IdentitySecurityLogManager = identitySecurityLogManager; |
|||
Logger = logger; |
|||
Localizer = localizer; |
|||
} |
|||
|
|||
public virtual async Task ValidateAsync(ExtensionGrantValidationContext context) |
|||
{ |
|||
var accessToken = context.Request.Raw["access_token"]; |
|||
if (accessToken.IsNullOrWhiteSpace()) |
|||
{ |
|||
context.Result = new GrantValidationResult |
|||
{ |
|||
IsError = true, |
|||
Error = "invalid_access_token" |
|||
}; |
|||
return; |
|||
} |
|||
|
|||
var result = await TokenValidator.ValidateAccessTokenAsync(accessToken); |
|||
if (result.IsError) |
|||
{ |
|||
context.Result = new GrantValidationResult |
|||
{ |
|||
IsError = true, |
|||
Error = result.Error, |
|||
ErrorDescription = result.ErrorDescription |
|||
}; |
|||
return; |
|||
} |
|||
|
|||
using (CurrentPrincipalAccessor.Change(result.Claims)) |
|||
{ |
|||
if (!Guid.TryParse(context.Request.Raw["LinkUserId"], out var linkUserId)) |
|||
{ |
|||
context.Result = new GrantValidationResult |
|||
{ |
|||
IsError = true, |
|||
Error = "invalid_link_user_id" |
|||
}; |
|||
return; |
|||
} |
|||
|
|||
Guid? linkTenantId = null; |
|||
if (!context.Request.Raw["LinkTenantId"].IsNullOrWhiteSpace()) |
|||
{ |
|||
if (!Guid.TryParse(context.Request.Raw["LinkTenantId"], out var parsedGuid)) |
|||
{ |
|||
context.Result = new GrantValidationResult |
|||
{ |
|||
IsError = true, |
|||
Error = "invalid_link_tenant_id" |
|||
}; |
|||
return; |
|||
} |
|||
|
|||
linkTenantId = parsedGuid; |
|||
} |
|||
|
|||
var isLinked = await IdentityLinkUserManager.IsLinkedAsync( |
|||
new IdentityLinkUserInfo(CurrentUser.GetId(), CurrentTenant.Id), |
|||
new IdentityLinkUserInfo(linkUserId, linkTenantId)); |
|||
|
|||
if (isLinked) |
|||
{ |
|||
using (CurrentTenant.Change(linkTenantId)) |
|||
{ |
|||
var user = await UserManager.GetByIdAsync(linkUserId); |
|||
var sub = await UserManager.GetUserIdAsync(user); |
|||
|
|||
var additionalClaims = new List<Claim>(); |
|||
await AddCustomClaimsAsync(additionalClaims, user, context); |
|||
|
|||
context.Result = new GrantValidationResult( |
|||
sub, |
|||
GrantType, |
|||
additionalClaims.ToArray() |
|||
); |
|||
} |
|||
} |
|||
else |
|||
{ |
|||
context.Result = new GrantValidationResult |
|||
{ |
|||
IsError = true, |
|||
Error = Localizer["TheTargetUserIsNotLinkedToYou"] |
|||
}; |
|||
} |
|||
} |
|||
} |
|||
|
|||
protected virtual Task AddCustomClaimsAsync(List<Claim> customClaims, IdentityUser user, ExtensionGrantValidationContext context) |
|||
{ |
|||
if (user.TenantId.HasValue) |
|||
{ |
|||
customClaims.Add(new Claim(AbpClaimTypes.TenantId, user.TenantId?.ToString())); |
|||
} |
|||
|
|||
return Task.CompletedTask; |
|||
} |
|||
} |
|||
} |
|||
Loading…
Reference in new issue