Skip to content

Instantly share code, notes, and snippets.

@weitzhandler
Last active March 20, 2016 20:28
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save weitzhandler/f9ebca6936ed0489d42d to your computer and use it in GitHub Desktop.
Save weitzhandler/f9ebca6936ed0489d42d to your computer and use it in GitHub Desktop.
Microsoft.AspNet.EntityFramework.Identity.Multitenant
{
"version": "2.0.0-alpha-1",
"description": "Identity.Multitenant Class Library",
"authors": [ "JSkimmer", "Shimmy" ],
"tags": [ "" ],
"projectUrl": "",
"licenseUrl": "",
"dependencies": {
"System.Runtime": "4.0.20-beta-23019",
"Microsoft.AspNet.Identity.EntityFramework": "3.0.0-beta5"
},
"frameworks": {
"dnx451": { },
"dnxcore50": { }
}
}
using Microsoft.AspNet.Identity.EntityFramework.Tenant;
using Microsoft.AspNet.Routing;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.AspNet.Identity.EntityFramework.Tenant
{
public class Tenant : Tenant<string>
{
}
/// <summary>
/// A class that encapsulates an organization.
/// </summary>
public class Tenant<TKey> : IValidatableObject
where TKey : IEquatable<TKey>
{
//TODO: should retrieve from config.json AppSettings.ReservedSubdomains
public static string[] ReservedTenantNames = new string[] { "www", "global", "info", "admin" };
[Key]
public virtual TKey Id { get; set; }
[RegularExpression(@"[a-z0-9\-]+")]
[StringLength(16, MinimumLength = 6)]
public virtual string TenantName { get; set; }
public virtual ICollection<TenantUser<TKey>> Users { get; set; } = new HashSet<TenantUser<TKey>>();
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (ReservedTenantNames.Contains(TenantName))
yield return new ValidationResult($"The tenant name {TenantName} is reserved.");
yield return ValidationResult.Success;
}
}
}
using System;
using Microsoft.Data.Entity;
using Microsoft.Data.Entity.Metadata;
namespace Microsoft.AspNet.Identity.EntityFramework.Tenant
{
public class TenantIdentityDbContext
: TenantIdentityDbContext<TenantUser, IdentityRole, Tenant, string,
TenantUserLogin, IdentityUserRole, IdentityUserClaim>
{
}
public class TenantIdentityDbContext<TUser, TRole, TTenant, TKey, TUserLogin, TUserRole, TUserClaim>
: IdentityDbContext<TUser, TRole, TKey>
where TUser : TenantUser<TKey>
where TRole : IdentityRole<TKey>
where TTenant : Tenant<TKey>
where TKey : IEquatable<TKey>
where TUserLogin : TenantUserLogin<TKey>, new()
{
public DbSet<TTenant> Tenants { get; set; }
protected override void OnModelCreating(ModelBuilder builder)
{
//hack to avoid duplicate generations of the following
builder.Model.RemoveEntityType(builder.Entity<IdentityUserLogin<TKey>>().Metadata);
builder.Model.RemoveEntityType(builder.Entity<IdentityUser<TKey>>().Metadata);
builder.Entity<TUser>(b =>
{
b.Key(u => u.Id);
b.Index(u => u.NormalizedUserName).Unique().IndexName("UserNameIndex");
b.Index(u => u.NormalizedEmail).IndexName("EmailIndex");
b.Index(u => new { u.UserName, u.TenantId }).Unique().IndexName("UserTenantIndex");
b.ToTable("Users");
b.Property(u => u.ConcurrencyStamp).ConcurrencyToken();
b.Property(u => u.UserName).MaxLength(16);
b.Property(u => u.NormalizedUserName).MaxLength(16);
b.Property(u => u.Email).MaxLength(256);
b.Property(u => u.NormalizedEmail).MaxLength(256);
b.Property(u => u.TenantId).Required();
b.Collection(u => u.Claims).InverseReference().ForeignKey(uc => uc.UserId);
b.Collection(u => u.Roles).InverseReference().ForeignKey(ur => ur.UserId);
b.Reference(u => u.Tenant);
});
builder.Entity<TRole>(b =>
{
b.Key(r => r.Id);
b.Index(r => r.NormalizedName).IndexName("RoleNameIndex");
b.ToTable("Roles");
b.Property(r => r.ConcurrencyStamp).ConcurrencyToken();
b.Property(u => u.Name).MaxLength(256);
b.Property(u => u.NormalizedName).MaxLength(256);
b.Collection(r => r.Users).InverseReference().ForeignKey(ur => ur.RoleId);
b.Collection(r => r.Claims).InverseReference().ForeignKey(rc => rc.RoleId);
});
builder.Entity<TTenant>(b =>
{
b.Key(t => t.Id);
b.Index(t => t.TenantName).Unique().IndexName("TenantNameIndex");
b.ToTable("Tenants");
b.Collection(t => t.Users).InverseReference().ForeignKey(u => u.TenantId);
});
builder.Entity<IdentityUserClaim<TKey>>(b =>
{
b.Key(uc => uc.Id);
b.ToTable("Claims");
});
builder.Entity<TUserLogin>(b =>
{
b.Key(ul =>
new
{
ul.LoginProvider,
ul.ProviderKey,
ul.UserId,
ul.TenantId,
});
});
builder.Entity<IdentityRoleClaim<TKey>>(b =>
{
b.Key(rc => rc.Id);
b.ToTable("RoleClaims");
});
builder.Entity<IdentityUserRole<TKey>>(b =>
{
b.Key(r => new { r.UserId, r.RoleId });
b.ToTable("UserRoles");
});
// Blocks delete currently without cascade
//.ForeignKeys(fk => fk.ForeignKey<TUser>(f => f.UserId))
//.ForeignKeys(fk => fk.ForeignKey<TRole>(f => f.RoleId));
}
}
}
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Threading.Tasks;
namespace Microsoft.AspNet.Identity.EntityFramework.Tenant
{
public class TenantUser : TenantUser<string>
{
public TenantUser()
{
Id = Guid.NewGuid().ToString();
}
}
public class TenantUser<TKey> : IdentityUser<TKey>
where TKey : IEquatable<TKey>
{
public virtual TKey TenantId { get; set; }
public virtual Tenant<TKey> Tenant { get; set; }
/// <summary>
/// Navigation property for users logins.
/// </summary>
public new ICollection<TenantUserLogin<TKey>> Logins { get; } = new HashSet<TenantUserLogin<TKey>>();
}
public class TenantUserValidator : TenantUserValidator<string>
{
}
public class TenantUserValidator<TKey> : UserValidator<TenantUser<TKey>>
where TKey : IEquatable<TKey>
{
public override Task<IdentityResult> ValidateAsync(UserManager<TenantUser<TKey>> manager, TenantUser<TKey> user)
{
return base.ValidateAsync(manager, user);
}
}
}
using System;
namespace Microsoft.AspNet.Identity.EntityFramework.Tenant
{
public class TenantUserLogin : TenantUserLogin<string, string>
{
}
public class TenantUserLogin<TKey, TTenantKey> : IdentityUserLogin<TKey>
where TKey : IEquatable<TKey>
where TTenantKey : IEquatable<TTenantKey>
{
public TTenantKey TenantId { get; set; }
}
}
using Microsoft.Data.Entity;
using System;
using System.Threading;
using System.Threading.Tasks;
namespace Microsoft.AspNet.Identity.EntityFramework.Tenant
{
public class TenantUserStore : TenantUserStore<TenantIdentityDbContext>
{
public TenantUserStore(TenantIdentityDbContext context)
: base(context)
{
}
}
public class TenantUserStore<TContext> : TenantUserStore<TenantUser, IdentityRole, Tenant, TContext, TenantUserLogin, IdentityUserRole, IdentityUserClaim>
where TContext : TenantIdentityDbContext<TenantUser, IdentityRole, Tenant, string, TenantUserLogin, IdentityUserRole, IdentityUserClaim>
{
public TenantUserStore(TContext context)
: base(context)
{
}
}
public class TenantUserStore<TUser, TRole, TTenant, TContext, TUserLogin, TUserRole, TUserClaim>
: TenantUserStore<TUser, TRole, TTenant, TContext, string, TUserLogin, TUserRole, TUserClaim>
where TUser : TenantUser
where TRole : IdentityRole
where TTenant : Tenant
where TContext : TenantIdentityDbContext<TUser, TRole, TTenant, string, TUserLogin, TUserRole, TUserClaim>
where TUserLogin : TenantUserLogin, new()
where TUserRole : IdentityUserRole, new()
where TUserClaim : IdentityUserClaim, new()
{
public TenantUserStore(TContext context)
: base(context)
{
}
}
public class TenantUserStore<
TUser, TRole, TTenant, TContext, TKey,
TUserLogin, TUserRole, TUserClaim>
: UserStore<TUser, TRole, TContext, TKey>
where TUser : TenantUser<TKey>
where TRole : IdentityRole<TKey>
where TTenant : Tenant<TKey>
where TContext : TenantIdentityDbContext<
TUser, TRole, TTenant, TKey, TUserLogin, TUserRole, TUserClaim>
where TKey : IEquatable<TKey>
where TUserLogin : TenantUserLogin<TKey>, new()
where TUserRole : IdentityUserRole<TKey>, new()
where TUserClaim : IdentityUserClaim<TKey>, new()
{
public TenantUserStore(TContext context)
: base(context)
{
}
private DbSet<TUserLogin> _Logins;
public DbSet<TUserLogin> Logins
{
get
{
return _Logins ?? (_Logins = Context.Set<TUserLogin>());
}
}
public override Task<IdentityResult> CreateAsync(TUser user, CancellationToken cancellationToken = default(CancellationToken))
{
return base.CreateAsync(user, cancellationToken);
}
public override Task<TUser> FindByNameAsync(string normalizedUserName, CancellationToken cancellationToken = default(CancellationToken))
{
return base.FindByNameAsync(normalizedUserName, cancellationToken);
}
public override Task<TUser> FindByIdAsync(string userId, CancellationToken cancellationToken = default(CancellationToken))
{
return base.FindByIdAsync(userId, cancellationToken);
}
public override Task AddLoginAsync(TUser user, UserLoginInfo login, CancellationToken cancellationToken = default(CancellationToken))
{
return base.AddLoginAsync(user, login, cancellationToken);
}
public override Task<TUser> FindByEmailAsync(string normalizedEmail, CancellationToken cancellationToken = default(CancellationToken))
{
return base.FindByEmailAsync(normalizedEmail, cancellationToken);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment