diff --git a/RavenDB.Identity/IdentityRole.cs b/RavenDB.Identity/IdentityRole.cs index 0d6ffc4..056f0d2 100644 --- a/RavenDB.Identity/IdentityRole.cs +++ b/RavenDB.Identity/IdentityRole.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Text; -namespace RavenDB.Identity +namespace Raven.Identity { /// /// The default implementation of which uses a string as the primary key. diff --git a/RavenDB.Identity/IdentityRoleClaim.cs b/RavenDB.Identity/IdentityRoleClaim.cs index 0296665..6920b54 100644 --- a/RavenDB.Identity/IdentityRoleClaim.cs +++ b/RavenDB.Identity/IdentityRoleClaim.cs @@ -3,7 +3,7 @@ using System.Collections.Generic; using System.Security.Claims; using System.Text; -namespace RavenDB.Identity +namespace Raven.Identity { /// /// Represents a claim that is granted to all users within a role. diff --git a/RavenDB.Identity/IdentityUser.cs b/RavenDB.Identity/IdentityUser.cs index 8b6cb07..8e98a64 100644 --- a/RavenDB.Identity/IdentityUser.cs +++ b/RavenDB.Identity/IdentityUser.cs @@ -5,7 +5,7 @@ using System.Text; using System.Threading.Tasks; using Microsoft.AspNetCore.Identity; -namespace RavenDB.Identity +namespace Raven.Identity { /// /// Base user class for RavenDB Identity. Inherit from this class to add your own properties. @@ -112,25 +112,63 @@ namespace RavenDB.Identity } } + /// + /// Defines a user login. + /// public sealed class IdentityUserLogin { + /// + /// The ID of the login. + /// public string Id { get; set; } + /// + /// The user ID. + /// public string UserId { get; set; } + /// + /// The login provider. + /// public string Provider { get; set; } + /// + /// The login provider key. + /// public string ProviderKey { get; set; } } + /// + /// A login claim. + /// public class IdentityUserClaim { + /// + /// The type of the login claim. + /// public virtual string ClaimType { get; set; } + /// + /// The login claim value. + /// public virtual string ClaimValue { get; set; } } + /// + /// Entity that aides in helping us load users by a well-known name directly from the RavenDB ACID storage engine, bypassing the eventually consistent RavenDB indexes. + /// public sealed class IdentityUserByUserName { + /// + /// The ID of the user. + /// public string UserId { get; set; } + /// + /// The user name. + /// public string UserName { get; set; } + /// + /// Creates a new IdentityUserByUserName. + /// + /// + /// public IdentityUserByUserName(string userId, string userName) { UserId = userId; diff --git a/RavenDB.Identity/RavenDB.Identity.csproj b/RavenDB.Identity/RavenDB.Identity.csproj index 058dea8..68e0602 100644 --- a/RavenDB.Identity/RavenDB.Identity.csproj +++ b/RavenDB.Identity/RavenDB.Identity.csproj @@ -18,14 +18,14 @@ false false false - 3.1.0 + 4.1.1 False - + - + diff --git a/RavenDB.Identity/RoleStore.cs b/RavenDB.Identity/RoleStore.cs index 73b4a5c..0711fbc 100644 --- a/RavenDB.Identity/RoleStore.cs +++ b/RavenDB.Identity/RoleStore.cs @@ -1,5 +1,4 @@ using Microsoft.AspNetCore.Identity; -using Raven.Abstractions.Exceptions; using Raven.Client; using System; using System.Collections.Generic; @@ -10,8 +9,10 @@ using System.Threading; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; +using Raven.Client.Documents.Session; +using Raven.Client.Exceptions; -namespace RavenDB.Identity +namespace Raven.Identity { /// /// diff --git a/RavenDB.Identity/ServiceCollectionExtensions.cs b/RavenDB.Identity/ServiceCollectionExtensions.cs index 88938e3..f76c3b8 100644 --- a/RavenDB.Identity/ServiceCollectionExtensions.cs +++ b/RavenDB.Identity/ServiceCollectionExtensions.cs @@ -1,81 +1,17 @@ using Microsoft.AspNetCore.Identity; using Microsoft.Extensions.DependencyInjection; using Raven.Client; -using Raven.Client.Document; +using Raven.Client.Documents; +using Raven.Client.Documents.Session; using System; -namespace RavenDB.Identity +namespace Raven.Identity { /// /// Extends the so that RavenDB services can be registered through it. /// public static class ServiceCollectionExtensions { - /// - /// Registers a RavenDB as a singleton. - /// - /// - /// - /// public void ConfigureServices(IServiceCollection services) - /// { - /// services.AddRavenDb(Configuration.GetConnectionString("RavenDbConnection"))); - /// } - /// - /// - /// The to add services to. - /// The connection string to the Raven database instance. - /// An optional action to configure the . - /// Based on code from https://github.com/maqduni/AspNetCore.Identity.RavenDb/blob/master/src/Maqduni.AspNetCore.Identity.RavenDb/RavenDbServiceCollectionExtensions.cs - /// - /// The same service collection so that multiple calls can be chained. - /// - public static IServiceCollection AddRavenDb( - this IServiceCollection serviceCollection, - string connectionString, - Action configureAction = null) - { - if (string.IsNullOrEmpty(connectionString)) - { - throw new ArgumentNullException("Connection string cannot be null or empty."); - } - - var documentStore = new DocumentStore(); - documentStore.ParseConnectionString(connectionString); - documentStore.Conventions.RegisterIdConvention((dbname, commands, user) => "IdentityUsers/" + user.Id); - configureAction?.Invoke(documentStore); - documentStore.Initialize(); - - serviceCollection.AddSingleton(documentStore); - - return serviceCollection; - } - - /// - /// Registers a RavenDB to be created and disposed on each request. - /// - /// - /// - /// public void ConfigureServices(IServiceCollection services) - /// { - /// services.AddRavenDbAsyncSession(); - /// } - /// - /// - /// The to add services to. - /// Based on code from https://github.com/maqduni/AspNetCore.Identity.RavenDb/blob/master/src/Maqduni.AspNetCore.Identity.RavenDb/RavenDbServiceCollectionExtensions.cs - /// The same service collection so that multiple calls can be chained. - public static IServiceCollection AddRavenDbAsyncSession(this IServiceCollection serviceCollection) - { - var docStore = serviceCollection.BuildServiceProvider().GetService(); - if (docStore == null) - { - throw new InvalidOperationException($"Please call {nameof(AddRavenDb)} before calling {nameof(AddRavenDbAsyncSession)}."); - } - - serviceCollection.Add(new ServiceDescriptor(typeof(IAsyncDocumentSession), p => docStore.OpenAsyncSession(), ServiceLifetime.Scoped)); - return serviceCollection; - } - /// /// Registers a RavenDB as the user store. /// @@ -103,5 +39,68 @@ namespace RavenDB.Identity return services; } + + /// + /// Registers a RavenDB to be created and disposed on each request. + /// + /// + /// + /// public void ConfigureServices(IServiceCollection services) + /// { + /// services.AddRavenDbAsyncSession(() => myRavenDocStore); + /// } + /// + /// + /// The to add services to. + /// The function that gets the database. + /// Based on code from https://github.com/maqduni/AspNetCore.Identity.RavenDb/blob/master/src/Maqduni.AspNetCore.Identity.RavenDb/RavenDbServiceCollectionExtensions.cs + /// The same service collection so that multiple calls can be chained. + public static IServiceCollection AddRavenDbAsyncSession(this IServiceCollection serviceCollection, Func dbGetter) + { + serviceCollection.Add(new ServiceDescriptor(typeof(IAsyncDocumentSession), p => dbGetter().OpenAsyncSession(), ServiceLifetime.Scoped)); + return serviceCollection; + } + + /// + /// Registers a RavenDB to be created and disposed on each request. + /// + /// + /// + /// public void ConfigureServices(IServiceCollection services) + /// { + /// services.AddRavenDbAsyncSession(() => myRavenDocStore); + /// } + /// + /// + /// The to add services to. + /// The RavenDB document store. + /// Based on code from https://github.com/maqduni/AspNetCore.Identity.RavenDb/blob/master/src/Maqduni.AspNetCore.Identity.RavenDb/RavenDbServiceCollectionExtensions.cs + /// The same service collection so that multiple calls can be chained. + public static IServiceCollection AddRavenDbAsyncSession(this IServiceCollection serviceCollection, IDocumentStore db) + { + serviceCollection.Add(new ServiceDescriptor(typeof(IAsyncDocumentSession), p => db.OpenAsyncSession(), ServiceLifetime.Scoped)); + return serviceCollection; + } + + /// + /// Registers a RavenDB to be created and disposed on each request. + /// This requires for an to be added to dependency injection services. + /// + /// + /// + /// public void ConfigureServices(IServiceCollection services) + /// { + /// services.AddRavenDbAsyncSession(); + /// } + /// + /// + /// The to add services to. + /// Based on code from https://github.com/maqduni/AspNetCore.Identity.RavenDb/blob/master/src/Maqduni.AspNetCore.Identity.RavenDb/RavenDbServiceCollectionExtensions.cs + /// The same service collection so that multiple calls can be chained. + public static IServiceCollection AddRavenDbAsyncSession(this IServiceCollection serviceCollection) + { + serviceCollection.Add(new ServiceDescriptor(typeof(IAsyncDocumentSession), p => p.GetRequiredService().OpenAsyncSession(), ServiceLifetime.Scoped)); + return serviceCollection; + } } } diff --git a/RavenDB.Identity/UserStore.cs b/RavenDB.Identity/UserStore.cs index e74b28b..3919e89 100644 --- a/RavenDB.Identity/UserStore.cs +++ b/RavenDB.Identity/UserStore.cs @@ -1,17 +1,14 @@ -using System; -using System.Collections; +using Microsoft.AspNetCore.Identity; +using Raven.Client.Documents; +using Raven.Client.Documents.Session; +using System; using System.Collections.Generic; using System.Linq; using System.Security.Claims; -using System.Security.Cryptography; -using System.Text; using System.Threading; using System.Threading.Tasks; -using Microsoft.AspNetCore.Identity; -using Raven.Client; -using System.Runtime.CompilerServices; -namespace RavenDB.Identity +namespace Raven.Identity { /// /// UserStore for entities in a RavenDB database. @@ -117,7 +114,7 @@ namespace RavenDB.Identity if (string.IsNullOrEmpty(user.Id)) { var conventions = DbSession.Advanced.DocumentStore.Conventions; - var entityName = conventions.GetTypeTagName(typeof(TUser)); + var entityName = conventions.GetCollectionName(typeof(TUser)); var separator = conventions.IdentityPartsSeparator; var id = $"{entityName}{separator}{user.Email}"; user.Id = id; @@ -301,7 +298,7 @@ namespace RavenDB.Identity } /// - public Task> GetUsersForClaimAsync(Claim claim, CancellationToken cancellationToken) + public async Task> GetUsersForClaimAsync(Claim claim, CancellationToken cancellationToken) { ThrowIfDisposedOrCancelled(cancellationToken); if (claim == null) @@ -309,9 +306,10 @@ namespace RavenDB.Identity throw new ArgumentNullException(nameof(claim)); } - return DbSession.Query() + var list = await DbSession.Query() .Where(u => u.Claims.Any(c => c.ClaimType == claim.Type && c.ClaimValue == claim.Value)) .ToListAsync(); + return list; } #endregion @@ -380,7 +378,7 @@ namespace RavenDB.Identity } /// - public Task> GetUsersInRoleAsync(string roleName, CancellationToken cancellationToken) + public async Task> GetUsersInRoleAsync(string roleName, CancellationToken cancellationToken) { ThrowIfDisposedOrCancelled(cancellationToken); if (string.IsNullOrEmpty(roleName)) @@ -388,10 +386,11 @@ namespace RavenDB.Identity throw new ArgumentNullException(nameof(roleName)); } - return DbSession.Query() + var users = await DbSession.Query() .Where(u => u.Roles.Contains(roleName, StringComparer.InvariantCultureIgnoreCase)) .Take(1024) .ToListAsync(); + return users; } #endregion @@ -660,7 +659,8 @@ namespace RavenDB.Identity if (_session == null) { _session = getSessionFunc(); - _session.Advanced.DocumentStore.Conventions.RegisterIdConvention((dbname, commands, user) => "IdentityUsers/" + user.Id); + // TODO: do we really need this? I don't believe so. Brought over from Raven 3.x - the new 4.0 uses async version only. + //_session.Advanced.DocumentStore.Conventions.RegisterIdConvention((dbname, commands, user) => "IdentityUsers/" + user.Id); } return _session; } diff --git a/RavenDB.Identity/Util.cs b/RavenDB.Identity/Util.cs index 934e022..edeb375 100644 --- a/RavenDB.Identity/Util.cs +++ b/RavenDB.Identity/Util.cs @@ -5,7 +5,7 @@ using System.Security.Cryptography; using System.Text; using Microsoft.AspNetCore.Identity; -namespace RavenDB.Identity +namespace Raven.Identity { internal static class Util { diff --git a/Readme.md b/Readme.md index 0701103..115556e 100644 --- a/Readme.md +++ b/Readme.md @@ -1,7 +1,7 @@ # ![RavenDB logo](https://github.com/JudahGabriel/RavenDB.Identity/blob/master/RavenDB.Identity/nuget-icon.png?raw=true) RavenDB.Identity # RavenDB identity provider for ASP.NET Core. -The simple and easy Identity provider for RavenDB and ASP.NET Core. Use Raven to store your users and logins. +The simple and easy Identity provider for RavenDB and ASP.NET Core. Use Raven to store your users and logins. Uses RavenDB 4+ ## Instructions ## 1. In Startup.cs: @@ -11,8 +11,7 @@ public void ConfigureServices(IServiceCollection services) { // Add RavenDB and identity. services - .AddRavenDb(Configuration.GetConnectionString("RavenDbConnection")) // Create a RavenDB DocumentStore singleton. - .AddRavenDbAsyncSession() // Create a RavenDB IAsyncDocumentSession for each request. + .AddRavenDbAsyncSession(docStore) // Create a RavenDB IAsyncDocumentSession for each request. docStore is your IDocumentStore instance. .AddRavenDbIdentity(); // Use Raven for users and roles. AppUser is your class, a simple DTO to hold user data. See https://github.com/JudahGabriel/RavenDB.Identity/blob/master/Sample/Models/AppUser.cs ... diff --git a/Sample/Controllers/AccountController.cs b/Sample/Controllers/AccountController.cs index 5291562..eed110f 100644 --- a/Sample/Controllers/AccountController.cs +++ b/Sample/Controllers/AccountController.cs @@ -1,19 +1,15 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Security.Claims; -using System.Threading.Tasks; -using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.Rendering; using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; +using Raven.Client.Documents.Session; using Sample.Models; using Sample.Models.AccountViewModels; using Sample.Services; -using Raven.Client; +using System; +using System.Security.Claims; +using System.Threading.Tasks; namespace Sample.Controllers { diff --git a/Sample/Controllers/HomeController.cs b/Sample/Controllers/HomeController.cs index d1f544f..87e74d1 100644 --- a/Sample/Controllers/HomeController.cs +++ b/Sample/Controllers/HomeController.cs @@ -1,13 +1,11 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Identity; +using Microsoft.AspNetCore.Mvc; +using Raven.Client.Documents; +using Raven.Client.Documents.Session; +using Sample.Models; using System.Linq; using System.Threading.Tasks; -using Microsoft.AspNetCore.Mvc; -using Sample.Models; -using Raven.Client; -using Microsoft.AspNetCore.Authorization; -using Microsoft.AspNetCore.Identity; namespace Sample.Controllers { diff --git a/Sample/Controllers/RavenController.cs b/Sample/Controllers/RavenController.cs index 5b95909..662d38e 100644 --- a/Sample/Controllers/RavenController.cs +++ b/Sample/Controllers/RavenController.cs @@ -1,10 +1,8 @@ using Microsoft.AspNetCore.Mvc; -using Raven.Client; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc.Filters; +using Raven.Client.Documents.Session; +using System; +using System.Threading.Tasks; namespace Sample.Controllers { diff --git a/Sample/Models/AppUser.cs b/Sample/Models/AppUser.cs index 0824bad..6d2f14d 100644 --- a/Sample/Models/AppUser.cs +++ b/Sample/Models/AppUser.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; -using RavenDB.Identity; +using Raven.Identity; namespace Sample.Models { diff --git a/Sample/Startup.cs b/Sample/Startup.cs index f5dc2ee..53a64fd 100644 --- a/Sample/Startup.cs +++ b/Sample/Startup.cs @@ -10,7 +10,8 @@ using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Sample.Models; using Sample.Services; -using RavenDB.Identity; +using Raven.Identity; +using Raven.Client.Documents; namespace Sample { @@ -26,10 +27,19 @@ namespace Sample // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { + + // Connect to a Raven server. We're using the public test playground at http://live-test.ravendb.net + // NOTE: Getting a DatabaseDoesNotExistException? Go to http://live-test.ravendb.net and create a database with the name 'Raven.Identity.Sample' + var docStore = new DocumentStore + { + Urls = new string[] { "http://live-test.ravendb.net" }, + Database = "Raven.Identity.Sample" + }; + docStore.Initialize(); + // Add RavenDB and identity. services - .AddRavenDb(Configuration.GetConnectionString("RavenDbConnection")) // Create a RavenDB DocumentStore singleton. - .AddRavenDbAsyncSession() // Create a RavenDB IAsyncDocumentSession for each request. + .AddRavenDbAsyncSession(docStore) // Create a RavenDB IAsyncDocumentSession for each request. .AddRavenDbIdentity(); // Use Raven for users and roles. // You can change the login path if need be.