diff --git a/examples/KriaSoft.AspNet.Identity.DbFirst/Startup.cs b/examples/KriaSoft.AspNet.Identity.DbFirst/Startup.cs index d93b96f..dffac04 100644 --- a/examples/KriaSoft.AspNet.Identity.DbFirst/Startup.cs +++ b/examples/KriaSoft.AspNet.Identity.DbFirst/Startup.cs @@ -22,7 +22,7 @@ public void Configuration(IAppBuilder app) app.CreatePerOwinContext(() => new ApplicationDbContext()); app.CreatePerOwinContext>( (IdentityFactoryOptions> options, IOwinContext context) => - new UserManager(new UserStore(context.Get()))); + new UserManager(new UserStore(context.Get(), "Example"))); // Enable the application to use bearer tokens to authenticate users app.UseOAuthBearerTokens(new OAuthAuthorizationServerOptions diff --git a/examples/KriaSoft.AspNet.Identity.DbFirst/Web.config b/examples/KriaSoft.AspNet.Identity.DbFirst/Web.config index 24620f7..2795817 100644 --- a/examples/KriaSoft.AspNet.Identity.DbFirst/Web.config +++ b/examples/KriaSoft.AspNet.Identity.DbFirst/Web.config @@ -5,7 +5,7 @@ --> - + diff --git a/src/KriaSoft.AspNet.Identity.Database/Publish Profiles/Local.publish.xml b/src/KriaSoft.AspNet.Identity.Database/Publish Profiles/Local.publish.xml index a175fc6..841daa5 100644 --- a/src/KriaSoft.AspNet.Identity.Database/Publish Profiles/Local.publish.xml +++ b/src/KriaSoft.AspNet.Identity.Database/Publish Profiles/Local.publish.xml @@ -4,7 +4,7 @@ True AspNetIdentity Database.sql - Data Source=(LocalDb)\v11.0;Integrated Security=True;Pooling=False;Connect Timeout=30 + Data Source=localhost\mssql2014;Integrated Security=True;Pooling=False;Connect Timeout=30 True True 1 diff --git a/src/KriaSoft.AspNet.Identity.Database/Reference Data/User.sql b/src/KriaSoft.AspNet.Identity.Database/Reference Data/User.sql index ad9e91c..9946e00 100644 --- a/src/KriaSoft.AspNet.Identity.Database/Reference Data/User.sql +++ b/src/KriaSoft.AspNet.Identity.Database/Reference Data/User.sql @@ -5,9 +5,9 @@ MERGE INTO [dbo].[User] AS Target USING (VALUES (1, N'admin', N'admin@example.com', 0, N'ACe+kHUdH61ms8NbkXSCXyV34CEP7tjfj93JrtlKRPfShGurFdAujQrmbVA7J9MDbg==', - N'9771f91d-b4a0-45e0-8971-899b907c5863', NULL, 0, 0, NULL, 0, 0), + N'9771f91d-b4a0-45e0-8971-899b907c5863', NULL, 0, 0, NULL, 0, 0, 'Example'), (2, N'user', N'user@example.com', 0, N'ACe+kHUdH61ms8NbkXSCXyV34CEP7tjfj93JrtlKRPfShGurFdAujQrmbVA7J9MDbg==', - N'9771f91d-b4a0-45e0-8971-899b907c5863', NULL, 0, 0, NULL, 0, 0) + N'9771f91d-b4a0-45e0-8971-899b907c5863', NULL, 0, 0, NULL, 0, 0, 'Example') ) AS Source ( [UserID], @@ -21,7 +21,8 @@ USING (VALUES [TwoFactorEnabled], [LockoutEndDateUtc], [LockoutEnabled], - [AccessFailedCount] + [AccessFailedCount], + [TenantId] ) ON Target.[UserID] = Source.[UserID] -- Update matched rows @@ -37,7 +38,8 @@ UPDATE SET [TwoFactorEnabled] = Source.[TwoFactorEnabled], [LockoutEndDateUtc] = Source.[LockoutEndDateUtc], [LockoutEnabled] = Source.[LockoutEnabled], - [AccessFailedCount] = Source.[AccessFailedCount] + [AccessFailedCount] = Source.[AccessFailedCount], + [TenantId] = Source.[TenantId] -- Insert new rows WHEN NOT MATCHED BY TARGET THEN INSERT ( @@ -52,7 +54,8 @@ INSERT ( [TwoFactorEnabled], [LockoutEndDateUtc], [LockoutEnabled], - [AccessFailedCount] + [AccessFailedCount], + [TenantId] ) VALUES ( [UserID], @@ -66,7 +69,8 @@ VALUES ( [TwoFactorEnabled], [LockoutEndDateUtc], [LockoutEnabled], - [AccessFailedCount] + [AccessFailedCount], + [TenantId] ); GO diff --git a/src/KriaSoft.AspNet.Identity.Database/Tables/User.sql b/src/KriaSoft.AspNet.Identity.Database/Tables/User.sql index 0928352..8174209 100644 --- a/src/KriaSoft.AspNet.Identity.Database/Tables/User.sql +++ b/src/KriaSoft.AspNet.Identity.Database/Tables/User.sql @@ -13,6 +13,7 @@ [LockoutEnabled] [dbo].[Flag] NOT NULL, [AccessFailedCount] INT NOT NULL, + [TenantId] NVARCHAR(50) NOT NULL, CONSTRAINT [PK_User_UserID] PRIMARY KEY CLUSTERED ([UserID] ASC), CONSTRAINT [UK_User_UserName] UNIQUE NONCLUSTERED ([UserName] ASC) ); diff --git a/src/KriaSoft.AspNet.Identity.EntityFramework/App.config b/src/KriaSoft.AspNet.Identity.EntityFramework/App.config index 063379a..79e8b6b 100644 --- a/src/KriaSoft.AspNet.Identity.EntityFramework/App.config +++ b/src/KriaSoft.AspNet.Identity.EntityFramework/App.config @@ -1,6 +1,6 @@  - + \ No newline at end of file diff --git a/src/KriaSoft.AspNet.Identity.EntityFramework/Model.Designer.cs b/src/KriaSoft.AspNet.Identity.EntityFramework/Model.Designer.cs index 25122be..b3b6ba5 100644 --- a/src/KriaSoft.AspNet.Identity.EntityFramework/Model.Designer.cs +++ b/src/KriaSoft.AspNet.Identity.EntityFramework/Model.Designer.cs @@ -1,4 +1,4 @@ -// T4 code generation is enabled for model 'C:\Projects\KriaSoft.AspNet.Identity\src\KriaSoft.AspNet.Identity.EntityFramework\Model.edmx'. +// T4 code generation is enabled for model 'C:\Projects\AspNet.Identity\src\KriaSoft.AspNet.Identity.EntityFramework\Model.edmx'. // To enable legacy code generation, change the value of the 'Code Generation Strategy' designer // property to 'Legacy ObjectContext'. This property is available in the Properties Window when the model // is open in the designer. diff --git a/src/KriaSoft.AspNet.Identity.EntityFramework/Model.edmx b/src/KriaSoft.AspNet.Identity.EntityFramework/Model.edmx index e912414..a12b867 100644 --- a/src/KriaSoft.AspNet.Identity.EntityFramework/Model.edmx +++ b/src/KriaSoft.AspNet.Identity.EntityFramework/Model.edmx @@ -4,7 +4,7 @@ - + @@ -21,6 +21,7 @@ + @@ -158,6 +159,7 @@ + @@ -247,6 +249,7 @@ + diff --git a/src/KriaSoft.AspNet.Identity.EntityFramework/User.cs b/src/KriaSoft.AspNet.Identity.EntityFramework/User.cs index cbd49da..2dfc27c 100644 --- a/src/KriaSoft.AspNet.Identity.EntityFramework/User.cs +++ b/src/KriaSoft.AspNet.Identity.EntityFramework/User.cs @@ -33,6 +33,7 @@ public User() public Nullable LockoutEndDateUtc { get; set; } public bool LockoutEnabled { get; set; } public int AccessFailedCount { get; set; } + public string TenantId { get; set; } public virtual ICollection Claims { get; set; } public virtual ICollection Logins { get; set; } diff --git a/src/KriaSoft.AspNet.Identity.EntityFramework/UserStore.cs b/src/KriaSoft.AspNet.Identity.EntityFramework/UserStore.cs index 41fae1e..e7cc6f3 100644 --- a/src/KriaSoft.AspNet.Identity.EntityFramework/UserStore.cs +++ b/src/KriaSoft.AspNet.Identity.EntityFramework/UserStore.cs @@ -20,16 +20,28 @@ public partial class UserStore : { private readonly ApplicationDbContext db; - public UserStore(ApplicationDbContext db) + public virtual string TenantId { get; set; } + /// + /// Throws exceptions if the state of the object is invalid or has been disposed. + /// + private void ThrowIfInvalid() { - if (db == null) - { - throw new ArgumentNullException("db"); - } + if (EqualityComparer.Default.Equals(TenantId, default(string))) + throw new InvalidOperationException("The TenantId has not been set."); + } + public UserStore(ApplicationDbContext db, string TenantId) + { + if (db == null) { throw new ArgumentNullException("db"); } this.db = db; + + if (TenantId == null) { throw new ArgumentNullException("TenantId"); } + this.TenantId = TenantId; + } + + //// IQueryableUserStore public IQueryable Users @@ -60,9 +72,11 @@ public Task FindByIdAsync(int userId) public Task FindByNameAsync(string userName) { + ThrowIfInvalid(); + return this.db.Users .Include(u => u.Logins).Include(u => u.Roles).Include(u => u.Claims) - .FirstOrDefaultAsync(u => u.UserName == userName); + .FirstOrDefaultAsync(u => u.UserName == userName && u.TenantId == TenantId); } public Task UpdateAsync(User user) @@ -128,6 +142,8 @@ public async Task FindAsync(UserLoginInfo login) throw new ArgumentNullException("login"); } + ThrowIfInvalid(); + var provider = login.LoginProvider; var key = login.ProviderKey; @@ -140,7 +156,7 @@ public async Task FindAsync(UserLoginInfo login) return await this.db.Users .Include(u => u.Logins).Include(u => u.Roles).Include(u => u.Claims) - .FirstOrDefaultAsync(u => u.Id.Equals(userLogin.UserId)); + .FirstOrDefaultAsync(u => u.Id.Equals(userLogin.UserId) && u.TenantId == TenantId); } public Task> GetLoginsAsync(User user) @@ -336,9 +352,11 @@ public Task SetSecurityStampAsync(User user, string stamp) public Task FindByEmailAsync(string email) { + ThrowIfInvalid(); + return this.db.Users .Include(u => u.Logins).Include(u => u.Roles).Include(u => u.Claims) - .FirstOrDefaultAsync(u => u.Email == email); + .FirstOrDefaultAsync(u => u.Email == email && u.TenantId == TenantId); } public Task GetEmailAsync(User user)