From 2abb1aaae53853685961d77fd433535afb7656a0 Mon Sep 17 00:00:00 2001 From: Kyle Spearrin Date: Wed, 11 Jan 2017 23:20:54 -0500 Subject: [PATCH] implemented grant repository and identity server PersistedGrantStore --- src/Api/Startup.cs | 11 ++- src/Core/Domains/Grant.cs | 15 +++ src/Core/Identity/PersistedGrantStore.cs | 90 ++++++++++++++++++ src/Core/Repositories/IGrantRepository.cs | 17 ++++ .../Repositories/SqlServer/GrantRepository.cs | 92 +++++++++++++++++++ 5 files changed, 221 insertions(+), 4 deletions(-) create mode 100644 src/Core/Domains/Grant.cs create mode 100644 src/Core/Identity/PersistedGrantStore.cs create mode 100644 src/Core/Repositories/IGrantRepository.cs create mode 100644 src/Core/Repositories/SqlServer/GrantRepository.cs diff --git a/src/Api/Startup.cs b/src/Api/Startup.cs index 7f954a4328..e27a610037 100644 --- a/src/Api/Startup.cs +++ b/src/Api/Startup.cs @@ -16,7 +16,7 @@ using Bit.Core.Domains; using Bit.Core.Identity; using Bit.Core.Repositories; using Bit.Core.Services; -using Repos = Bit.Core.Repositories.SqlServer; +using SqlServerRepos = Bit.Core.Repositories.SqlServer; using System.Text; using Loggr.Extensions.Logging; using System.Linq; @@ -28,6 +28,7 @@ using Bit.Api.Middleware; using IdentityServer4.Validation; using IdentityServer4.Services; using IdentityModel.AspNetCore.OAuth2Introspection; +using IdentityServer4.Stores; namespace Bit.Api { @@ -70,9 +71,10 @@ namespace Bit.Api services.Configure(Configuration.GetSection("IpRateLimitPolicies")); // Repositories - services.AddSingleton(); - services.AddSingleton(); - services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); // Context services.AddScoped(); @@ -92,6 +94,7 @@ namespace Bit.Api .AddInMemoryClients(Clients.GetClients()); services.AddSingleton(); services.AddSingleton(); + services.AddSingleton(); // Identity services.AddTransient(); diff --git a/src/Core/Domains/Grant.cs b/src/Core/Domains/Grant.cs new file mode 100644 index 0000000000..19aa078ca9 --- /dev/null +++ b/src/Core/Domains/Grant.cs @@ -0,0 +1,15 @@ +using System; + +namespace Bit.Core.Domains +{ + public class Grant + { + public string Key { get; set; } + public string Type { get; set; } + public string SubjectId { get; set; } + public string ClientId { get; set; } + public DateTime CreationDate { get; set; } + public DateTime? ExpirationDate { get; set; } + public string Data { get; set; } + } +} diff --git a/src/Core/Identity/PersistedGrantStore.cs b/src/Core/Identity/PersistedGrantStore.cs new file mode 100644 index 0000000000..3d65e480f2 --- /dev/null +++ b/src/Core/Identity/PersistedGrantStore.cs @@ -0,0 +1,90 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Bit.Core.Domains; +using Bit.Core.Repositories; +using IdentityServer4.Models; +using IdentityServer4.Stores; + +namespace Bit.Core.Identity +{ + public class PersistedGrantStore : IPersistedGrantStore + { + private readonly IGrantRepository _grantRepository; + + public PersistedGrantStore( + IGrantRepository grantRepository) + { + _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); + if(grant == null) + { + return null; + } + + var pGrant = ToPersistedGrant(grant); + return pGrant; + } + + public async Task RemoveAllAsync(string subjectId, string clientId) + { + await _grantRepository.DeleteAsync(subjectId, clientId); + } + + public async Task RemoveAllAsync(string subjectId, string clientId, string type) + { + await _grantRepository.DeleteAsync(subjectId, clientId, type); + } + + public async Task RemoveAsync(string key) + { + await _grantRepository.DeleteAsync(key); + } + + public async Task StoreAsync(PersistedGrant pGrant) + { + var grant = ToGrant(pGrant); + await _grantRepository.SaveAsync(grant); + } + + private Grant ToGrant(PersistedGrant pGrant) + { + return new Grant + { + Key = pGrant.Key, + Type = pGrant.Type, + SubjectId = pGrant.SubjectId, + ClientId = pGrant.ClientId, + CreationDate = pGrant.CreationTime, + ExpirationDate = pGrant.Expiration, + Data = pGrant.Data + }; + } + + private PersistedGrant ToPersistedGrant(Grant grant) + { + return new PersistedGrant + { + Key = grant.Key, + Type = grant.Type, + SubjectId = grant.SubjectId, + ClientId = grant.ClientId, + CreationTime = grant.CreationDate, + Expiration = grant.ExpirationDate, + Data = grant.Data + }; + } + } +} diff --git a/src/Core/Repositories/IGrantRepository.cs b/src/Core/Repositories/IGrantRepository.cs new file mode 100644 index 0000000000..c78614d68a --- /dev/null +++ b/src/Core/Repositories/IGrantRepository.cs @@ -0,0 +1,17 @@ +using System; +using System.Threading.Tasks; +using Bit.Core.Domains; +using System.Collections.Generic; + +namespace Bit.Core.Repositories +{ + public interface IGrantRepository + { + Task GetByKeyAsync(string key); + Task> GetManyAsync(string subjectId); + Task SaveAsync(Grant obj); + Task DeleteAsync(string key); + Task DeleteAsync(string subjectId, string clientId); + Task DeleteAsync(string subjectId, string clientId, string type); + } +} diff --git a/src/Core/Repositories/SqlServer/GrantRepository.cs b/src/Core/Repositories/SqlServer/GrantRepository.cs new file mode 100644 index 0000000000..2f3506e874 --- /dev/null +++ b/src/Core/Repositories/SqlServer/GrantRepository.cs @@ -0,0 +1,92 @@ +using System; +using System.Collections.Generic; +using System.Data; +using System.Data.SqlClient; +using System.Linq; +using System.Threading.Tasks; +using Bit.Core.Domains; +using Dapper; + +namespace Bit.Core.Repositories.SqlServer +{ + public class GrantRepository : BaseRepository, IGrantRepository + { + public GrantRepository(GlobalSettings globalSettings) + : this(globalSettings.SqlServer.ConnectionString) + { } + + public GrantRepository(string connectionString) + : base(connectionString) + { } + + public async Task GetByKeyAsync(string key) + { + using(var connection = new SqlConnection(ConnectionString)) + { + var results = await connection.QueryAsync( + "[dbo].[Grant_ReadByKey]", + new { Key = key }, + commandType: CommandType.StoredProcedure); + + return results.SingleOrDefault(); + } + } + + public async Task> GetManyAsync(string subjectId) + { + using(var connection = new SqlConnection(ConnectionString)) + { + var results = await connection.QueryAsync( + "[dbo].[Grant_ReadBySubjectId]", + new { SubjectId = subjectId }, + commandType: CommandType.StoredProcedure); + + return results.ToList(); + } + } + + public async Task SaveAsync(Grant obj) + { + using(var connection = new SqlConnection(ConnectionString)) + { + var results = await connection.ExecuteAsync( + "[dbo].[Grant_Save]", + obj, + commandType: CommandType.StoredProcedure); + } + } + + public async Task DeleteAsync(string key) + { + using(var connection = new SqlConnection(ConnectionString)) + { + await connection.ExecuteAsync( + "[dbo].[Grant_DeleteByKey]", + new { Key = key }, + commandType: CommandType.StoredProcedure); + } + } + + public async Task DeleteAsync(string subjectId, string clientId) + { + 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 }, + commandType: CommandType.StoredProcedure); + } + } + } +}