using IdentityServer4.Services; using System.Threading.Tasks; using IdentityServer4.Models; using Bit.Core.Repositories; using Bit.Core.Services; using System.Security.Claims; using System.Collections.Generic; using Microsoft.AspNetCore.Builder; using System.Linq; using Microsoft.Extensions.Options; using System; namespace Bit.Api.IdentityServer { public class ProfileService : IProfileService { 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(); } public async Task GetProfileDataAsync(ProfileDataRequestContext context) { var existingClaims = context.Subject.Claims; var newClaims = new List(); var user = await _userService.GetUserByPrincipalAsync(context.Subject); if(user != null) { newClaims.AddRange(new List { new Claim("plan", "0"), // free plan hard coded for now new Claim("sstamp", user.SecurityStamp), new Claim("email", user.Email), // Deprecated claims for backwards compatability new Claim(_identityOptions.ClaimsIdentity.UserNameClaimType, user.Email), new Claim(_identityOptions.ClaimsIdentity.SecurityStampClaimType, user.SecurityStamp) }); if(!string.IsNullOrWhiteSpace(user.Name)) { 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.OrganizationId.ToString())); } break; case Core.Enums.OrganizationUserType.Admin: foreach(var org in group) { newClaims.Add(new Claim("orgadmin", org.OrganizationId.ToString())); } break; case Core.Enums.OrganizationUserType.User: foreach(var org in group) { newClaims.Add(new Claim("orguser", org.OrganizationId.ToString())); } break; default: break; } } } } // 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))) .ToList(); newClaims.AddRange(existingClaimsToKeep); if(newClaims.Any()) { context.AddFilteredClaims(newClaims); } } public async Task IsActiveAsync(IsActiveContext context) { var securityTokenClaim = context.Subject?.Claims.FirstOrDefault(c => c.Type == _identityOptions.ClaimsIdentity.SecurityStampClaimType); var user = await _userService.GetUserByPrincipalAsync(context.Subject); if(user != null && securityTokenClaim != null) { context.IsActive = string.Equals(user.SecurityStamp, securityTokenClaim.Value, StringComparison.InvariantCultureIgnoreCase); return; } else { context.IsActive = true; } } } }