Skip to content

Commit

Permalink
feat: ravendb stores
Browse files Browse the repository at this point in the history
  • Loading branch information
aguacongas committed Feb 16, 2021
1 parent 5c2aac3 commit 41548e0
Show file tree
Hide file tree
Showing 21 changed files with 3,273 additions and 0 deletions.
39 changes: 39 additions & 0 deletions Aguacongas.Identity.RavenDb.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.31005.135
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{06F6F8B5-B8CA-4723-ABA3-481DD2966A3B}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Aguacongas.Identity.RavenDb", "src\Aguacongas.Identity.RavenDb\Aguacongas.Identity.RavenDb.csproj", "{7763B0A7-45C9-438E-8E98-7F0D6D5ECD07}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{7BA591BC-2549-43AC-A93B-84D2CE705CD4}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Aguacongas.Identity.RavenDb.IntegrationTest", "test\Aguacongas.Identity.RavenDb.IntegrationTest\Aguacongas.Identity.RavenDb.IntegrationTest.csproj", "{F1F52BC9-B9DE-4777-B734-88F1D10E2567}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{7763B0A7-45C9-438E-8E98-7F0D6D5ECD07}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{7763B0A7-45C9-438E-8E98-7F0D6D5ECD07}.Debug|Any CPU.Build.0 = Debug|Any CPU
{7763B0A7-45C9-438E-8E98-7F0D6D5ECD07}.Release|Any CPU.ActiveCfg = Release|Any CPU
{7763B0A7-45C9-438E-8E98-7F0D6D5ECD07}.Release|Any CPU.Build.0 = Release|Any CPU
{F1F52BC9-B9DE-4777-B734-88F1D10E2567}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{F1F52BC9-B9DE-4777-B734-88F1D10E2567}.Debug|Any CPU.Build.0 = Debug|Any CPU
{F1F52BC9-B9DE-4777-B734-88F1D10E2567}.Release|Any CPU.ActiveCfg = Release|Any CPU
{F1F52BC9-B9DE-4777-B734-88F1D10E2567}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{7763B0A7-45C9-438E-8E98-7F0D6D5ECD07} = {06F6F8B5-B8CA-4723-ABA3-481DD2966A3B}
{F1F52BC9-B9DE-4777-B734-88F1D10E2567} = {7BA591BC-2549-43AC-A93B-84D2CE705CD4}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {BDAAC575-A68B-4FA3-A55C-F9CB7513A299}
EndGlobalSection
EndGlobal
14 changes: 14 additions & 0 deletions Aguacongas.Identity.RavenDb.sln.licenseheader
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
extensions: designer.cs generated.cs
extensions: .cs .cpp .h
// Project: Aguafrommars/Identity.RavenDb
// Copyright (c) 2021 Olivier Lefebvre
extensions: .aspx .ascx
<%--
Project: Aguafrommars/Identity.RavenDb
Copyright (c) 2021 Olivier Lefebvre
--%>
extensions: .xml .config .xsd
<!--
Project: Aguafrommars/Identity.RavenDb
Copyright (c) 2021 Olivier Lefebvre
-->
28 changes: 28 additions & 0 deletions src/Aguacongas.Identity.RavenDb/Aguacongas.Identity.RavenDb.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>netstandard2.0;net462</TargetFrameworks>
<Company>Olivier Lefebvre</Company>
<Description>Identity stores implementation for RavenDb</Description>
<Copyright>Olivier Lefebvre @2018</Copyright>
<PackageLicenseUrl>https://raw.githubusercontent.com/Aguafrommars/Identity.RavenDb/master/LICENSE</PackageLicenseUrl>
<PackageProjectUrl>https://github.com/Aguafrommars/Identity.RavenDb</PackageProjectUrl>
<RepositoryUrl>https://github.com/Aguafrommars/Identity.RavenDb</RepositoryUrl>
<RepositoryType>git</RepositoryType>
<PackageTags>identity RavenDb</PackageTags>
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Release|netstandard2.0|AnyCPU'">
<CodeAnalysisRuleSet>Aguacongas.Identity.RavenDb.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>

<PropertyGroup Condition="'$(Configuration)|$(TargetFramework)|$(Platform)'=='Debug|netstandard2.0|AnyCPU'">
<CodeAnalysisRuleSet>Aguacongas.Identity.RavenDb.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Identity" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Identity.Stores" Version="3.1.12" />
<PackageReference Include="RavenDB.Client" Version="5.1.4" />
</ItemGroup>
</Project>
108 changes: 108 additions & 0 deletions src/Aguacongas.Identity.RavenDb/IdentityBuilderExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
// Project: Aguafrommars/Identity.RavenDb
// Copyright (c) 2021 Olivier Lefebvre
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;
using System.Reflection;

namespace Microsoft.Extensions.DependencyInjection
{

/// <summary>
/// Contains extension methods to <see cref="IdentityBuilder"/> for adding entity framework stores.
/// </summary>
public static class IdentityBuilderExtensions
{
/// <summary>
/// Adds an RavenDb implementation of identity stores.
/// </summary>
/// <param name="builder">The <see cref="IdentityBuilder" /> instance this method extends.</param>
/// <param name="getDocumentStore"><see cref="IDocumentStore" /> factory function returning the RavenDb document store to use</param>
/// <param name="dataBase">The data base.</param>
/// <returns>
/// The <see cref="IdentityBuilder" /> instance this method extends.
/// </returns>
public static IdentityBuilder AddRavenDbStores(this IdentityBuilder builder, Func<IServiceProvider, IDocumentStore> getDocumentStore = null, string dataBase = null)
{
if (getDocumentStore == null)
{
getDocumentStore = p => p.GetRequiredService<IDocumentStore>();
}

AddStores(builder.Services, builder.UserType, builder.RoleType, p =>
{
var session = getDocumentStore(p).OpenAsyncSession(new SessionOptions
{
Database = dataBase
});
var adv = session.Advanced;
adv.UseOptimisticConcurrency = true;
adv.MaxNumberOfRequestsPerSession = int.MaxValue;
return session;
});

return builder;
}

private static void AddStores(IServiceCollection services, Type userType, Type roleType, Func<IServiceProvider, IAsyncDocumentSession> getSession)
{
var identityUserType = FindGenericBaseType(userType, typeof(IdentityUser<>));
if (identityUserType == null)
{
throw new InvalidOperationException("AddEntityFrameworkStores can only be called with a user that derives from IdentityUser<TKey>.");
}

var keyType = identityUserType.GenericTypeArguments[0];

var userOnlyStoreType = typeof(UserOnlyStore<,>).MakeGenericType(userType, keyType);

if (roleType != null)
{
var identityRoleType = FindGenericBaseType(roleType, typeof(IdentityRole<>));
if (identityRoleType == null)
{
throw new InvalidOperationException("AddEntityFrameworkStores can only be called with a role that derives from IdentityRole<TKey>.");
}

var userStoreType = typeof(UserStore<,,>).MakeGenericType(userType, roleType, keyType);
var roleStoreType = typeof(RoleStore<,>).MakeGenericType(roleType, keyType);

services.TryAddScoped(typeof(UserOnlyStore<,>).MakeGenericType(userType, keyType), provider => CreateStoreInstance(userOnlyStoreType, getSession(provider), provider.GetService<IdentityErrorDescriber>()));
services.TryAddScoped(typeof(IUserStore<>).MakeGenericType(userType), provider => userStoreType.GetConstructor(new Type[] { typeof(IAsyncDocumentSession), userOnlyStoreType, typeof(IdentityErrorDescriber) })
.Invoke(new object[] { getSession(provider), provider.GetService(userOnlyStoreType), provider.GetService<IdentityErrorDescriber>() }));
services.TryAddScoped(typeof(IRoleStore<>).MakeGenericType(roleType), provider => CreateStoreInstance(roleStoreType, getSession(provider), provider.GetService<IdentityErrorDescriber>()));
}
else
{ // No Roles
services.TryAddScoped(typeof(IUserStore<>).MakeGenericType(userType), provider => CreateStoreInstance(userOnlyStoreType, getSession(provider), provider.GetService<IdentityErrorDescriber>()));
}
}

private static object CreateStoreInstance(Type storeType, IAsyncDocumentSession session, IdentityErrorDescriber errorDescriber)
{
var constructor = storeType.GetConstructor(new Type[] { typeof(IAsyncDocumentSession), typeof(IdentityErrorDescriber)});
return constructor.Invoke(new object[] { session, errorDescriber });
}

private static TypeInfo FindGenericBaseType(Type currentType, Type genericBaseType)
{
var type = currentType;
while (type != null)
{
var typeInfo = type.GetTypeInfo();
var genericType = type.IsGenericType ? type.GetGenericTypeDefinition() : null;
if (genericType != null && genericType == genericBaseType)
{
return typeInfo;
}
type = type.BaseType;
}
return null;
}
}
}
20 changes: 20 additions & 0 deletions src/Aguacongas.Identity.RavenDb/Models/RoleData.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Project: Aguafrommars/Identity.RavenDb
// Copyright (c) 2021 Olivier Lefebvre
using Microsoft.AspNetCore.Identity;
using System;
using System.Collections.Generic;

namespace Aguacongas.Identity.RavenDb
{
public class RoleData<TKey, TRole, TRoleClaims>
where TKey: IEquatable<TKey>
where TRole : IdentityRole<TKey>
where TRoleClaims: IdentityRoleClaim<TKey>
{
public string Id { get; set; }

public virtual TRole Role { get; set; }

public virtual List<TRoleClaims> Claims { get; private set; } = new List<TRoleClaims>();
}
}
11 changes: 11 additions & 0 deletions src/Aguacongas.Identity.RavenDb/Models/RoleNameIndex.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace Aguacongas.Identity.RavenDb
{
public class RoleNameIndex
{
public string Id { get; set; }

public string RoleId { get; set; }

public string NormalizedName { get; set; }
}
}
24 changes: 24 additions & 0 deletions src/Aguacongas.Identity.RavenDb/Models/UserData.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// Project: Aguafrommars/Identity.RavenDb
// Copyright (c) 2021 Olivier Lefebvre
using Microsoft.AspNetCore.Identity;
using System;
using System.Collections.Generic;

namespace Aguacongas.Identity.RavenDb
{
public class UserData<TKey, TUser, TUserClaim, TUserLogin, TUserToken>
where TKey: IEquatable<TKey>
where TUser: IdentityUser<TKey>
where TUserClaim : IdentityUserClaim<TKey>
where TUserLogin : IdentityUserLogin<TKey>
where TUserToken : IdentityUserToken<TKey>
{
public string Id { get; set; }

public virtual TUser User { get; set; }

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

public virtual List<TUserLogin> Logins { get; private set; } = new List<TUserLogin>();
}
}
12 changes: 12 additions & 0 deletions src/Aguacongas.Identity.RavenDb/Models/UserEMailIndex.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
using System.Collections.Generic;

namespace Aguacongas.Identity.RavenDb
{
public class UserEMailIndex
{
public string Id { get; set; }

public string UserId { get; set; }

}
}
13 changes: 13 additions & 0 deletions src/Aguacongas.Identity.RavenDb/Models/UserLoginIndex.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace Aguacongas.Identity.RavenDb
{
public class UserLoginIndex
{
public string Id { get; set; }

public string UserId { get; set; }

public string LoginProvider { get; set; }

public string ProviderKey { get; set; }
}
}
11 changes: 11 additions & 0 deletions src/Aguacongas.Identity.RavenDb/Models/UserNameIndex.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace Aguacongas.Identity.RavenDb
{
public class UserNameIndex
{
public string Id { get; set; }

public string UserId { get; set; }

public string NormalizedUserName { get; set; }
}
}
Loading

0 comments on commit 41548e0

Please sign in to comment.