From a4744493540bb9a10abb5ec69a6dcb36bbcdea7e Mon Sep 17 00:00:00 2001 From: Kyle Spearrin Date: Wed, 5 Apr 2017 15:31:33 -0400 Subject: [PATCH] added user orgs to claims --- src/Api/IdentityServer/ApiResources.cs | 5 ++- src/Api/IdentityServer/Clients.cs | 10 ++++- src/Api/IdentityServer/ProfileService.cs | 43 ++++++++++++++++++- .../IOrganizationUserRepository.cs | 1 + .../SqlServer/OrganizationUserRepository.cs | 13 ++++++ src/Sql/Sql.sqlproj | 1 + .../OrganizationUser_ReadByUserId.sql | 13 ++++++ 7 files changed, 81 insertions(+), 5 deletions(-) create mode 100644 src/Sql/dbo/Stored Procedures/OrganizationUser_ReadByUserId.sql diff --git a/src/Api/IdentityServer/ApiResources.cs b/src/Api/IdentityServer/ApiResources.cs index 62efc45c7e..3ac1d51bae 100644 --- a/src/Api/IdentityServer/ApiResources.cs +++ b/src/Api/IdentityServer/ApiResources.cs @@ -20,7 +20,10 @@ namespace Bit.Api.IdentityServer "email", "sstamp", // security stamp "plan", - "device" + "device", + "orgowner", + "orgadmin", + "orguser" }) }; } diff --git a/src/Api/IdentityServer/Clients.cs b/src/Api/IdentityServer/Clients.cs index 6ccd578551..dfe9441175 100644 --- a/src/Api/IdentityServer/Clients.cs +++ b/src/Api/IdentityServer/Clients.cs @@ -18,7 +18,7 @@ namespace Bit.Api.IdentityServer public class ApiClient : Client { - public ApiClient(string id) + public ApiClient(string id, string[] additionalScopes = null) { ClientId = id; RequireClientSecret = false; @@ -26,7 +26,13 @@ namespace Bit.Api.IdentityServer UpdateAccessTokenClaimsOnRefresh = true; AccessTokenLifetime = 60 * 60; // 1 hour AllowOfflineAccess = true; - AllowedScopes = new string[] { "api" }; + + var scopes = new List { "api" }; + if(additionalScopes != null) + { + scopes.AddRange(additionalScopes); + } + AllowedScopes = scopes; } } } diff --git a/src/Api/IdentityServer/ProfileService.cs b/src/Api/IdentityServer/ProfileService.cs index 85b2893461..4ab4f34be3 100644 --- a/src/Api/IdentityServer/ProfileService.cs +++ b/src/Api/IdentityServer/ProfileService.cs @@ -16,15 +16,18 @@ namespace Bit.Api.IdentityServer { private readonly IUserService _userService; private readonly IUserRepository _userRepository; + private readonly IOrganizationUserRepository _organizationUserRepository; private IdentityOptions _identityOptions; public ProfileService( IUserRepository userRepository, IUserService userService, + IOrganizationUserRepository organizationUserRepository, IOptions identityOptionsAccessor) { _userRepository = userRepository; _userService = userService; + _organizationUserRepository = organizationUserRepository; _identityOptions = identityOptionsAccessor?.Value ?? new IdentityOptions(); } @@ -42,7 +45,7 @@ namespace Bit.Api.IdentityServer new Claim("sstamp", user.SecurityStamp), new Claim("email", user.Email), - // Deprecated claims for backwards compatability, + // Deprecated claims for backwards compatability new Claim(_identityOptions.ClaimsIdentity.UserNameClaimType, user.Email), new Claim(_identityOptions.ClaimsIdentity.SecurityStampClaimType, user.SecurityStamp) }); @@ -51,11 +54,47 @@ namespace Bit.Api.IdentityServer { newClaims.Add(new Claim("name", user.Name)); } + + // Orgs that this user belongs to + var orgs = await _organizationUserRepository.GetManyByUserAsync(user.Id); + if(orgs.Any()) + { + var groupedOrgs = orgs.Where(o => o.Status == Core.Enums.OrganizationUserStatusType.Confirmed) + .GroupBy(o => o.Type); + + foreach(var group in groupedOrgs) + { + switch(group.Key) + { + case Core.Enums.OrganizationUserType.Owner: + foreach(var org in group) + { + newClaims.Add(new Claim("orgowner", org.Id.ToString())); + } + break; + case Core.Enums.OrganizationUserType.Admin: + foreach(var org in group) + { + newClaims.Add(new Claim("orgadmin", org.Id.ToString())); + } + break; + case Core.Enums.OrganizationUserType.User: + foreach(var org in group) + { + newClaims.Add(new Claim("orguser", org.Id.ToString())); + } + break; + default: + break; + } + } + } } // filter out any of the new claims var existingClaimsToKeep = existingClaims - .Where(c => newClaims.Count == 0 || !newClaims.Any(nc => nc.Type == c.Type)).ToList(); + .Where(c => !c.Type.StartsWith("org") && (newClaims.Count == 0 || !newClaims.Any(nc => nc.Type == c.Type))) + .ToList(); newClaims.AddRange(existingClaimsToKeep); if(newClaims.Any()) diff --git a/src/Core/Repositories/IOrganizationUserRepository.cs b/src/Core/Repositories/IOrganizationUserRepository.cs index 427288d9c4..f4984925f9 100644 --- a/src/Core/Repositories/IOrganizationUserRepository.cs +++ b/src/Core/Repositories/IOrganizationUserRepository.cs @@ -10,6 +10,7 @@ namespace Bit.Core.Repositories public interface IOrganizationUserRepository : IRepository { Task GetByOrganizationAsync(Guid organizationId, Guid userId); + Task> GetManyByUserAsync(Guid userId); Task> GetManyByOrganizationAsync(Guid organizationId, OrganizationUserType? type); Task GetByOrganizationAsync(Guid organizationId, string email); Task>> GetDetailsByIdAsync(Guid id); diff --git a/src/Core/Repositories/SqlServer/OrganizationUserRepository.cs b/src/Core/Repositories/SqlServer/OrganizationUserRepository.cs index 16c68f973b..df0e95eef6 100644 --- a/src/Core/Repositories/SqlServer/OrganizationUserRepository.cs +++ b/src/Core/Repositories/SqlServer/OrganizationUserRepository.cs @@ -47,6 +47,19 @@ namespace Bit.Core.Repositories.SqlServer } } + public async Task> GetManyByUserAsync(Guid userId) + { + using(var connection = new SqlConnection(ConnectionString)) + { + var results = await connection.QueryAsync( + "[dbo].[OrganizationUser_ReadByUserId]", + new { UserId = userId }, + commandType: CommandType.StoredProcedure); + + return results.ToList(); + } + } + public async Task> GetManyByOrganizationAsync(Guid organizationId, OrganizationUserType? type) { diff --git a/src/Sql/Sql.sqlproj b/src/Sql/Sql.sqlproj index fbc5b588dd..91194bd070 100644 --- a/src/Sql/Sql.sqlproj +++ b/src/Sql/Sql.sqlproj @@ -183,5 +183,6 @@ + \ No newline at end of file diff --git a/src/Sql/dbo/Stored Procedures/OrganizationUser_ReadByUserId.sql b/src/Sql/dbo/Stored Procedures/OrganizationUser_ReadByUserId.sql new file mode 100644 index 0000000000..8f30ade5e3 --- /dev/null +++ b/src/Sql/dbo/Stored Procedures/OrganizationUser_ReadByUserId.sql @@ -0,0 +1,13 @@ +CREATE PROCEDURE [dbo].[OrganizationUser_ReadByUserId] + @UserId UNIQUEIDENTIFIER +AS +BEGIN + SET NOCOUNT ON + + SELECT + * + FROM + [dbo].[OrganizationUserView] + WHERE + [UserId] = @UserId +END \ No newline at end of file