|
|
|
@ -35,6 +35,7 @@ public sealed class MongoUserStore(IMongoDatabase database) : |
|
|
|
private const string InternalLoginProvider = "[AspNetUserStore]"; |
|
|
|
private const string AuthenticatorKeyTokenName = "AuthenticatorKey"; |
|
|
|
private const string RecoveryCodeTokenName = "RecoveryCodes"; |
|
|
|
private IMongoCollection<IdentityUser> queryableCollection; |
|
|
|
|
|
|
|
static MongoUserStore() |
|
|
|
{ |
|
|
|
@ -51,11 +52,11 @@ public sealed class MongoUserStore(IMongoDatabase database) : |
|
|
|
parameters[1].Name == "value" && |
|
|
|
parameters[1].ParameterType == typeof(string); |
|
|
|
})) |
|
|
|
.SetArguments(new[] |
|
|
|
{ |
|
|
|
.SetArguments( |
|
|
|
[ |
|
|
|
nameof(Claim.Type), |
|
|
|
nameof(Claim.Value), |
|
|
|
}); |
|
|
|
]); |
|
|
|
|
|
|
|
cm.MapMember(x => x.Type); |
|
|
|
cm.MapMember(x => x.Value); |
|
|
|
@ -70,12 +71,12 @@ public sealed class MongoUserStore(IMongoDatabase database) : |
|
|
|
|
|
|
|
return parameters.Length == 3; |
|
|
|
})) |
|
|
|
.SetArguments(new[] |
|
|
|
{ |
|
|
|
.SetArguments( |
|
|
|
[ |
|
|
|
nameof(UserLogin.LoginProvider), |
|
|
|
nameof(UserLogin.ProviderKey), |
|
|
|
nameof(UserLogin.ProviderDisplayName), |
|
|
|
}); |
|
|
|
]); |
|
|
|
|
|
|
|
cm.AutoMap(); |
|
|
|
}); |
|
|
|
@ -121,6 +122,8 @@ public sealed class MongoUserStore(IMongoDatabase database) : |
|
|
|
cm.MapMember(x => x.TwoFactorEnabled) |
|
|
|
.SetIgnoreIfDefault(true); |
|
|
|
}); |
|
|
|
|
|
|
|
BsonSerializer.TryRegisterSerializer(new IdentityUserForwardingSerializer()); |
|
|
|
} |
|
|
|
|
|
|
|
protected override string CollectionName() |
|
|
|
@ -165,7 +168,7 @@ public sealed class MongoUserStore(IMongoDatabase database) : |
|
|
|
|
|
|
|
public IQueryable<IdentityUser> Users |
|
|
|
{ |
|
|
|
get => Collection.AsQueryable(); |
|
|
|
get => (queryableCollection ??= Database.GetCollection<IdentityUser>(CollectionName())).AsQueryable(); |
|
|
|
} |
|
|
|
|
|
|
|
public bool IsId(string id) |
|
|
|
@ -181,7 +184,9 @@ public sealed class MongoUserStore(IMongoDatabase database) : |
|
|
|
public async Task<IdentityUser?> FindByIdAsync(string userId, |
|
|
|
CancellationToken cancellationToken) |
|
|
|
{ |
|
|
|
var result = await Collection.Find(x => x.Id == userId).FirstOrDefaultAsync(cancellationToken); |
|
|
|
var result = |
|
|
|
await Collection.Find(x => x.Id == userId) |
|
|
|
.FirstOrDefaultAsync(cancellationToken); |
|
|
|
|
|
|
|
return result; |
|
|
|
} |
|
|
|
@ -189,7 +194,9 @@ public sealed class MongoUserStore(IMongoDatabase database) : |
|
|
|
public async Task<IdentityUser?> FindByEmailAsync(string normalizedEmail, |
|
|
|
CancellationToken cancellationToken) |
|
|
|
{ |
|
|
|
var result = await Collection.Find(x => x.NormalizedEmail == normalizedEmail).FirstOrDefaultAsync(cancellationToken); |
|
|
|
var result = |
|
|
|
await Collection.Find(x => x.NormalizedEmail == normalizedEmail) |
|
|
|
.FirstOrDefaultAsync(cancellationToken); |
|
|
|
|
|
|
|
return result; |
|
|
|
} |
|
|
|
@ -197,7 +204,9 @@ public sealed class MongoUserStore(IMongoDatabase database) : |
|
|
|
public async Task<IdentityUser?> FindByNameAsync(string normalizedUserName, |
|
|
|
CancellationToken cancellationToken) |
|
|
|
{ |
|
|
|
var result = await Collection.Find(x => x.NormalizedEmail == normalizedUserName).FirstOrDefaultAsync(cancellationToken); |
|
|
|
var result = |
|
|
|
await Collection.Find(x => x.NormalizedEmail == normalizedUserName) |
|
|
|
.FirstOrDefaultAsync(cancellationToken); |
|
|
|
|
|
|
|
return result; |
|
|
|
} |
|
|
|
@ -205,7 +214,9 @@ public sealed class MongoUserStore(IMongoDatabase database) : |
|
|
|
public async Task<IdentityUser?> FindByLoginAsync(string loginProvider, string providerKey, |
|
|
|
CancellationToken cancellationToken) |
|
|
|
{ |
|
|
|
var result = await Collection.Find(Filter.ElemMatch(x => x.Logins, BuildFilter(loginProvider, providerKey))).FirstOrDefaultAsync(cancellationToken); |
|
|
|
var result = |
|
|
|
await Collection.Find(Filter.ElemMatch(x => x.Logins, BuildFilter(loginProvider, providerKey))) |
|
|
|
.FirstOrDefaultAsync(cancellationToken); |
|
|
|
|
|
|
|
return result; |
|
|
|
} |
|
|
|
@ -213,7 +224,9 @@ public sealed class MongoUserStore(IMongoDatabase database) : |
|
|
|
public async Task<IList<IdentityUser>> GetUsersForClaimAsync(Claim claim, |
|
|
|
CancellationToken cancellationToken) |
|
|
|
{ |
|
|
|
var result = await Collection.Find(x => x.Claims.Exists(y => y.Type == claim.Type && y.Value == claim.Value)).ToListAsync(cancellationToken); |
|
|
|
var result = |
|
|
|
await Collection.Find(x => x.Claims.Exists(y => y.Type == claim.Type && y.Value == claim.Value)) |
|
|
|
.ToListAsync(cancellationToken); |
|
|
|
|
|
|
|
return result.OfType<IdentityUser>().ToList(); |
|
|
|
} |
|
|
|
@ -221,7 +234,9 @@ public sealed class MongoUserStore(IMongoDatabase database) : |
|
|
|
public async Task<IList<IdentityUser>> GetUsersInRoleAsync(string roleName, |
|
|
|
CancellationToken cancellationToken) |
|
|
|
{ |
|
|
|
var result = await Collection.Find(x => x.Roles.Contains(roleName)).ToListAsync(cancellationToken); |
|
|
|
var result = |
|
|
|
await Collection.Find(x => x.Roles.Contains(roleName)) |
|
|
|
.ToListAsync(cancellationToken); |
|
|
|
|
|
|
|
return result.OfType<IdentityUser>().ToList(); |
|
|
|
} |
|
|
|
@ -231,7 +246,8 @@ public sealed class MongoUserStore(IMongoDatabase database) : |
|
|
|
{ |
|
|
|
user.Id = ObjectId.GenerateNewId().ToString(); |
|
|
|
|
|
|
|
await Collection.InsertOneAsync((MongoUser)user, null, cancellationToken); |
|
|
|
await Collection.InsertOneAsync((MongoUser)user, null, |
|
|
|
cancellationToken); |
|
|
|
|
|
|
|
return IdentityResult.Success; |
|
|
|
} |
|
|
|
@ -239,7 +255,8 @@ public sealed class MongoUserStore(IMongoDatabase database) : |
|
|
|
public async Task<IdentityResult> UpdateAsync(IdentityUser user, |
|
|
|
CancellationToken cancellationToken) |
|
|
|
{ |
|
|
|
await Collection.ReplaceOneAsync(x => x.Id == user.Id, (MongoUser)user, cancellationToken: cancellationToken); |
|
|
|
await Collection.ReplaceOneAsync(x => x.Id == user.Id, (MongoUser)user, |
|
|
|
cancellationToken: cancellationToken); |
|
|
|
|
|
|
|
return IdentityResult.Success; |
|
|
|
} |
|
|
|
@ -247,7 +264,8 @@ public sealed class MongoUserStore(IMongoDatabase database) : |
|
|
|
public async Task<IdentityResult> DeleteAsync(IdentityUser user, |
|
|
|
CancellationToken cancellationToken) |
|
|
|
{ |
|
|
|
await Collection.DeleteOneAsync(x => x.Id == user.Id, null, cancellationToken); |
|
|
|
await Collection.DeleteOneAsync(x => x.Id == user.Id, null, |
|
|
|
cancellationToken); |
|
|
|
|
|
|
|
return IdentityResult.Success; |
|
|
|
} |
|
|
|
@ -654,4 +672,29 @@ public sealed class MongoUserStore(IMongoDatabase database) : |
|
|
|
filter.Eq(x => x.LoginProvider, loginProvider), |
|
|
|
filter.Eq(x => x.ProviderKey, providerKey)); |
|
|
|
} |
|
|
|
|
|
|
|
private sealed class IdentityUserForwardingSerializer |
|
|
|
: SerializerBase<IdentityUser>, IBsonDocumentSerializer |
|
|
|
{ |
|
|
|
private static readonly IBsonSerializer<MongoUser> MongoUserSerializer = |
|
|
|
BsonSerializer.LookupSerializer<MongoUser>(); |
|
|
|
|
|
|
|
public override IdentityUser Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) |
|
|
|
{ |
|
|
|
args.NominalType = typeof(MongoUser); |
|
|
|
return MongoUserSerializer.Deserialize(context, args); |
|
|
|
} |
|
|
|
|
|
|
|
public override void Serialize(BsonSerializationContext context, BsonSerializationArgs args, IdentityUser value) |
|
|
|
{ |
|
|
|
args.NominalType = typeof(MongoUser); |
|
|
|
MongoUserSerializer.Serialize(context, args, (MongoUser)value); |
|
|
|
} |
|
|
|
|
|
|
|
public bool TryGetMemberSerializationInfo(string memberName, out BsonSerializationInfo serializationInfo) |
|
|
|
{ |
|
|
|
return ((IBsonDocumentSerializer)MongoUserSerializer) |
|
|
|
.TryGetMemberSerializationInfo(memberName, out serializationInfo); |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|