Skip to content

Commit

Permalink
Merge pull request #7953 from abpframework/maliming/Linked-user-enhan…
Browse files Browse the repository at this point in the history
…cements

Link users enhancements.
  • Loading branch information
ismcagdas committed Mar 17, 2021
2 parents 6908518 + f581319 commit 1c1544c
Show file tree
Hide file tree
Showing 12 changed files with 335 additions and 67 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public override void PreConfigureServices(ServiceConfigurationContext context)
{
builder
.AddDefaultTokenProviders()
.AddTokenProvider<LinkUserTokenProvider>(LinkUserTokenProviderConsts.LinkUserTokenProviderName)
.AddSignInManager<AbpSignInManager>();
});
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

namespace Volo.Abp.Identity.AspNetCore
{
public class LinkUserTokenProvider : DataProtectorTokenProvider<IdentityUser>
{
public LinkUserTokenProvider(
IDataProtectionProvider dataProtectionProvider,
IOptions<DataProtectionTokenProviderOptions> options,
ILogger<DataProtectorTokenProvider<IdentityUser>> logger)
: base(dataProtectionProvider, options, logger)
{

}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
namespace Volo.Abp.Identity
{
public static class LinkUserTokenProviderConsts
{
public static string LinkUserTokenProviderName { get; set; } = "AbpLinkUser";

public static string LinkUserTokenPurpose { get; set; } = "AbpLinkUserLogin";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,7 @@ public static IdentityBuilder AddAbpIdentity(this IServiceCollection services, A
return services
.AddIdentityCore<IdentityUser>(setupAction)
.AddRoles<IdentityRole>()
.AddClaimsPrincipalFactory<AbpUserClaimsPrincipalFactory>()
.AddTokenProvider<LinkUserTokenProvider>(LinkUserTokenProvider.LinkUserTokenProviderName);
.AddClaimsPrincipalFactory<AbpUserClaimsPrincipalFactory>();
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
Expand All @@ -14,6 +14,11 @@ public interface IIdentityLinkUserRepository : IBasicRepository<IdentityLinkUser
CancellationToken cancellationToken = default);

Task<List<IdentityLinkUser>> GetListAsync(
IdentityLinkUserInfo linkUserInfo,
List<IdentityLinkUserInfo> excludes = null,
CancellationToken cancellationToken = default);

Task DeleteAsync(
IdentityLinkUserInfo linkUserInfo,
CancellationToken cancellationToken = default);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Encodings.Web;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.Domain.Services;
using Volo.Abp.MultiTenancy;
Expand All @@ -20,74 +23,128 @@ public IdentityLinkUserManager(IIdentityLinkUserRepository identityLinkUserRepos
CurrentTenant = currentTenant;
}

public virtual async Task LinkAsync(IdentityLinkUserInfo sourceLinkUser, IdentityLinkUserInfo targetLinkUser)
public async Task<List<IdentityLinkUser>> GetListAsync(IdentityLinkUserInfo linkUserInfo, bool includeIndirect = false, CancellationToken cancellationToken = default)
{
if (sourceLinkUser.UserId == targetLinkUser.UserId && sourceLinkUser.TenantId == targetLinkUser.TenantId)
using (CurrentTenant.Change(null))
{
return;
}
var users = await IdentityLinkUserRepository.GetListAsync(linkUserInfo, cancellationToken: cancellationToken);
if (includeIndirect == false)
{
return users;
}

if (await IsLinkedAsync(sourceLinkUser, targetLinkUser))
{
return;
var userInfos = new List<IdentityLinkUserInfo>()
{
linkUserInfo
};

var allUsers = new List<IdentityLinkUser>();
allUsers.AddRange(users);

do
{
var nextUsers = new List<IdentityLinkUserInfo>();
foreach (var user in users)
{
if (userInfos.Any(x => x.TenantId != user.SourceTenantId || x.UserId != user.SourceUserId))
{
nextUsers.Add(new IdentityLinkUserInfo(user.SourceUserId, user.SourceTenantId));
}

if (userInfos.Any(x => x.TenantId != user.TargetTenantId || x.UserId != user.TargetUserId))
{
nextUsers.Add(new IdentityLinkUserInfo(user.TargetUserId, user.TargetTenantId));
}
}

users = new List<IdentityLinkUser>();
foreach (var next in nextUsers)
{
users.AddRange(await IdentityLinkUserRepository.GetListAsync(next, userInfos, cancellationToken));
}

userInfos.AddRange(nextUsers);
allUsers.AddRange(users);
} while (users.Any());

return allUsers;
}
}

public virtual async Task LinkAsync(IdentityLinkUserInfo sourceLinkUser, IdentityLinkUserInfo targetLinkUser, CancellationToken cancellationToken = default)
{
using (CurrentTenant.Change(null))
{
if (sourceLinkUser.UserId == targetLinkUser.UserId && sourceLinkUser.TenantId == targetLinkUser.TenantId)
{
return;
}

if (await IsLinkedAsync(sourceLinkUser, targetLinkUser, cancellationToken: cancellationToken))
{
return;
}

var userLink = new IdentityLinkUser(
GuidGenerator.Create(),
sourceLinkUser,
targetLinkUser);
await IdentityLinkUserRepository.InsertAsync(userLink, true);
await IdentityLinkUserRepository.InsertAsync(userLink, true, cancellationToken);
}
}

public virtual async Task<bool> IsLinkedAsync(IdentityLinkUserInfo sourceLinkUser, IdentityLinkUserInfo targetLinkUser)
public virtual async Task<bool> IsLinkedAsync(IdentityLinkUserInfo sourceLinkUser, IdentityLinkUserInfo targetLinkUser, bool includeIndirect = false, CancellationToken cancellationToken = default)
{
using (CurrentTenant.Change(null))
{
return await IdentityLinkUserRepository.FindAsync(sourceLinkUser, targetLinkUser) != null;
if (includeIndirect)
{
return (await GetListAsync(sourceLinkUser, true, cancellationToken: cancellationToken))
.Any(x => x.SourceTenantId == targetLinkUser.TenantId && x.SourceUserId == targetLinkUser.UserId ||
x.TargetTenantId == targetLinkUser.TenantId && x.TargetUserId == targetLinkUser.UserId);
}
return await IdentityLinkUserRepository.FindAsync(sourceLinkUser, targetLinkUser, cancellationToken) != null;
}
}

public virtual async Task UnlinkAsync(IdentityLinkUserInfo sourceLinkUser, IdentityLinkUserInfo targetLinkUser)
public virtual async Task UnlinkAsync(IdentityLinkUserInfo sourceLinkUser, IdentityLinkUserInfo targetLinkUser, CancellationToken cancellationToken = default)
{
if (!await IsLinkedAsync(sourceLinkUser, targetLinkUser))
{
return;
}

using (CurrentTenant.Change(null))
{
var linkedUser = await IdentityLinkUserRepository.FindAsync(sourceLinkUser, targetLinkUser);
if (!await IsLinkedAsync(sourceLinkUser, targetLinkUser, cancellationToken: cancellationToken))
{
return;
}

var linkedUser = await IdentityLinkUserRepository.FindAsync(sourceLinkUser, targetLinkUser, cancellationToken);
if (linkedUser != null)
{
await IdentityLinkUserRepository.DeleteAsync(linkedUser);
await IdentityLinkUserRepository.DeleteAsync(linkedUser, cancellationToken: cancellationToken);
}
}
}

public virtual async Task<string> GenerateLinkTokenAsync(IdentityLinkUserInfo targetLinkUser)
public virtual async Task<string> GenerateLinkTokenAsync(IdentityLinkUserInfo targetLinkUser, CancellationToken cancellationToken = default)
{
using (CurrentTenant.Change(targetLinkUser.TenantId))
{
var user = await UserManager.GetByIdAsync(targetLinkUser.UserId);
return await UserManager.GenerateUserTokenAsync(
user,
LinkUserTokenProvider.LinkUserTokenProviderName,
LinkUserTokenProvider.LinkUserTokenPurpose);
LinkUserTokenProviderConsts.LinkUserTokenProviderName,
LinkUserTokenProviderConsts.LinkUserTokenPurpose);
}
}

public virtual async Task<bool> VerifyLinkTokenAsync(IdentityLinkUserInfo targetLinkUser, string token)
public virtual async Task<bool> VerifyLinkTokenAsync(IdentityLinkUserInfo targetLinkUser, string token, CancellationToken cancellationToken = default)
{
using (CurrentTenant.Change(targetLinkUser.TenantId))
{
var user = await UserManager.GetByIdAsync(targetLinkUser.UserId);
return await UserManager.VerifyUserTokenAsync(
user,
LinkUserTokenProvider.LinkUserTokenProviderName,
LinkUserTokenProvider.LinkUserTokenPurpose,
LinkUserTokenProviderConsts.LinkUserTokenProviderName,
LinkUserTokenProviderConsts.LinkUserTokenPurpose,
token);
}
}
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
Expand Down Expand Up @@ -28,13 +28,35 @@ public virtual async Task<IdentityLinkUser> FindAsync(IdentityLinkUserInfo sourc
, cancellationToken: GetCancellationToken(cancellationToken));
}

public virtual async Task<List<IdentityLinkUser>> GetListAsync(IdentityLinkUserInfo linkUserInfo, CancellationToken cancellationToken = default)
public virtual async Task<List<IdentityLinkUser>> GetListAsync(IdentityLinkUserInfo linkUserInfo, List<IdentityLinkUserInfo> excludes = null,
CancellationToken cancellationToken = default)
{
return await (await GetDbSetAsync())
IQueryable<IdentityLinkUser> query = (await GetDbSetAsync())
.Where(x =>
x.SourceUserId == linkUserInfo.UserId && x.SourceTenantId == linkUserInfo.TenantId ||
x.TargetUserId == linkUserInfo.UserId && x.TargetTenantId == linkUserInfo.TenantId);

if (!excludes.IsNullOrEmpty())
{
foreach (var userInfo in excludes)
{
query = query.Where(x =>
(x.SourceTenantId != userInfo.TenantId || x.SourceUserId != userInfo.UserId) &&
(x.TargetTenantId != userInfo.TenantId || x.TargetUserId != userInfo.UserId));
}
}

return await query.ToListAsync(cancellationToken: GetCancellationToken(cancellationToken));
}

public virtual async Task DeleteAsync(IdentityLinkUserInfo linkUserInfo, CancellationToken cancellationToken = default)
{
var linkUsers = await (await GetDbSetAsync()).Where(x =>
x.SourceUserId == linkUserInfo.UserId && x.SourceTenantId == linkUserInfo.TenantId ||
x.TargetUserId == linkUserInfo.UserId && x.TargetTenantId == linkUserInfo.TenantId)
.ToListAsync(cancellationToken: GetCancellationToken(cancellationToken));

await DeleteManyAsync(linkUsers, cancellationToken: cancellationToken);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
using System;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Dynamic.Core;
using System.Threading;
using System.Threading.Tasks;
using MongoDB.Driver;
Expand All @@ -28,12 +27,34 @@ public virtual async Task<IdentityLinkUser> FindAsync(IdentityLinkUserInfo sourc
, cancellationToken: GetCancellationToken(cancellationToken));
}

public virtual async Task<List<IdentityLinkUser>> GetListAsync(IdentityLinkUserInfo linkUserInfo, CancellationToken cancellationToken = default)
public virtual async Task<List<IdentityLinkUser>> GetListAsync(IdentityLinkUserInfo linkUserInfo, List<IdentityLinkUserInfo> excludes = null,
CancellationToken cancellationToken = default)
{
return await (await GetMongoQueryableAsync(cancellationToken)).Where(x =>
var query = (await GetMongoQueryableAsync(cancellationToken)).Where(x =>
x.SourceUserId == linkUserInfo.UserId && x.SourceTenantId == linkUserInfo.TenantId ||
x.TargetUserId == linkUserInfo.UserId && x.TargetTenantId == linkUserInfo.TenantId);

if (!excludes.IsNullOrEmpty())
{
foreach (var userInfo in excludes)
{
query = query.Where(x =>
(x.SourceTenantId != userInfo.TenantId || x.SourceUserId != userInfo.UserId) &&
(x.TargetTenantId != userInfo.TenantId || x.TargetUserId != userInfo.UserId));
}
}

return await query.ToListAsync(cancellationToken: GetCancellationToken(cancellationToken));
}

public virtual async Task DeleteAsync(IdentityLinkUserInfo linkUserInfo, CancellationToken cancellationToken = default)
{
var linkUsers = await (await GetMongoQueryableAsync(cancellationToken)).Where(x =>
x.SourceUserId == linkUserInfo.UserId && x.SourceTenantId == linkUserInfo.TenantId ||
x.TargetUserId == linkUserInfo.UserId && x.TargetTenantId == linkUserInfo.TenantId)
.ToListAsync(cancellationToken: GetCancellationToken(cancellationToken));

await DeleteManyAsync(linkUsers, cancellationToken: cancellationToken);
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Microsoft.AspNetCore.Identity;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Options;
using Shouldly;
using Xunit;
Expand All @@ -7,14 +8,37 @@ namespace Volo.Abp.Identity.AspNetCore
{
public class LinkUserTokenProvider_Tests : AbpIdentityAspNetCoreTestBase
{
protected IIdentityUserRepository UserRepository { get; }
protected IIdentityLinkUserRepository IdentityLinkUserRepository { get; }
protected IdentityLinkUserManager IdentityLinkUserManager { get; }
protected IdentityTestData TestData { get; }

public LinkUserTokenProvider_Tests()
{
UserRepository = GetRequiredService<IIdentityUserRepository>();
IdentityLinkUserRepository = GetRequiredService<IIdentityLinkUserRepository>();
IdentityLinkUserManager = GetRequiredService<IdentityLinkUserManager>();
TestData = GetRequiredService<IdentityTestData>();
}

[Fact]
public void LinkUserTokenProvider_Should_Be_Register()
{
var identityOptions = GetRequiredService<IOptions<IdentityOptions>>().Value;

identityOptions.Tokens.ProviderMap.ShouldContain(x =>
x.Key == LinkUserTokenProvider.LinkUserTokenProviderName &&
x.Key == LinkUserTokenProviderConsts.LinkUserTokenProviderName &&
x.Value.ProviderType == typeof(LinkUserTokenProvider));
}

[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 comments on commit 1c1544c

Please sign in to comment.