diff --git a/src/Core/Core.csproj b/src/Core/Core.csproj
index 7258a9a48..dd12b0726 100644
--- a/src/Core/Core.csproj
+++ b/src/Core/Core.csproj
@@ -46,7 +46,7 @@
-
+
diff --git a/src/Core/IdentityServer/ApiScopes.cs b/src/Core/IdentityServer/ApiScopes.cs
new file mode 100644
index 000000000..76a362027
--- /dev/null
+++ b/src/Core/IdentityServer/ApiScopes.cs
@@ -0,0 +1,20 @@
+using IdentityServer4.Models;
+using System.Collections.Generic;
+
+namespace Bit.Core.IdentityServer
+{
+ public class ApiScopes
+ {
+ public static IEnumerable GetApiScopes()
+ {
+ return new List
+ {
+ new ApiScope("api", "API Access"),
+ new ApiScope("api.push", "API Push Access"),
+ new ApiScope("api.licensing", "API Licensing Access"),
+ new ApiScope("api.organization", "API Organization Access"),
+ new ApiScope("internal", "Internal Access")
+ };
+ }
+ }
+}
diff --git a/src/Core/IdentityServer/AuthorizationCodeStore.cs b/src/Core/IdentityServer/AuthorizationCodeStore.cs
index 194c676e1..9ae7300e6 100644
--- a/src/Core/IdentityServer/AuthorizationCodeStore.cs
+++ b/src/Core/IdentityServer/AuthorizationCodeStore.cs
@@ -23,7 +23,8 @@ namespace Bit.Core.IdentityServer
public Task StoreAuthorizationCodeAsync(AuthorizationCode code)
{
- return CreateItemAsync(code, code.ClientId, code.Subject.GetSubjectId(), code.CreationTime, code.Lifetime);
+ return CreateItemAsync(code, code.ClientId, code.Subject.GetSubjectId(), code.SessionId,
+ code.Description, code.CreationTime, code.Lifetime);
}
public Task GetAuthorizationCodeAsync(string code)
diff --git a/src/Core/IdentityServer/ClientStore.cs b/src/Core/IdentityServer/ClientStore.cs
index 6d596ebb1..2005cebf7 100644
--- a/src/Core/IdentityServer/ClientStore.cs
+++ b/src/Core/IdentityServer/ClientStore.cs
@@ -47,7 +47,7 @@ namespace Bit.Core.IdentityServer
AllowedGrantTypes = GrantTypes.ClientCredentials,
AccessTokenLifetime = 3600 * 24,
Enabled = installation.Enabled,
- Claims = new List { new Claim(JwtClaimTypes.Subject, installation.Id.ToString()) }
+ Claims = new List { new ClientClaim(JwtClaimTypes.Subject, installation.Id.ToString()) }
};
}
}
@@ -70,7 +70,7 @@ namespace Bit.Core.IdentityServer
AllowedGrantTypes = GrantTypes.ClientCredentials,
AccessTokenLifetime = 3600 * 24,
Enabled = true,
- Claims = new List { new Claim(JwtClaimTypes.Subject, id) }
+ Claims = new List { new ClientClaim(JwtClaimTypes.Subject, id) }
};
}
}
@@ -92,7 +92,7 @@ namespace Bit.Core.IdentityServer
AllowedGrantTypes = GrantTypes.ClientCredentials,
AccessTokenLifetime = 3600 * 1,
Enabled = org.Enabled && org.UseApi,
- Claims = new List { new Claim(JwtClaimTypes.Subject, org.Id.ToString()) }
+ Claims = new List { new ClientClaim(JwtClaimTypes.Subject, org.Id.ToString()) }
};
}
}
diff --git a/src/Core/IdentityServer/PersistedGrantStore.cs b/src/Core/IdentityServer/PersistedGrantStore.cs
index 20768f40c..8365a995e 100644
--- a/src/Core/IdentityServer/PersistedGrantStore.cs
+++ b/src/Core/IdentityServer/PersistedGrantStore.cs
@@ -1,8 +1,6 @@
-using System;
-using System.Collections.Generic;
+using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
-using Bit.Core.Models.Table;
using Bit.Core.Repositories;
using IdentityServer4.Models;
using IdentityServer4.Stores;
@@ -19,13 +17,6 @@ namespace Bit.Core.IdentityServer
_grantRepository = grantRepository;
}
- public async Task> GetAllAsync(string subjectId)
- {
- var grants = await _grantRepository.GetManyAsync(subjectId);
- var pGrants = grants.Select(g => ToPersistedGrant(g));
- return pGrants;
- }
-
public async Task GetAsync(string key)
{
var grant = await _grantRepository.GetByKeyAsync(key);
@@ -38,19 +29,22 @@ namespace Bit.Core.IdentityServer
return pGrant;
}
- public async Task RemoveAllAsync(string subjectId, string clientId)
+ public async Task> GetAllAsync(PersistedGrantFilter filter)
{
- await _grantRepository.DeleteAsync(subjectId, clientId);
+ var grants = await _grantRepository.GetManyAsync(filter.SubjectId, filter.SessionId,
+ filter.ClientId, filter.Type);
+ var pGrants = grants.Select(g => ToPersistedGrant(g));
+ return pGrants;
}
- public async Task RemoveAllAsync(string subjectId, string clientId, string type)
+ public async Task RemoveAllAsync(PersistedGrantFilter filter)
{
- await _grantRepository.DeleteAsync(subjectId, clientId, type);
+ await _grantRepository.DeleteManyAsync(filter.SubjectId, filter.SessionId, filter.ClientId, filter.Type);
}
public async Task RemoveAsync(string key)
{
- await _grantRepository.DeleteAsync(key);
+ await _grantRepository.DeleteByKeyAsync(key);
}
public async Task StoreAsync(PersistedGrant pGrant)
@@ -59,30 +53,36 @@ namespace Bit.Core.IdentityServer
await _grantRepository.SaveAsync(grant);
}
- private Grant ToGrant(PersistedGrant pGrant)
+ private Models.Table.Grant ToGrant(PersistedGrant pGrant)
{
- return new Grant
+ return new Models.Table.Grant
{
Key = pGrant.Key,
Type = pGrant.Type,
SubjectId = pGrant.SubjectId,
+ SessionId = pGrant.SessionId,
ClientId = pGrant.ClientId,
+ Description = pGrant.Description,
CreationDate = pGrant.CreationTime,
ExpirationDate = pGrant.Expiration,
+ ConsumedDate = pGrant.ConsumedTime,
Data = pGrant.Data
};
}
- private PersistedGrant ToPersistedGrant(Grant grant)
+ private PersistedGrant ToPersistedGrant(Models.Table.Grant grant)
{
return new PersistedGrant
{
Key = grant.Key,
Type = grant.Type,
SubjectId = grant.SubjectId,
+ SessionId = grant.SessionId,
ClientId = grant.ClientId,
+ Description = grant.Description,
CreationTime = grant.CreationDate,
Expiration = grant.ExpirationDate,
+ ConsumedTime = grant.ConsumedDate,
Data = grant.Data
};
}
diff --git a/src/Core/IdentityServer/ProfileService.cs b/src/Core/IdentityServer/ProfileService.cs
index 3801414b5..4d2affd36 100644
--- a/src/Core/IdentityServer/ProfileService.cs
+++ b/src/Core/IdentityServer/ProfileService.cs
@@ -14,19 +14,16 @@ namespace Bit.Core.IdentityServer
public class ProfileService : IProfileService
{
private readonly IUserService _userService;
- private readonly IUserRepository _userRepository;
private readonly IOrganizationUserRepository _organizationUserRepository;
private readonly ILicensingService _licensingService;
private readonly CurrentContext _currentContext;
public ProfileService(
- IUserRepository userRepository,
IUserService userService,
IOrganizationUserRepository organizationUserRepository,
ILicensingService licensingService,
CurrentContext currentContext)
{
- _userRepository = userRepository;
_userService = userService;
_organizationUserRepository = organizationUserRepository;
_licensingService = licensingService;
@@ -46,7 +43,8 @@ namespace Bit.Core.IdentityServer
{
new Claim("premium", isPremium ? "true" : "false", ClaimValueTypes.Boolean),
new Claim(JwtClaimTypes.Email, user.Email),
- new Claim(JwtClaimTypes.EmailVerified, user.EmailVerified ? "true" : "false", ClaimValueTypes.Boolean),
+ new Claim(JwtClaimTypes.EmailVerified, user.EmailVerified ? "true" : "false",
+ ClaimValueTypes.Boolean),
new Claim("sstamp", user.SecurityStamp)
});
@@ -96,13 +94,14 @@ namespace Bit.Core.IdentityServer
// filter out any of the new claims
var existingClaimsToKeep = existingClaims
- .Where(c => !c.Type.StartsWith("org") && (newClaims.Count == 0 || !newClaims.Any(nc => nc.Type == c.Type)))
+ .Where(c => !c.Type.StartsWith("org") &&
+ (newClaims.Count == 0 || !newClaims.Any(nc => nc.Type == c.Type)))
.ToList();
newClaims.AddRange(existingClaimsToKeep);
if (newClaims.Any())
{
- context.AddRequestedClaims(newClaims);
+ context.IssuedClaims.AddRange(newClaims);
}
}
diff --git a/src/Core/Models/Table/Grant.cs b/src/Core/Models/Table/Grant.cs
index 95fd16957..785fa5d1a 100644
--- a/src/Core/Models/Table/Grant.cs
+++ b/src/Core/Models/Table/Grant.cs
@@ -7,9 +7,12 @@ namespace Bit.Core.Models.Table
public string Key { get; set; }
public string Type { get; set; }
public string SubjectId { get; set; }
+ public string SessionId { get; set; }
public string ClientId { get; set; }
+ public string Description { get; set; }
public DateTime CreationDate { get; set; }
public DateTime? ExpirationDate { get; set; }
+ public DateTime? ConsumedDate { get; set; }
public string Data { get; set; }
}
}
diff --git a/src/Core/Repositories/IGrantRepository.cs b/src/Core/Repositories/IGrantRepository.cs
index ba86c6b28..86730040a 100644
--- a/src/Core/Repositories/IGrantRepository.cs
+++ b/src/Core/Repositories/IGrantRepository.cs
@@ -8,10 +8,9 @@ namespace Bit.Core.Repositories
public interface IGrantRepository
{
Task GetByKeyAsync(string key);
- Task> GetManyAsync(string subjectId);
+ Task> GetManyAsync(string subjectId, string sessionId, string clientId, string type);
Task SaveAsync(Grant obj);
- Task DeleteAsync(string key);
- Task DeleteAsync(string subjectId, string clientId);
- Task DeleteAsync(string subjectId, string clientId, string type);
+ Task DeleteByKeyAsync(string key);
+ Task DeleteManyAsync(string subjectId, string sessionId, string clientId, string type);
}
}
diff --git a/src/Core/Repositories/SqlServer/GrantRepository.cs b/src/Core/Repositories/SqlServer/GrantRepository.cs
index 39fe540ff..39b71bb67 100644
--- a/src/Core/Repositories/SqlServer/GrantRepository.cs
+++ b/src/Core/Repositories/SqlServer/GrantRepository.cs
@@ -1,5 +1,4 @@
-using System;
-using System.Collections.Generic;
+using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;
@@ -32,13 +31,14 @@ namespace Bit.Core.Repositories.SqlServer
}
}
- public async Task> GetManyAsync(string subjectId)
+ public async Task> GetManyAsync(string subjectId, string sessionId,
+ string clientId, string type)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.QueryAsync(
- "[dbo].[Grant_ReadBySubjectId]",
- new { SubjectId = subjectId },
+ "[dbo].[Grant_Read]",
+ new { SubjectId = subjectId, SessionId = sessionId, ClientId = clientId, Type = type },
commandType: CommandType.StoredProcedure);
return results.ToList();
@@ -56,7 +56,7 @@ namespace Bit.Core.Repositories.SqlServer
}
}
- public async Task DeleteAsync(string key)
+ public async Task DeleteByKeyAsync(string key)
{
using (var connection = new SqlConnection(ConnectionString))
{
@@ -67,24 +67,13 @@ namespace Bit.Core.Repositories.SqlServer
}
}
- public async Task DeleteAsync(string subjectId, string clientId)
+ public async Task DeleteManyAsync(string subjectId, string sessionId, string clientId, string type)
{
using (var connection = new SqlConnection(ConnectionString))
{
await connection.ExecuteAsync(
- "[dbo].[Grant_DeleteBySubjectIdClientId]",
- new { SubjectId = subjectId, ClientId = clientId },
- commandType: CommandType.StoredProcedure);
- }
- }
-
- public async Task DeleteAsync(string subjectId, string clientId, string type)
- {
- using (var connection = new SqlConnection(ConnectionString))
- {
- await connection.ExecuteAsync(
- "[dbo].[Grant_DeleteBySubjectIdClientIdType]",
- new { SubjectId = subjectId, ClientId = clientId, Type = type },
+ "[dbo].[Grant_Delete]",
+ new { SubjectId = subjectId, SessionId = sessionId, ClientId = clientId, Type = type },
commandType: CommandType.StoredProcedure);
}
}
diff --git a/src/Identity/Controllers/AccountController.cs b/src/Identity/Controllers/AccountController.cs
index 2b81b3c4d..b89856470 100644
--- a/src/Identity/Controllers/AccountController.cs
+++ b/src/Identity/Controllers/AccountController.cs
@@ -7,6 +7,7 @@ using Bit.Core.Models.Table;
using Bit.Core.Repositories;
using Bit.Identity.Models;
using IdentityModel;
+using IdentityServer4;
using IdentityServer4.Services;
using IdentityServer4.Stores;
using Microsoft.AspNetCore.Authentication;
@@ -132,8 +133,12 @@ namespace Bit.Identity.Controllers
ProcessLoginCallbackForOidc(result, additionalLocalClaims, localSignInProps);
// issue authentication cookie for user
- await HttpContext.SignInAsync(user.Id.ToString(), user.Email, provider,
- localSignInProps, additionalLocalClaims.ToArray());
+ await HttpContext.SignInAsync(new IdentityServerUser(user.Id.ToString())
+ {
+ DisplayName = user.Email,
+ IdentityProvider = provider,
+ AdditionalClaims = additionalLocalClaims.ToArray()
+ }, localSignInProps);
// delete temporary cookie used during external authentication
await HttpContext.SignOutAsync(IdentityServer4.IdentityServerConstants.ExternalCookieAuthenticationScheme);
@@ -144,7 +149,7 @@ namespace Bit.Identity.Controllers
var context = await _interaction.GetAuthorizationContextAsync(returnUrl);
if (context != null)
{
- if (await IsPkceClientAsync(context.ClientId))
+ if (await IsPkceClientAsync(context.Client.ClientId))
{
// if the client is PKCE then we assume it's native, so this change in how to
// return the response is for better UX for the end user.
diff --git a/src/Identity/Startup.cs b/src/Identity/Startup.cs
index d4ac92be3..119a0ac40 100644
--- a/src/Identity/Startup.cs
+++ b/src/Identity/Startup.cs
@@ -65,6 +65,19 @@ namespace Bit.Identity
services.AddSingleton();
}
+ // Cookies
+ if (Environment.IsDevelopment())
+ {
+ services.Configure(options =>
+ {
+ options.MinimumSameSitePolicy = Microsoft.AspNetCore.Http.SameSiteMode.Unspecified;
+ options.OnAppendCookie = ctx =>
+ {
+ ctx.CookieOptions.SameSite = Microsoft.AspNetCore.Http.SameSiteMode.Unspecified;
+ };
+ });
+ }
+
JwtSecurityTokenHandler.DefaultMapInboundClaims = false;
// Authentication
@@ -133,6 +146,12 @@ namespace Bit.Identity
app.UseForwardedHeaders(globalSettings);
}
+ if (env.IsDevelopment())
+ {
+ app.UseDeveloperExceptionPage();
+ app.UseCookiePolicy();
+ }
+
// Add static files to the request pipeline.
app.UseStaticFiles();
@@ -172,9 +191,14 @@ namespace Bit.Identity
options.Endpoints.EnableTokenRevocationEndpoint = false;
options.IssuerUri = $"{issuerUri.Scheme}://{issuerUri.Host}";
options.Caching.ClientStoreExpiration = new TimeSpan(0, 5, 0);
+ if(env.IsDevelopment())
+ {
+ options.Authentication.CookieSameSiteMode = Microsoft.AspNetCore.Http.SameSiteMode.Unspecified;
+ }
})
.AddInMemoryCaching()
.AddInMemoryApiResources(ApiResources.GetApiResources())
+ .AddInMemoryApiScopes(ApiScopes.GetApiScopes())
.AddClientStoreCache()
.AddCustomTokenRequestValidator()
.AddProfileService()
diff --git a/src/Sql/Sql.sqlproj b/src/Sql/Sql.sqlproj
index ee4fd6978..afc9cf443 100644
--- a/src/Sql/Sql.sqlproj
+++ b/src/Sql/Sql.sqlproj
@@ -109,12 +109,11 @@
-
+
-
-
+
diff --git a/src/Sql/dbo/Stored Procedures/Grant_Delete.sql b/src/Sql/dbo/Stored Procedures/Grant_Delete.sql
new file mode 100644
index 000000000..59a2d1882
--- /dev/null
+++ b/src/Sql/dbo/Stored Procedures/Grant_Delete.sql
@@ -0,0 +1,18 @@
+CREATE PROCEDURE [dbo].[Grant_Delete]
+ @SubjectId NVARCHAR(200),
+ @SessionId NVARCHAR(100),
+ @ClientId NVARCHAR(200),
+ @Type NVARCHAR(50)
+AS
+BEGIN
+ SET NOCOUNT ON
+
+ DELETE
+ FROM
+ [dbo].[Grant]
+ WHERE
+ (@SubjectId IS NULL OR [SubjectId] = @SubjectId)
+ AND (@ClientId IS NULL OR [ClientId] = @ClientId)
+ AND (@SessionId IS NULL OR [SessionId] = @SessionId)
+ AND (@Type IS NULL OR [Type] = @Type)
+END
diff --git a/src/Sql/dbo/Stored Procedures/Grant_DeleteBySubjectIdClientId.sql b/src/Sql/dbo/Stored Procedures/Grant_DeleteBySubjectIdClientId.sql
deleted file mode 100644
index 48d5036cb..000000000
--- a/src/Sql/dbo/Stored Procedures/Grant_DeleteBySubjectIdClientId.sql
+++ /dev/null
@@ -1,14 +0,0 @@
-CREATE PROCEDURE [dbo].[Grant_DeleteBySubjectIdClientId]
- @SubjectId NVARCHAR(50),
- @ClientId NVARCHAR(50)
-AS
-BEGIN
- SET NOCOUNT ON
-
- DELETE
- FROM
- [dbo].[Grant]
- WHERE
- [SubjectId] = @SubjectId
- AND [ClientId] = @ClientId
-END
diff --git a/src/Sql/dbo/Stored Procedures/Grant_DeleteBySubjectIdClientIdType.sql b/src/Sql/dbo/Stored Procedures/Grant_DeleteBySubjectIdClientIdType.sql
deleted file mode 100644
index 5f7c48373..000000000
--- a/src/Sql/dbo/Stored Procedures/Grant_DeleteBySubjectIdClientIdType.sql
+++ /dev/null
@@ -1,16 +0,0 @@
-CREATE PROCEDURE [dbo].[Grant_DeleteBySubjectIdClientIdType]
- @SubjectId NVARCHAR(50),
- @ClientId NVARCHAR(50),
- @Type NVARCHAR(50)
-AS
-BEGIN
- SET NOCOUNT ON
-
- DELETE
- FROM
- [dbo].[Grant]
- WHERE
- [SubjectId] = @SubjectId
- AND [ClientId] = @ClientId
- AND [Type] = @Type
-END
diff --git a/src/Sql/dbo/Stored Procedures/Grant_Read.sql b/src/Sql/dbo/Stored Procedures/Grant_Read.sql
new file mode 100644
index 000000000..9cbd617ce
--- /dev/null
+++ b/src/Sql/dbo/Stored Procedures/Grant_Read.sql
@@ -0,0 +1,19 @@
+CREATE PROCEDURE [dbo].[Grant_Read]
+ @SubjectId NVARCHAR(200),
+ @SessionId NVARCHAR(100),
+ @ClientId NVARCHAR(200),
+ @Type NVARCHAR(50)
+AS
+BEGIN
+ SET NOCOUNT ON
+
+ SELECT
+ *
+ FROM
+ [dbo].[GrantView]
+ WHERE
+ (@SubjectId IS NULL OR [SubjectId] = @SubjectId)
+ AND (@ClientId IS NULL OR [ClientId] = @ClientId)
+ AND (@SessionId IS NULL OR [SessionId] = @SessionId)
+ AND (@Type IS NULL OR [Type] = @Type)
+END
diff --git a/src/Sql/dbo/Stored Procedures/Grant_ReadBySubjectId.sql b/src/Sql/dbo/Stored Procedures/Grant_ReadBySubjectId.sql
deleted file mode 100644
index f4270b2e6..000000000
--- a/src/Sql/dbo/Stored Procedures/Grant_ReadBySubjectId.sql
+++ /dev/null
@@ -1,13 +0,0 @@
-CREATE PROCEDURE [dbo].[Grant_ReadBySubjectId]
- @SubjectId NVARCHAR(50)
-AS
-BEGIN
- SET NOCOUNT ON
-
- SELECT
- *
- FROM
- [dbo].[GrantView]
- WHERE
- [SubjectId] = @SubjectId
-END
diff --git a/src/Sql/dbo/Stored Procedures/Grant_Save.sql b/src/Sql/dbo/Stored Procedures/Grant_Save.sql
index 097fd6c25..bac37f140 100644
--- a/src/Sql/dbo/Stored Procedures/Grant_Save.sql
+++ b/src/Sql/dbo/Stored Procedures/Grant_Save.sql
@@ -1,10 +1,13 @@
CREATE PROCEDURE [dbo].[Grant_Save]
@Key NVARCHAR(200),
@Type NVARCHAR(50),
- @SubjectId NVARCHAR(50),
- @ClientId NVARCHAR(50),
+ @SubjectId NVARCHAR(200),
+ @SessionId NVARCHAR(100),
+ @ClientId NVARCHAR(200),
+ @Description NVARCHAR(200),
@CreationDate DATETIME2,
@ExpirationDate DATETIME2,
+ @ConsumedDate DATETIME2,
@Data NVARCHAR(MAX)
AS
BEGIN
@@ -19,9 +22,12 @@ BEGIN
@Key,
@Type,
@SubjectId,
+ @SessionId,
@ClientId,
+ @Description,
@CreationDate,
@ExpirationDate,
+ @ConsumedDate,
@Data
)
) AS [Source]
@@ -29,9 +35,12 @@ BEGIN
[Key],
[Type],
[SubjectId],
+ [SessionId],
[ClientId],
+ [Description],
[CreationDate],
[ExpirationDate],
+ [ConsumedDate],
[Data]
)
ON
@@ -41,9 +50,12 @@ BEGIN
SET
[Type] = [Source].[Type],
[SubjectId] = [Source].[SubjectId],
+ [SessionId] = [Source].[SessionId],
[ClientId] = [Source].[ClientId],
+ [Description] = [Source].[Description],
[CreationDate] = [Source].[CreationDate],
[ExpirationDate] = [Source].[ExpirationDate],
+ [ConsumedDate] = [Source].[ConsumedDate],
[Data] = [Source].[Data]
WHEN NOT MATCHED THEN
INSERT
@@ -51,9 +63,12 @@ BEGIN
[Key],
[Type],
[SubjectId],
+ [SessionId],
[ClientId],
+ [Description],
[CreationDate],
[ExpirationDate],
+ [ConsumedDate],
[Data]
)
VALUES
@@ -61,9 +76,12 @@ BEGIN
[Source].[Key],
[Source].[Type],
[Source].[SubjectId],
+ [Source].[SessionId],
[Source].[ClientId],
+ [Source].[Description],
[Source].[CreationDate],
[Source].[ExpirationDate],
+ [Source].[ConsumedDate],
[Source].[Data]
)
;
diff --git a/src/Sql/dbo/Tables/Grant.sql b/src/Sql/dbo/Tables/Grant.sql
index 6ec355229..02eda444b 100644
--- a/src/Sql/dbo/Tables/Grant.sql
+++ b/src/Sql/dbo/Tables/Grant.sql
@@ -1,10 +1,13 @@
CREATE TABLE [dbo].[Grant] (
[Key] NVARCHAR (200) NOT NULL,
- [Type] NVARCHAR (50) NULL,
- [SubjectId] NVARCHAR (50) NULL,
- [ClientId] NVARCHAR (50) NOT NULL,
+ [Type] NVARCHAR (50) NOT NULL,
+ [SubjectId] NVARCHAR (200) NULL,
+ [SessionId] NVARCHAR (100) NULL,
+ [ClientId] NVARCHAR (200) NOT NULL,
+ [Description] NVARCHAR (200) NULL,
[CreationDate] DATETIME2 (7) NOT NULL,
[ExpirationDate] DATETIME2 (7) NULL,
+ [ConsumedDate] DATETIME2 (7) NULL,
[Data] NVARCHAR (MAX) NOT NULL,
CONSTRAINT [PK_Grant] PRIMARY KEY CLUSTERED ([Key] ASC)
);
@@ -14,6 +17,10 @@ GO
CREATE NONCLUSTERED INDEX [IX_Grant_SubjectId_ClientId_Type]
ON [dbo].[Grant]([SubjectId] ASC, [ClientId] ASC, [Type] ASC);
+GO
+CREATE NONCLUSTERED INDEX [IX_Grant_SubjectId_SessionId_Type]
+ ON [dbo].[Grant]([SubjectId] ASC, [SessionId] ASC, [Type] ASC);
+
GO
CREATE NONCLUSTERED INDEX [IX_Grant_ExpirationDate]
ON [dbo].[Grant]([ExpirationDate] ASC);
diff --git a/util/Migrator/DbScripts/2020-07-30_00_IdServerv4.sql b/util/Migrator/DbScripts/2020-07-30_00_IdServerv4.sql
new file mode 100644
index 000000000..265638347
--- /dev/null
+++ b/util/Migrator/DbScripts/2020-07-30_00_IdServerv4.sql
@@ -0,0 +1,267 @@
+IF EXISTS (
+ SELECT * FROM sys.indexes WHERE [Name] = 'IX_Grant_SubjectId_ClientId_Type'
+ AND object_id = OBJECT_ID('[dbo].[Grant]')
+)
+BEGIN
+ DROP INDEX [IX_Grant_SubjectId_ClientId_Type]
+ ON [dbo].[Grant]
+END
+GO
+
+IF EXISTS (
+ SELECT * FROM sys.indexes WHERE [Name] = 'IX_Grant_SubjectId_SessionId_Type'
+ AND object_id = OBJECT_ID('[dbo].[Grant]')
+)
+BEGIN
+ DROP INDEX [IX_Grant_SubjectId_SessionId_Type]
+ ON [dbo].[Grant]
+END
+GO
+
+IF COL_LENGTH('[dbo].[Grant]', 'SessionId') IS NULL
+BEGIN
+ ALTER TABLE
+ [dbo].[Grant]
+ ADD
+ [SessionId] NVARCHAR (100) NULL
+END
+GO
+
+IF COL_LENGTH('[dbo].[Grant]', 'Description') IS NULL
+BEGIN
+ ALTER TABLE
+ [dbo].[Grant]
+ ADD
+ [Description] NVARCHAR (200) NULL
+END
+GO
+
+IF COL_LENGTH('[dbo].[Grant]', 'ConsumedDate') IS NULL
+BEGIN
+ ALTER TABLE
+ [dbo].[Grant]
+ ADD
+ [ConsumedDate] DATETIME2 (7) NULL
+END
+GO
+
+ALTER TABLE
+ [dbo].[Grant]
+ALTER COLUMN
+ [Type] NVARCHAR (50) NOT NULL
+GO
+
+ALTER TABLE
+ [dbo].[Grant]
+ALTER COLUMN
+ [SubjectId] NVARCHAR (200) NULL
+GO
+
+ALTER TABLE
+ [dbo].[Grant]
+ALTER COLUMN
+ [ClientId] NVARCHAR (200) NOT NULL
+GO
+
+IF EXISTS(SELECT * FROM sys.views WHERE [Name] = 'GrantView')
+BEGIN
+ DROP VIEW [dbo].[GrantView]
+END
+GO
+
+CREATE VIEW [dbo].[GrantView]
+AS
+SELECT
+ *
+FROM
+ [dbo].[Grant]
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM sys.indexes WHERE [Name] = 'IX_Grant_SubjectId_ClientId_Type'
+ AND object_id = OBJECT_ID('[dbo].[Grant]')
+)
+BEGIN
+ CREATE NONCLUSTERED INDEX [IX_Grant_SubjectId_ClientId_Type]
+ ON [dbo].[Grant]([SubjectId] ASC, [ClientId] ASC, [Type] ASC)
+ -- TODO WITH ONLINE
+END
+GO
+
+IF NOT EXISTS (
+ SELECT * FROM sys.indexes WHERE [Name] = 'IX_Grant_SubjectId_SessionId_Type'
+ AND object_id = OBJECT_ID('[dbo].[Grant]')
+)
+BEGIN
+ CREATE NONCLUSTERED INDEX [IX_Grant_SubjectId_SessionId_Type]
+ ON [dbo].[Grant]([SubjectId] ASC, [SessionId] ASC, [Type] ASC)
+ -- TODO WITH ONLINE
+END
+GO
+
+IF OBJECT_ID('[dbo].[Grant_Delete]') IS NOT NULL
+BEGIN
+ DROP PROCEDURE [dbo].[Grant_Delete]
+END
+GO
+
+CREATE PROCEDURE [dbo].[Grant_Delete]
+ @SubjectId NVARCHAR(200),
+ @SessionId NVARCHAR(100),
+ @ClientId NVARCHAR(200),
+ @Type NVARCHAR(50)
+AS
+BEGIN
+ SET NOCOUNT ON
+
+ DELETE
+ FROM
+ [dbo].[Grant]
+ WHERE
+ (@SubjectId IS NULL OR [SubjectId] = @SubjectId)
+ AND (@ClientId IS NULL OR [ClientId] = @ClientId)
+ AND (@SessionId IS NULL OR [SessionId] = @SessionId)
+ AND (@Type IS NULL OR [Type] = @Type)
+END
+GO
+
+IF OBJECT_ID('[dbo].[Grant_Read]') IS NOT NULL
+BEGIN
+ DROP PROCEDURE [dbo].[Grant_Read]
+END
+GO
+
+CREATE PROCEDURE [dbo].[Grant_Read]
+ @SubjectId NVARCHAR(200),
+ @SessionId NVARCHAR(100),
+ @ClientId NVARCHAR(200),
+ @Type NVARCHAR(50)
+AS
+BEGIN
+ SET NOCOUNT ON
+
+ SELECT
+ *
+ FROM
+ [dbo].[GrantView]
+ WHERE
+ (@SubjectId IS NULL OR [SubjectId] = @SubjectId)
+ AND (@ClientId IS NULL OR [ClientId] = @ClientId)
+ AND (@SessionId IS NULL OR [SessionId] = @SessionId)
+ AND (@Type IS NULL OR [Type] = @Type)
+END
+GO
+
+IF OBJECT_ID('[dbo].[Grant_Save]') IS NOT NULL
+BEGIN
+ DROP PROCEDURE [dbo].[Grant_Save]
+END
+GO
+
+CREATE PROCEDURE [dbo].[Grant_Save]
+ @Key NVARCHAR(200),
+ @Type NVARCHAR(50),
+ @SubjectId NVARCHAR(200),
+ @SessionId NVARCHAR(100),
+ @ClientId NVARCHAR(200),
+ @Description NVARCHAR(200),
+ @CreationDate DATETIME2,
+ @ExpirationDate DATETIME2,
+ @ConsumedDate DATETIME2,
+ @Data NVARCHAR(MAX)
+AS
+BEGIN
+ SET NOCOUNT ON
+
+ MERGE
+ [dbo].[Grant] AS [Target]
+ USING
+ (
+ VALUES
+ (
+ @Key,
+ @Type,
+ @SubjectId,
+ @SessionId,
+ @ClientId,
+ @Description,
+ @CreationDate,
+ @ExpirationDate,
+ @ConsumedDate,
+ @Data
+ )
+ ) AS [Source]
+ (
+ [Key],
+ [Type],
+ [SubjectId],
+ [SessionId],
+ [ClientId],
+ [Description],
+ [CreationDate],
+ [ExpirationDate],
+ [ConsumedDate],
+ [Data]
+ )
+ ON
+ [Target].[Key] = [Source].[Key]
+ WHEN MATCHED THEN
+ UPDATE
+ SET
+ [Type] = [Source].[Type],
+ [SubjectId] = [Source].[SubjectId],
+ [SessionId] = [Source].[SessionId],
+ [ClientId] = [Source].[ClientId],
+ [Description] = [Source].[Description],
+ [CreationDate] = [Source].[CreationDate],
+ [ExpirationDate] = [Source].[ExpirationDate],
+ [ConsumedDate] = [Source].[ConsumedDate],
+ [Data] = [Source].[Data]
+ WHEN NOT MATCHED THEN
+ INSERT
+ (
+ [Key],
+ [Type],
+ [SubjectId],
+ [SessionId],
+ [ClientId],
+ [Description],
+ [CreationDate],
+ [ExpirationDate],
+ [ConsumedDate],
+ [Data]
+ )
+ VALUES
+ (
+ [Source].[Key],
+ [Source].[Type],
+ [Source].[SubjectId],
+ [Source].[SessionId],
+ [Source].[ClientId],
+ [Source].[Description],
+ [Source].[CreationDate],
+ [Source].[ExpirationDate],
+ [Source].[ConsumedDate],
+ [Source].[Data]
+ )
+ ;
+END
+GO
+
+IF OBJECT_ID('[dbo].[Grant_DeleteBySubjectIdClientId]') IS NOT NULL
+BEGIN
+ DROP PROCEDURE [dbo].[Grant_DeleteBySubjectIdClientId]
+END
+GO
+
+IF OBJECT_ID('[dbo].[Grant_DeleteBySubjectIdClientIdType]') IS NOT NULL
+BEGIN
+ DROP PROCEDURE [dbo].[Grant_DeleteBySubjectIdClientIdType]
+END
+GO
+
+IF OBJECT_ID('[dbo].[Grant_ReadBySubjectId]') IS NOT NULL
+BEGIN
+ DROP PROCEDURE [dbo].[Grant_ReadBySubjectId]
+END
+GO