Skip to content

Commit

Permalink
Merge pull request #7 from Aguafrommars/feature/requestable
Browse files Browse the repository at this point in the history
Feature/requestable
  • Loading branch information
aguacongas authored Mar 15, 2021
2 parents 9e71010 + 6a59665 commit 53beb44
Show file tree
Hide file tree
Showing 16 changed files with 287 additions and 255 deletions.
7 changes: 3 additions & 4 deletions appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ image:
environment:
GH_TOKEN:
secure: u7qaOQsrkLqq44yS24C0eM2vRCzp1A8gZTWNmlA58TIDJGmrDXguHL9H/vww7Fg/
donetsdk: 3.1.406
donetsdk5: 5.0.102
donetsdk3: 3.1.406
donetsdk: 5.0.200
JAVA_HOME: C:\Program Files\Java\jdk14
init:
- cmd: git config --global core.autocrlf true
Expand All @@ -26,12 +26,11 @@ install:
- sh: sudo apt-get -y install apt-transport-https
- sh: sudo apt-get update
- sh: sudo chmod +x ./dotnet-install.sh
- sh: sudo ./dotnet-install.sh -Channel Current -Version $donetsdk3 -InstallDir ./dotnetsdk -NoPath
- sh: sudo ./dotnet-install.sh -Channel Current -Version $donetsdk -InstallDir ./dotnetsdk -NoPath
- sh: sudo ./dotnet-install.sh -Channel Current -Version $donetsdk5 -InstallDir ./dotnetsdk -NoPath
- sh: export PATH=/home/appveyor/projects/identity-ravendb/dotnetsdk:$PATH
- sh: sudo apt -y install nuget
- ps: if ($isWindows) { .\dotnet-install.ps1 -Version $env:donetsdk }
- ps: if ($isWindows) { .\dotnet-install.ps1 -Version $env:donetsdk5 }
- ps: dotnet tool install --global GitVersion.Tool
- ps: dotnet gitversion /l console /output buildserver
- ps: dotnet tool install --global dotnet-sonarscanner
Expand Down
2 changes: 1 addition & 1 deletion samples/IdentitySample/IdentitySample.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="5.0.3" />
<PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="5.0.4" />
</ItemGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>netstandard2.0;net462</TargetFrameworks>
Expand All @@ -25,7 +25,7 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Identity.Stores" Version="3.1.12" />
<PackageReference Include="RavenDB.Client" Version="5.1.4" />
<PackageReference Include="Microsoft.Extensions.Identity.Stores" Version="3.1.13" />
<PackageReference Include="RavenDB.Client" Version="5.1.5" />
</ItemGroup>
</Project>
93 changes: 93 additions & 0 deletions src/Aguacongas.Identity.RavenDb/DocumentStoreExtension.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
using Aguacongas.Identity.RavenDb;
using Microsoft.AspNetCore.Identity;
using System;
using System.Reflection;

namespace Raven.Client.Documents
{
public static class DocumentStoreExtension
{
public static IDocumentStore SetFindIdentityPropertyForIdentityModel(this IDocumentStore store)
{
var findId = store.Conventions.FindIdentityProperty;
store.Conventions.FindIdentityProperty = memberInfo => SetConventions(memberInfo, findId);
return store;
}

private static bool SetConventions(MemberInfo memberInfo, Func<MemberInfo, bool> findId)
{
if (memberInfo.DeclaringType == typeof(UserData))
{
return false;
}
if (memberInfo.DeclaringType == typeof(RoleData))
{
return false;
}
if (IsSubclassOf(memberInfo.DeclaringType, typeof(IdentityUser<>)))
{
return false;
}
if (IsSubclassOf(memberInfo.DeclaringType, typeof(IdentityUserClaim<>)))
{
return false;
}
if (IsSubclassOf(memberInfo.DeclaringType, typeof(IdentityUserRole<>)))
{
return false;
}
if (IsSubclassOf(memberInfo.DeclaringType, typeof(IdentityUserLogin<>)))
{
return false;
}
if (IsSubclassOf(memberInfo.DeclaringType, typeof(IdentityUserToken<>)))
{
return false;
}
if (IsSubclassOf(memberInfo.DeclaringType, typeof(IdentityRole<>)))
{
return false;
}
if (IsSubclassOf(memberInfo.DeclaringType, typeof(IdentityRoleClaim<>)))
{
return false;
}

return findId(memberInfo);
}

private static bool IsSubclassOf(Type type, Type baseType)
{
if (type == null || baseType == null || type == baseType)
{
return false;
}

if (!baseType.IsGenericType)
{
if (!type.IsGenericType)
{
return type.IsSubclassOf(baseType);
}
}
else
{
baseType = baseType.GetGenericTypeDefinition();
}

var objectType = typeof(object);
while (type != objectType && type != null)
{
var curentType = type.IsGenericType ? type.GetGenericTypeDefinition() : type;
if (curentType == baseType)
{
return true;
}

type = type.BaseType;
}

return false;
}
}
}
2 changes: 0 additions & 2 deletions src/Aguacongas.Identity.RavenDb/IdentityBuilderExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,6 @@
using Aguacongas.Identity.RavenDb;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Raven.Client.Documents;
using Raven.Client.Documents.Session;
using System;
Expand Down
13 changes: 3 additions & 10 deletions src/Aguacongas.Identity.RavenDb/Models/RoleData.cs
Original file line number Diff line number Diff line change
@@ -1,22 +1,15 @@
// Project: Aguafrommars/Identity.RavenDb
// Copyright (c) 2021 Olivier Lefebvre
using Microsoft.AspNetCore.Identity;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;

namespace Aguacongas.Identity.RavenDb
{
[SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "All are needed")]
public class RoleData<TKey, TRole, TRoleClaims>
where TKey: IEquatable<TKey>
where TRole : IdentityRole<TKey>
where TRoleClaims: IdentityRoleClaim<TKey>
public class RoleData
{
public string Id { get; set; }

public virtual TRole Role { get; set; }
public string RoleId { get; set; }

public virtual List<TRoleClaims> Claims { get; private set; } = new List<TRoleClaims>();
public List<string> ClaimIds { get; private set; } = new List<string>();
}
}
16 changes: 4 additions & 12 deletions src/Aguacongas.Identity.RavenDb/Models/UserData.cs
Original file line number Diff line number Diff line change
@@ -1,25 +1,17 @@
// Project: Aguafrommars/Identity.RavenDb
// Copyright (c) 2021 Olivier Lefebvre
using Microsoft.AspNetCore.Identity;
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;

namespace Aguacongas.Identity.RavenDb
{
[SuppressMessage("Major Code Smell", "S2436:Types and methods should not have too many generic parameters", Justification = "All are needed")]
public class UserData<TKey, TUser, TUserClaim, TUserLogin>
where TKey: IEquatable<TKey>
where TUser: IdentityUser<TKey>
where TUserClaim : IdentityUserClaim<TKey>
where TUserLogin : IdentityUserLogin<TKey>
public class UserData
{
public string Id { get; set; }

public virtual TUser User { get; set; }
public string UserId { get; set; }

public virtual List<TUserClaim> Claims { get; private set; } = new List<TUserClaim>();
public List<string> ClaimIds { get; private set; } = new List<string>();

public virtual List<TUserLogin> Logins { get; private set; } = new List<TUserLogin>();
public List<string> LoginIds { get; private set; } = new List<string>();
}
}
13 changes: 0 additions & 13 deletions src/Aguacongas.Identity.RavenDb/Models/UserLoginIndex.cs

This file was deleted.

76 changes: 52 additions & 24 deletions src/Aguacongas.Identity.RavenDb/RoleStore.cs
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,7 @@ public class RoleStore<TRole, TKey, TUserRole, TRoleClaim> :
/// <summary>
/// A navigation property for the roles the store contains.
/// </summary>
public IQueryable<TRole> Roles => _session.Query<RoleData<TKey, TRole, TRoleClaim>>()
.Select(d => d.Role)
public IQueryable<TRole> Roles => _session.Query<TRole>()
.ToListAsync().ConfigureAwait(false).GetAwaiter().GetResult().AsQueryable();

/// <summary>
Expand Down Expand Up @@ -104,12 +103,14 @@ public async virtual Task<IdentityResult> CreateAsync(TRole role, CancellationTo
AssertNotNull(role, nameof(role));

var roleId = ConvertIdToString(role.Id);
var data = new RoleData<TKey, TRole, TRoleClaim>
var data = new RoleData
{
Id = $"role/{roleId}",
Role = role
Id = $"roledata/{roleId}",
RoleId = $"role/{roleId}"
};
await _session.StoreAsync(data, cancellationToken).ConfigureAwait(false);
await _session.StoreAsync(role, data.RoleId, cancellationToken).ConfigureAwait(false);
await _session.StoreAsync(data, data.Id, cancellationToken).ConfigureAwait(false);

await _session.SaveChangesAsync(cancellationToken).ConfigureAwait(false);

return IdentityResult.Success;
Expand All @@ -128,8 +129,8 @@ public async virtual Task<IdentityResult> UpdateAsync(TRole role, CancellationTo
AssertNotNull(role, nameof(role));

var roleId = ConvertIdToString(role.Id);
var data = await _session.LoadAsync<RoleData<TKey, TRole, TRoleClaim>>($"role/{roleId}").ConfigureAwait(false);
data.Role = role;
var existing = await _session.LoadAsync<TRole>($"role/{roleId}").ConfigureAwait(false);
CloneEntity(existing, typeof(TRole), role);

try
{
Expand All @@ -156,7 +157,13 @@ public async virtual Task<IdentityResult> DeleteAsync(TRole role, CancellationTo
AssertNotNull(role, nameof(role));

var roleId = ConvertIdToString(role.Id);
_session.Delete($"role/{roleId}");

var data = await _session.LoadAsync<RoleData>($"roledata/{roleId}", cancellationToken).ConfigureAwait(false);
_session.Delete(data.RoleId);
foreach(var claimId in data.ClaimIds)
{
_session.Delete(claimId);
}
_session.Delete($"rolename/{role.NormalizedName}");

try
Expand Down Expand Up @@ -224,14 +231,12 @@ public virtual Task SetRoleNameAsync(TRole role, string roleName, CancellationTo
/// <param name="roleId">The role ID to look for.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> used to propagate notifications that the operation should be canceled.</param>
/// <returns>A <see cref="Task{TResult}"/> that result of the look up.</returns>
public virtual async Task<TRole> FindByIdAsync(string roleId, CancellationToken cancellationToken = default)
public virtual Task<TRole> FindByIdAsync(string roleId, CancellationToken cancellationToken = default)
{
cancellationToken.ThrowIfCancellationRequested();
ThrowIfDisposed();

var data = await _session.LoadAsync<RoleData<TKey, TRole, TRoleClaim>>($"role/{roleId}", cancellationToken).ConfigureAwait(false);

return data?.Role;
return _session.LoadAsync<TRole>($"role/{roleId}", cancellationToken);
}

/// <summary>
Expand All @@ -255,9 +260,7 @@ public virtual async Task<TRole> FindByNameAsync(string normalizedRoleName, Canc
return null;
}

var data = await _session.LoadAsync<RoleData<TKey, TRole, TRoleClaim>>(index.RoleId, cancellationToken).ConfigureAwait(false);

return data.Role;
return await _session.LoadAsync<TRole>(index.RoleId, cancellationToken).ConfigureAwait(false);
}

/// <summary>
Expand Down Expand Up @@ -335,7 +338,7 @@ public async virtual Task<IList<Claim>> GetClaimsAsync(TRole role, CancellationT
ThrowIfDisposed();
AssertNotNull(role, nameof(role));

var claimList = await GetRoleClaimsAsync(role).ConfigureAwait(false);
var claimList = await GetRoleClaimsAsync(role, cancellationToken).ConfigureAwait(false);
return claimList
.Select(c => c.ToClaim())
.ToList();
Expand All @@ -354,8 +357,13 @@ public virtual async Task AddClaimAsync(TRole role, Claim claim, CancellationTok
AssertNotNull(role, nameof(role));
AssertNotNull(claim, nameof(claim));

var roleClaims = await GetRoleClaimsAsync(role).ConfigureAwait(false);
roleClaims.Add(CreateRoleClaim(role, claim));
var roleId = ConvertIdToString(role.Id);
var data = await _session.LoadAsync<RoleData>($"roledata/{roleId}", cancellationToken).ConfigureAwait(false);
var roleClaim = CreateRoleClaim(role, claim);
roleClaim.Id = data.ClaimIds.Count;
var claimId = $"roleclaim/{roleId}@{roleClaim.Id}";
data.ClaimIds.Add(claimId);
await _session.StoreAsync(roleClaim, claimId, cancellationToken).ConfigureAwait(false);
}

/// <summary>
Expand All @@ -371,8 +379,15 @@ public async virtual Task RemoveClaimAsync(TRole role, Claim claim, Cancellation
AssertNotNull(role, nameof(role));
AssertNotNull(claim, nameof(claim));

var roleClaims = await GetRoleClaimsAsync(role).ConfigureAwait(false);
roleClaims.RemoveAll(c => c.ClaimType == claim.Type && c.ClaimValue == claim.Value);
var roleId = ConvertIdToString(role.Id);
var claimList = await GetRoleClaimsAsync(role, cancellationToken).ConfigureAwait(false);
var data = await _session.LoadAsync<RoleData>($"roledata/{roleId}", cancellationToken).ConfigureAwait(false);
foreach(var roleClain in claimList.Where(c => c.ClaimType == claim.Type && c.ClaimValue == claim.Value))
{
var claimId = $"roleclaim/{roleId}@{roleClain.Id}";
_session.Delete(claimId);
data.ClaimIds.Remove(claimId);
}
}

/// <summary>
Expand Down Expand Up @@ -412,11 +427,16 @@ public virtual string ConvertIdToString(TKey id)
return id.ToString();
}

protected virtual async Task<List<TRoleClaim>> GetRoleClaimsAsync(TRole role)
protected virtual async Task<List<TRoleClaim>> GetRoleClaimsAsync(TRole role, CancellationToken cancellationToken = default)
{
var roleId = ConvertIdToString(role.Id);
var data = await _session.LoadAsync<RoleData<TKey, TRole, TRoleClaim>>($"role/{roleId}").ConfigureAwait(false);
return data.Claims;
var data = await _session.LoadAsync<RoleData>($"roledata/{roleId}", builder => builder.IncludeDocuments(d => d.ClaimIds), cancellationToken).ConfigureAwait(false);
var list = new List<TRoleClaim>(data.ClaimIds.Count);
foreach(var id in data.ClaimIds)
{
list.Add(await _session.LoadAsync<TRoleClaim>(id).ConfigureAwait(false));
}
return list;
}

private static void AssertNotNull(object p, string pName)
Expand All @@ -426,5 +446,13 @@ private static void AssertNotNull(object p, string pName)
throw new ArgumentNullException(pName);
}
}

private static void CloneEntity(object entity, Type type, object loaded)
{
foreach (var property in type.GetProperties())
{
property.SetValue(entity, property.GetValue(loaded));
}
}
}
}
Loading

0 comments on commit 53beb44

Please sign in to comment.