Add `IdentitySessionManager`.

pull/18242/head
maliming 2 years ago
parent 66ebf079f7
commit 3d91733bc1
No known key found for this signature in database
GPG Key ID: A646B9CB645ECEA4

@ -276,4 +276,37 @@ public static class AbpClaimsIdentityExtensions
return principal;
}
public static Guid? FindSessionId([NotNull] this IIdentity identity)
{
Check.NotNull(identity, nameof(identity));
var claimsIdentity = identity as ClaimsIdentity;
var sessionIdOrNull = claimsIdentity?.Claims?.FirstOrDefault(c => c.Type == AbpClaimTypes.SessionId);
if (sessionIdOrNull == null || sessionIdOrNull.Value.IsNullOrWhiteSpace())
{
return null;
}
if (Guid.TryParse(sessionIdOrNull.Value, out var guid))
{
return guid;
}
return null;
}
public static string? FindSessionId([NotNull] this ClaimsPrincipal principal)
{
Check.NotNull(principal, nameof(principal));
var sessionIdOrNull = principal.Claims?.FirstOrDefault(c => c.Type == AbpClaimTypes.SessionId);
if (sessionIdOrNull == null || sessionIdOrNull.Value.IsNullOrWhiteSpace())
{
return null;
}
return sessionIdOrNull.Value;
}
}

@ -87,9 +87,15 @@ public static class AbpClaimTypes
/// Default: "impersonator_username".
/// </summary>
public static string ImpersonatorUserName { get; set; } = "impersonator_username";
/// <summary>
/// Default: "picture".
/// </summary>
public static string Picture { get; set; } = "picture";
/// <summary>
/// Default: "session_id".
/// </summary>
public static string SessionId { get; set; } = "session_id";
}

@ -70,4 +70,26 @@ public static class CurrentUserExtensions
{
return currentUser.FindClaimValue(AbpClaimTypes.ImpersonatorUserName);
}
public static Guid GetSessionId([NotNull] this ICurrentUser currentUser)
{
var sessionId = currentUser.FindSessionId();
Debug.Assert(sessionId != null, "sessionId != null");
return sessionId!.Value;
}
public static Guid? FindSessionId([NotNull] this ICurrentUser currentUser)
{
var sessionId = currentUser.FindClaimValue(AbpClaimTypes.SessionId);
if (sessionId.IsNullOrWhiteSpace())
{
return null;
}
if (Guid.TryParse(sessionId, out var guid))
{
return guid;
}
return null;
}
}

@ -2,7 +2,11 @@ namespace Volo.Abp.Identity;
public class IdentitySessionConsts
{
public static int MaxDeviceLength { get; set; } = 128;
public static int MaxSessionIdLength { get; set; } = 128;
public static int MaxDeviceLength { get; set; } = 64;
public static int MaxDeviceInfoLength { get; set; } = 64;
public static int MaxClientIdLength { get; set; } = 64;

@ -0,0 +1,8 @@
namespace Volo.Abp.Identity;
public static class IdentitySessionDevices
{
public const string Web = "Web";
public const string OAuth = "OAuth";
}

@ -1,9 +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 IIdentitySessionRepository : IBasicRepository<IdentitySession, Guid>
{
Task<IdentitySession> FindAsync(string sessionId, CancellationToken cancellationToken = default);
Task<IdentitySession> GetAsync(string sessionId, CancellationToken cancellationToken = default);
Task<List<IdentitySession>> GetListAsync(Guid userId, CancellationToken cancellationToken = default);
Task DeleteAllAsync(Guid userId, Guid? exceptSessionId = null, CancellationToken cancellationToken = default);
Task DeleteAllAsync(Guid userId, string device, Guid? exceptSessionId = null, CancellationToken cancellationToken = default);
}

@ -1,16 +1,21 @@
using System;
using System.Collections.Generic;
using Volo.Abp.Domain.Entities;
using Volo.Abp.MultiTenancy;
namespace Volo.Abp.Identity;
public class IdentitySession : BasicAggregateRoot<Guid>
public class IdentitySession : BasicAggregateRoot<Guid>, IMultiTenant
{
public virtual string SessionId { get; protected set; }
/// <summary>
/// Web, CLI, STUDIO, ...
/// Web, OAuth ...
/// </summary>
public virtual string Device { get; protected set; }
public virtual string DeviceInfo { get; protected set; }
public virtual Guid? TenantId { get; protected set; }
public virtual Guid UserId { get; protected set; }
@ -30,16 +35,20 @@ public class IdentitySession : BasicAggregateRoot<Guid>
public IdentitySession(
Guid id,
string sessionId,
string device,
string deviceInfo,
Guid userId,
Guid? tenantId,
string clientId,
string ipAddresses,
DateTime signedIn,
DateTime? lastAccessed)
DateTime? lastAccessed = null)
{
Id = id;
SessionId = sessionId;
Device = device;
DeviceInfo = deviceInfo;
UserId = userId;
TenantId = tenantId;
ClientId = clientId;

@ -0,0 +1,98 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Volo.Abp.Domain.Services;
using Volo.Abp.Users;
namespace Volo.Abp.Identity;
public class IdentitySessionManager : DomainService
{
protected IIdentitySessionRepository IdentitySessionRepository { get; }
protected ICurrentUser CurrentUser { get; }
public IdentitySessionManager(IIdentitySessionRepository identitySessionRepository, ICurrentUser currentUser)
{
IdentitySessionRepository = identitySessionRepository;
CurrentUser = currentUser;
}
public virtual async Task<IdentitySession> CreateAsync(
string sessionId,
string device,
string deviceInfo,
Guid userId,
Guid? tenantId,
string clientId,
string ipAddresses)
{
Check.NotNullOrWhiteSpace(sessionId, nameof(sessionId));
Check.NotNullOrWhiteSpace(device, nameof(device));
var session = new IdentitySession(
GuidGenerator.Create(),
sessionId,
device,
deviceInfo,
userId,
tenantId,
clientId,
ipAddresses,
Clock.Now
);
return await IdentitySessionRepository.InsertAsync(session);
}
public virtual async Task UpdateAsync(IdentitySession session)
{
await IdentitySessionRepository.UpdateAsync(session);
}
public virtual async Task<List<IdentitySession>> GetListAsync(Guid userId)
{
return await IdentitySessionRepository.GetListAsync(userId);
}
public virtual async Task<IdentitySession> GetAsync(Guid id)
{
return await IdentitySessionRepository.GetAsync(id);
}
public virtual async Task<IdentitySession> FindAsync(Guid id)
{
return await IdentitySessionRepository.FindAsync(id);
}
public virtual async Task<IdentitySession> GetAsync(string sessionId)
{
return await IdentitySessionRepository.GetAsync(sessionId);
}
public virtual async Task<IdentitySession> FindAsync(string sessionId)
{
return await IdentitySessionRepository.FindAsync(sessionId);
}
public virtual async Task RevokeAsync(Guid id)
{
var session = await IdentitySessionRepository.GetAsync(id);
await IdentitySessionRepository.DeleteAsync(session);
}
public virtual async Task RevokeAsync(string sessionId)
{
var session = await IdentitySessionRepository.GetAsync(sessionId);
await IdentitySessionRepository.DeleteAsync(session);
}
public virtual async Task RevokeAllAsync(Guid userId, Guid? exceptSessionId = null)
{
await IdentitySessionRepository.DeleteAllAsync(userId, exceptSessionId);
}
public virtual async Task RevokeAllAsync(Guid userId, string device, Guid? exceptSessionId = null)
{
await IdentitySessionRepository.DeleteAllAsync(userId, device, exceptSessionId);
}
}

@ -1,4 +1,11 @@
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.Entities;
using Volo.Abp.Domain.Repositories.EntityFrameworkCore;
using Volo.Abp.EntityFrameworkCore;
@ -12,4 +19,34 @@ public class EfCoreIdentitySessionRepository : EfCoreRepository<IIdentityDbConte
}
public virtual async Task<IdentitySession> FindAsync(string sessionId, CancellationToken cancellationToken = default)
{
return await (await GetDbSetAsync()).FirstOrDefaultAsync(x => x.SessionId == sessionId, GetCancellationToken(cancellationToken));
}
public virtual async Task<IdentitySession> GetAsync(string sessionId, CancellationToken cancellationToken = default)
{
var session = await FindAsync(sessionId, cancellationToken);
if (session == null)
{
throw new EntityNotFoundException(typeof(IdentitySession));
}
return session;
}
public virtual async Task<List<IdentitySession>> GetListAsync(Guid userId, CancellationToken cancellationToken = default)
{
return await (await GetDbSetAsync()).Where(x => x.UserId == userId).ToListAsync(GetCancellationToken(cancellationToken));
}
public virtual async Task DeleteAllAsync(Guid userId, Guid? exceptSessionId = null, CancellationToken cancellationToken = default)
{
await DeleteDirectAsync(x => x.UserId == userId && x.Id != exceptSessionId, cancellationToken);
}
public virtual async Task DeleteAllAsync(Guid userId, string device, Guid? exceptSessionId = null, CancellationToken cancellationToken = default)
{
await DeleteDirectAsync(x => x.UserId == userId && x.Device == device && x.Id != exceptSessionId, cancellationToken);
}
}

@ -285,11 +285,14 @@ public static class IdentityDbContextModelBuilderExtensions
b.ConfigureByConvention();
b.Property(x => x.SessionId).HasMaxLength(IdentitySessionConsts.MaxSessionIdLength).IsRequired();
b.Property(x => x.Device).HasMaxLength(IdentitySessionConsts.MaxDeviceLength).IsRequired();
b.Property(x => x.DeviceInfo).HasMaxLength(IdentitySessionConsts.MaxDeviceInfoLength);
b.Property(x => x.ClientId).HasMaxLength(IdentitySessionConsts.MaxClientIdLength);
b.Property(x => x.IpAddresses).HasMaxLength(IdentitySessionConsts.MaxIpAddressesLength);
b.HasIndex(x => new { x.Device });
b.HasIndex(x => x.SessionId);
b.HasIndex(x => x.Device );
b.HasIndex(x => new { x.TenantId, x.UserId });
b.ApplyObjectExtensionMappings();

@ -1,4 +1,10 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using MongoDB.Driver;
using MongoDB.Driver.Linq;
using Volo.Abp.Domain.Entities;
using Volo.Abp.Domain.Repositories.MongoDB;
using Volo.Abp.MongoDB;
@ -11,4 +17,39 @@ public class MongoIdentitySessionRepository : MongoDbRepository<IAbpIdentityMong
{
}
public virtual async Task<IdentitySession> FindAsync(string sessionId, CancellationToken cancellationToken = default)
{
return await (await GetMongoQueryableAsync(GetCancellationToken(cancellationToken)))
.As<IMongoQueryable<IdentitySession>>()
.FirstOrDefaultAsync(x => x.SessionId == sessionId, GetCancellationToken(cancellationToken));
}
public virtual async Task<IdentitySession> GetAsync(string sessionId, CancellationToken cancellationToken = default)
{
var session = await FindAsync(sessionId, cancellationToken);
if (session == null)
{
throw new EntityNotFoundException(typeof(IdentitySession));
}
return session;
}
public virtual async Task<List<IdentitySession>> GetListAsync(Guid userId, CancellationToken cancellationToken = default)
{
return await (await GetMongoQueryableAsync(GetCancellationToken(cancellationToken)))
.As<IMongoQueryable<IdentitySession>>()
.Where(x => x.UserId == userId).ToListAsync(GetCancellationToken(cancellationToken));
}
public virtual async Task DeleteAllAsync(Guid userId, Guid? exceptSessionId = null, CancellationToken cancellationToken = default)
{
await DeleteDirectAsync(x => x.UserId == userId && x.Id != exceptSessionId, cancellationToken);
}
public virtual async Task DeleteAllAsync(Guid userId, string device, Guid? exceptSessionId = null, CancellationToken cancellationToken = default)
{
await DeleteDirectAsync(x => x.UserId == userId && x.Device == device && x.Id != exceptSessionId, cancellationToken);
}
}

Loading…
Cancel
Save