1
0
mirror of https://github.com/bitwarden/server.git synced 2024-11-28 13:15:12 +01:00

upgrade identity server 4 to v4 (#842)

* upgrade identity server 4 to v4

* remove script ref
This commit is contained in:
Kyle Spearrin 2020-07-30 17:00:13 -04:00 committed by GitHub
parent 22eb8316f2
commit 623cd36bd4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
20 changed files with 432 additions and 107 deletions

View File

@ -46,7 +46,7 @@
<PackageReference Include="Serilog.Extensions.Logging.File" Version="2.0.0" /> <PackageReference Include="Serilog.Extensions.Logging.File" Version="2.0.0" />
<PackageReference Include="Serilog.Sinks.AzureDocumentDB" Version="3.8.0" /> <PackageReference Include="Serilog.Sinks.AzureDocumentDB" Version="3.8.0" />
<PackageReference Include="Sentry.Serilog" Version="2.1.5" /> <PackageReference Include="Sentry.Serilog" Version="2.1.5" />
<PackageReference Include="IdentityServer4" Version="3.1.3" /> <PackageReference Include="IdentityServer4" Version="4.0.4" />
<PackageReference Include="Dapper" Version="2.0.35" /> <PackageReference Include="Dapper" Version="2.0.35" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="Serilog.Sinks.SyslogMessages" Version="1.0.5" /> <PackageReference Include="Serilog.Sinks.SyslogMessages" Version="1.0.5" />

View File

@ -0,0 +1,20 @@
using IdentityServer4.Models;
using System.Collections.Generic;
namespace Bit.Core.IdentityServer
{
public class ApiScopes
{
public static IEnumerable<ApiScope> GetApiScopes()
{
return new List<ApiScope>
{
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")
};
}
}
}

View File

@ -23,7 +23,8 @@ namespace Bit.Core.IdentityServer
public Task<string> StoreAuthorizationCodeAsync(AuthorizationCode code) public Task<string> 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<AuthorizationCode> GetAuthorizationCodeAsync(string code) public Task<AuthorizationCode> GetAuthorizationCodeAsync(string code)

View File

@ -47,7 +47,7 @@ namespace Bit.Core.IdentityServer
AllowedGrantTypes = GrantTypes.ClientCredentials, AllowedGrantTypes = GrantTypes.ClientCredentials,
AccessTokenLifetime = 3600 * 24, AccessTokenLifetime = 3600 * 24,
Enabled = installation.Enabled, Enabled = installation.Enabled,
Claims = new List<Claim> { new Claim(JwtClaimTypes.Subject, installation.Id.ToString()) } Claims = new List<ClientClaim> { new ClientClaim(JwtClaimTypes.Subject, installation.Id.ToString()) }
}; };
} }
} }
@ -70,7 +70,7 @@ namespace Bit.Core.IdentityServer
AllowedGrantTypes = GrantTypes.ClientCredentials, AllowedGrantTypes = GrantTypes.ClientCredentials,
AccessTokenLifetime = 3600 * 24, AccessTokenLifetime = 3600 * 24,
Enabled = true, Enabled = true,
Claims = new List<Claim> { new Claim(JwtClaimTypes.Subject, id) } Claims = new List<ClientClaim> { new ClientClaim(JwtClaimTypes.Subject, id) }
}; };
} }
} }
@ -92,7 +92,7 @@ namespace Bit.Core.IdentityServer
AllowedGrantTypes = GrantTypes.ClientCredentials, AllowedGrantTypes = GrantTypes.ClientCredentials,
AccessTokenLifetime = 3600 * 1, AccessTokenLifetime = 3600 * 1,
Enabled = org.Enabled && org.UseApi, Enabled = org.Enabled && org.UseApi,
Claims = new List<Claim> { new Claim(JwtClaimTypes.Subject, org.Id.ToString()) } Claims = new List<ClientClaim> { new ClientClaim(JwtClaimTypes.Subject, org.Id.ToString()) }
}; };
} }
} }

View File

@ -1,8 +1,6 @@
using System; using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Bit.Core.Models.Table;
using Bit.Core.Repositories; using Bit.Core.Repositories;
using IdentityServer4.Models; using IdentityServer4.Models;
using IdentityServer4.Stores; using IdentityServer4.Stores;
@ -19,13 +17,6 @@ namespace Bit.Core.IdentityServer
_grantRepository = grantRepository; _grantRepository = grantRepository;
} }
public async Task<IEnumerable<PersistedGrant>> GetAllAsync(string subjectId)
{
var grants = await _grantRepository.GetManyAsync(subjectId);
var pGrants = grants.Select(g => ToPersistedGrant(g));
return pGrants;
}
public async Task<PersistedGrant> GetAsync(string key) public async Task<PersistedGrant> GetAsync(string key)
{ {
var grant = await _grantRepository.GetByKeyAsync(key); var grant = await _grantRepository.GetByKeyAsync(key);
@ -38,19 +29,22 @@ namespace Bit.Core.IdentityServer
return pGrant; return pGrant;
} }
public async Task RemoveAllAsync(string subjectId, string clientId) public async Task<IEnumerable<PersistedGrant>> 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) public async Task RemoveAsync(string key)
{ {
await _grantRepository.DeleteAsync(key); await _grantRepository.DeleteByKeyAsync(key);
} }
public async Task StoreAsync(PersistedGrant pGrant) public async Task StoreAsync(PersistedGrant pGrant)
@ -59,30 +53,36 @@ namespace Bit.Core.IdentityServer
await _grantRepository.SaveAsync(grant); 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, Key = pGrant.Key,
Type = pGrant.Type, Type = pGrant.Type,
SubjectId = pGrant.SubjectId, SubjectId = pGrant.SubjectId,
SessionId = pGrant.SessionId,
ClientId = pGrant.ClientId, ClientId = pGrant.ClientId,
Description = pGrant.Description,
CreationDate = pGrant.CreationTime, CreationDate = pGrant.CreationTime,
ExpirationDate = pGrant.Expiration, ExpirationDate = pGrant.Expiration,
ConsumedDate = pGrant.ConsumedTime,
Data = pGrant.Data Data = pGrant.Data
}; };
} }
private PersistedGrant ToPersistedGrant(Grant grant) private PersistedGrant ToPersistedGrant(Models.Table.Grant grant)
{ {
return new PersistedGrant return new PersistedGrant
{ {
Key = grant.Key, Key = grant.Key,
Type = grant.Type, Type = grant.Type,
SubjectId = grant.SubjectId, SubjectId = grant.SubjectId,
SessionId = grant.SessionId,
ClientId = grant.ClientId, ClientId = grant.ClientId,
Description = grant.Description,
CreationTime = grant.CreationDate, CreationTime = grant.CreationDate,
Expiration = grant.ExpirationDate, Expiration = grant.ExpirationDate,
ConsumedTime = grant.ConsumedDate,
Data = grant.Data Data = grant.Data
}; };
} }

View File

@ -14,19 +14,16 @@ namespace Bit.Core.IdentityServer
public class ProfileService : IProfileService public class ProfileService : IProfileService
{ {
private readonly IUserService _userService; private readonly IUserService _userService;
private readonly IUserRepository _userRepository;
private readonly IOrganizationUserRepository _organizationUserRepository; private readonly IOrganizationUserRepository _organizationUserRepository;
private readonly ILicensingService _licensingService; private readonly ILicensingService _licensingService;
private readonly CurrentContext _currentContext; private readonly CurrentContext _currentContext;
public ProfileService( public ProfileService(
IUserRepository userRepository,
IUserService userService, IUserService userService,
IOrganizationUserRepository organizationUserRepository, IOrganizationUserRepository organizationUserRepository,
ILicensingService licensingService, ILicensingService licensingService,
CurrentContext currentContext) CurrentContext currentContext)
{ {
_userRepository = userRepository;
_userService = userService; _userService = userService;
_organizationUserRepository = organizationUserRepository; _organizationUserRepository = organizationUserRepository;
_licensingService = licensingService; _licensingService = licensingService;
@ -46,7 +43,8 @@ namespace Bit.Core.IdentityServer
{ {
new Claim("premium", isPremium ? "true" : "false", ClaimValueTypes.Boolean), new Claim("premium", isPremium ? "true" : "false", ClaimValueTypes.Boolean),
new Claim(JwtClaimTypes.Email, user.Email), 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) new Claim("sstamp", user.SecurityStamp)
}); });
@ -96,13 +94,14 @@ namespace Bit.Core.IdentityServer
// filter out any of the new claims // filter out any of the new claims
var existingClaimsToKeep = existingClaims 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(); .ToList();
newClaims.AddRange(existingClaimsToKeep); newClaims.AddRange(existingClaimsToKeep);
if (newClaims.Any()) if (newClaims.Any())
{ {
context.AddRequestedClaims(newClaims); context.IssuedClaims.AddRange(newClaims);
} }
} }

View File

@ -7,9 +7,12 @@ namespace Bit.Core.Models.Table
public string Key { get; set; } public string Key { get; set; }
public string Type { get; set; } public string Type { get; set; }
public string SubjectId { get; set; } public string SubjectId { get; set; }
public string SessionId { get; set; }
public string ClientId { get; set; } public string ClientId { get; set; }
public string Description { get; set; }
public DateTime CreationDate { get; set; } public DateTime CreationDate { get; set; }
public DateTime? ExpirationDate { get; set; } public DateTime? ExpirationDate { get; set; }
public DateTime? ConsumedDate { get; set; }
public string Data { get; set; } public string Data { get; set; }
} }
} }

View File

@ -8,10 +8,9 @@ namespace Bit.Core.Repositories
public interface IGrantRepository public interface IGrantRepository
{ {
Task<Grant> GetByKeyAsync(string key); Task<Grant> GetByKeyAsync(string key);
Task<ICollection<Grant>> GetManyAsync(string subjectId); Task<ICollection<Grant>> GetManyAsync(string subjectId, string sessionId, string clientId, string type);
Task SaveAsync(Grant obj); Task SaveAsync(Grant obj);
Task DeleteAsync(string key); Task DeleteByKeyAsync(string key);
Task DeleteAsync(string subjectId, string clientId); Task DeleteManyAsync(string subjectId, string sessionId, string clientId, string type);
Task DeleteAsync(string subjectId, string clientId, string type);
} }
} }

View File

@ -1,5 +1,4 @@
using System; using System.Collections.Generic;
using System.Collections.Generic;
using System.Data; using System.Data;
using System.Data.SqlClient; using System.Data.SqlClient;
using System.Linq; using System.Linq;
@ -32,13 +31,14 @@ namespace Bit.Core.Repositories.SqlServer
} }
} }
public async Task<ICollection<Grant>> GetManyAsync(string subjectId) public async Task<ICollection<Grant>> GetManyAsync(string subjectId, string sessionId,
string clientId, string type)
{ {
using (var connection = new SqlConnection(ConnectionString)) using (var connection = new SqlConnection(ConnectionString))
{ {
var results = await connection.QueryAsync<Grant>( var results = await connection.QueryAsync<Grant>(
"[dbo].[Grant_ReadBySubjectId]", "[dbo].[Grant_Read]",
new { SubjectId = subjectId }, new { SubjectId = subjectId, SessionId = sessionId, ClientId = clientId, Type = type },
commandType: CommandType.StoredProcedure); commandType: CommandType.StoredProcedure);
return results.ToList(); 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)) 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)) using (var connection = new SqlConnection(ConnectionString))
{ {
await connection.ExecuteAsync( await connection.ExecuteAsync(
"[dbo].[Grant_DeleteBySubjectIdClientId]", "[dbo].[Grant_Delete]",
new { SubjectId = subjectId, ClientId = clientId }, new { SubjectId = subjectId, SessionId = sessionId, ClientId = clientId, Type = type },
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 },
commandType: CommandType.StoredProcedure); commandType: CommandType.StoredProcedure);
} }
} }

View File

@ -7,6 +7,7 @@ using Bit.Core.Models.Table;
using Bit.Core.Repositories; using Bit.Core.Repositories;
using Bit.Identity.Models; using Bit.Identity.Models;
using IdentityModel; using IdentityModel;
using IdentityServer4;
using IdentityServer4.Services; using IdentityServer4.Services;
using IdentityServer4.Stores; using IdentityServer4.Stores;
using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication;
@ -132,8 +133,12 @@ namespace Bit.Identity.Controllers
ProcessLoginCallbackForOidc(result, additionalLocalClaims, localSignInProps); ProcessLoginCallbackForOidc(result, additionalLocalClaims, localSignInProps);
// issue authentication cookie for user // issue authentication cookie for user
await HttpContext.SignInAsync(user.Id.ToString(), user.Email, provider, await HttpContext.SignInAsync(new IdentityServerUser(user.Id.ToString())
localSignInProps, additionalLocalClaims.ToArray()); {
DisplayName = user.Email,
IdentityProvider = provider,
AdditionalClaims = additionalLocalClaims.ToArray()
}, localSignInProps);
// delete temporary cookie used during external authentication // delete temporary cookie used during external authentication
await HttpContext.SignOutAsync(IdentityServer4.IdentityServerConstants.ExternalCookieAuthenticationScheme); await HttpContext.SignOutAsync(IdentityServer4.IdentityServerConstants.ExternalCookieAuthenticationScheme);
@ -144,7 +149,7 @@ namespace Bit.Identity.Controllers
var context = await _interaction.GetAuthorizationContextAsync(returnUrl); var context = await _interaction.GetAuthorizationContextAsync(returnUrl);
if (context != null) 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 // 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. // return the response is for better UX for the end user.

View File

@ -65,6 +65,19 @@ namespace Bit.Identity
services.AddSingleton<IRateLimitCounterStore, MemoryCacheRateLimitCounterStore>(); services.AddSingleton<IRateLimitCounterStore, MemoryCacheRateLimitCounterStore>();
} }
// Cookies
if (Environment.IsDevelopment())
{
services.Configure<CookiePolicyOptions>(options =>
{
options.MinimumSameSitePolicy = Microsoft.AspNetCore.Http.SameSiteMode.Unspecified;
options.OnAppendCookie = ctx =>
{
ctx.CookieOptions.SameSite = Microsoft.AspNetCore.Http.SameSiteMode.Unspecified;
};
});
}
JwtSecurityTokenHandler.DefaultMapInboundClaims = false; JwtSecurityTokenHandler.DefaultMapInboundClaims = false;
// Authentication // Authentication
@ -133,6 +146,12 @@ namespace Bit.Identity
app.UseForwardedHeaders(globalSettings); app.UseForwardedHeaders(globalSettings);
} }
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseCookiePolicy();
}
// Add static files to the request pipeline. // Add static files to the request pipeline.
app.UseStaticFiles(); app.UseStaticFiles();
@ -172,9 +191,14 @@ namespace Bit.Identity
options.Endpoints.EnableTokenRevocationEndpoint = false; options.Endpoints.EnableTokenRevocationEndpoint = false;
options.IssuerUri = $"{issuerUri.Scheme}://{issuerUri.Host}"; options.IssuerUri = $"{issuerUri.Scheme}://{issuerUri.Host}";
options.Caching.ClientStoreExpiration = new TimeSpan(0, 5, 0); options.Caching.ClientStoreExpiration = new TimeSpan(0, 5, 0);
if(env.IsDevelopment())
{
options.Authentication.CookieSameSiteMode = Microsoft.AspNetCore.Http.SameSiteMode.Unspecified;
}
}) })
.AddInMemoryCaching() .AddInMemoryCaching()
.AddInMemoryApiResources(ApiResources.GetApiResources()) .AddInMemoryApiResources(ApiResources.GetApiResources())
.AddInMemoryApiScopes(ApiScopes.GetApiScopes())
.AddClientStoreCache<ClientStore>() .AddClientStoreCache<ClientStore>()
.AddCustomTokenRequestValidator<CustomTokenRequestValidator>() .AddCustomTokenRequestValidator<CustomTokenRequestValidator>()
.AddProfileService<ProfileService>() .AddProfileService<ProfileService>()

View File

@ -109,12 +109,11 @@
<Build Include="dbo\Stored Procedures\OrganizationUser_Create.sql" /> <Build Include="dbo\Stored Procedures\OrganizationUser_Create.sql" />
<Build Include="dbo\Stored Procedures\Grant_DeleteByKey.sql" /> <Build Include="dbo\Stored Procedures\Grant_DeleteByKey.sql" />
<Build Include="dbo\Stored Procedures\OrganizationUser_DeleteById.sql" /> <Build Include="dbo\Stored Procedures\OrganizationUser_DeleteById.sql" />
<Build Include="dbo\Stored Procedures\Grant_DeleteBySubjectIdClientId.sql" /> <Build Include="dbo\Stored Procedures\Grant_Delete.sql" />
<Build Include="dbo\Stored Procedures\OrganizationUser_ReadById.sql" /> <Build Include="dbo\Stored Procedures\OrganizationUser_ReadById.sql" />
<Build Include="dbo\Stored Procedures\Grant_DeleteBySubjectIdClientIdType.sql" />
<Build Include="dbo\Stored Procedures\OrganizationUser_ReadByOrganizationId.sql" /> <Build Include="dbo\Stored Procedures\OrganizationUser_ReadByOrganizationId.sql" />
<Build Include="dbo\Stored Procedures\Grant_ReadByKey.sql" /> <Build Include="dbo\Stored Procedures\Grant_ReadByKey.sql" />
<Build Include="dbo\Stored Procedures\Grant_ReadBySubjectId.sql" /> <Build Include="dbo\Stored Procedures\Grant_Read.sql" />
<Build Include="dbo\Stored Procedures\OrganizationUser_ReadByOrganizationIdUserId.sql" /> <Build Include="dbo\Stored Procedures\OrganizationUser_ReadByOrganizationIdUserId.sql" />
<Build Include="dbo\Stored Procedures\Grant_Save.sql" /> <Build Include="dbo\Stored Procedures\Grant_Save.sql" />
<Build Include="dbo\Stored Procedures\OrganizationUser_ReadByUserId.sql" /> <Build Include="dbo\Stored Procedures\OrganizationUser_ReadByUserId.sql" />

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -1,10 +1,13 @@
CREATE PROCEDURE [dbo].[Grant_Save] CREATE PROCEDURE [dbo].[Grant_Save]
@Key NVARCHAR(200), @Key NVARCHAR(200),
@Type NVARCHAR(50), @Type NVARCHAR(50),
@SubjectId NVARCHAR(50), @SubjectId NVARCHAR(200),
@ClientId NVARCHAR(50), @SessionId NVARCHAR(100),
@ClientId NVARCHAR(200),
@Description NVARCHAR(200),
@CreationDate DATETIME2, @CreationDate DATETIME2,
@ExpirationDate DATETIME2, @ExpirationDate DATETIME2,
@ConsumedDate DATETIME2,
@Data NVARCHAR(MAX) @Data NVARCHAR(MAX)
AS AS
BEGIN BEGIN
@ -19,9 +22,12 @@ BEGIN
@Key, @Key,
@Type, @Type,
@SubjectId, @SubjectId,
@SessionId,
@ClientId, @ClientId,
@Description,
@CreationDate, @CreationDate,
@ExpirationDate, @ExpirationDate,
@ConsumedDate,
@Data @Data
) )
) AS [Source] ) AS [Source]
@ -29,9 +35,12 @@ BEGIN
[Key], [Key],
[Type], [Type],
[SubjectId], [SubjectId],
[SessionId],
[ClientId], [ClientId],
[Description],
[CreationDate], [CreationDate],
[ExpirationDate], [ExpirationDate],
[ConsumedDate],
[Data] [Data]
) )
ON ON
@ -41,9 +50,12 @@ BEGIN
SET SET
[Type] = [Source].[Type], [Type] = [Source].[Type],
[SubjectId] = [Source].[SubjectId], [SubjectId] = [Source].[SubjectId],
[SessionId] = [Source].[SessionId],
[ClientId] = [Source].[ClientId], [ClientId] = [Source].[ClientId],
[Description] = [Source].[Description],
[CreationDate] = [Source].[CreationDate], [CreationDate] = [Source].[CreationDate],
[ExpirationDate] = [Source].[ExpirationDate], [ExpirationDate] = [Source].[ExpirationDate],
[ConsumedDate] = [Source].[ConsumedDate],
[Data] = [Source].[Data] [Data] = [Source].[Data]
WHEN NOT MATCHED THEN WHEN NOT MATCHED THEN
INSERT INSERT
@ -51,9 +63,12 @@ BEGIN
[Key], [Key],
[Type], [Type],
[SubjectId], [SubjectId],
[SessionId],
[ClientId], [ClientId],
[Description],
[CreationDate], [CreationDate],
[ExpirationDate], [ExpirationDate],
[ConsumedDate],
[Data] [Data]
) )
VALUES VALUES
@ -61,9 +76,12 @@ BEGIN
[Source].[Key], [Source].[Key],
[Source].[Type], [Source].[Type],
[Source].[SubjectId], [Source].[SubjectId],
[Source].[SessionId],
[Source].[ClientId], [Source].[ClientId],
[Source].[Description],
[Source].[CreationDate], [Source].[CreationDate],
[Source].[ExpirationDate], [Source].[ExpirationDate],
[Source].[ConsumedDate],
[Source].[Data] [Source].[Data]
) )
; ;

View File

@ -1,10 +1,13 @@
CREATE TABLE [dbo].[Grant] ( CREATE TABLE [dbo].[Grant] (
[Key] NVARCHAR (200) NOT NULL, [Key] NVARCHAR (200) NOT NULL,
[Type] NVARCHAR (50) NULL, [Type] NVARCHAR (50) NOT NULL,
[SubjectId] NVARCHAR (50) NULL, [SubjectId] NVARCHAR (200) NULL,
[ClientId] NVARCHAR (50) NOT NULL, [SessionId] NVARCHAR (100) NULL,
[ClientId] NVARCHAR (200) NOT NULL,
[Description] NVARCHAR (200) NULL,
[CreationDate] DATETIME2 (7) NOT NULL, [CreationDate] DATETIME2 (7) NOT NULL,
[ExpirationDate] DATETIME2 (7) NULL, [ExpirationDate] DATETIME2 (7) NULL,
[ConsumedDate] DATETIME2 (7) NULL,
[Data] NVARCHAR (MAX) NOT NULL, [Data] NVARCHAR (MAX) NOT NULL,
CONSTRAINT [PK_Grant] PRIMARY KEY CLUSTERED ([Key] ASC) CONSTRAINT [PK_Grant] PRIMARY KEY CLUSTERED ([Key] ASC)
); );
@ -14,6 +17,10 @@ GO
CREATE NONCLUSTERED INDEX [IX_Grant_SubjectId_ClientId_Type] CREATE NONCLUSTERED INDEX [IX_Grant_SubjectId_ClientId_Type]
ON [dbo].[Grant]([SubjectId] ASC, [ClientId] ASC, [Type] ASC); 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 GO
CREATE NONCLUSTERED INDEX [IX_Grant_ExpirationDate] CREATE NONCLUSTERED INDEX [IX_Grant_ExpirationDate]
ON [dbo].[Grant]([ExpirationDate] ASC); ON [dbo].[Grant]([ExpirationDate] ASC);

View File

@ -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