1
0
mirror of https://github.com/bitwarden/server synced 2025-12-15 07:43:54 +00:00
Files
server/src/Core/IdentityServer/ProfileService.cs
Addison Beck 25a9991908 Implement User-based API Keys (#981)
* added column ApiKey to dbo.User

* added dbo.User.ApiKey to User_Update

* added dbo.User.ApiKey to User_Create

* wrote migration script for implementing dbo.User.ApiKey

* Added ApiKey prop to the User table model

* Created AccountsController method for getting a user's API Key

* Created AccountsController method for rotating a user API key

* Added support to ApiClient for passed-through ClientSecrets when the request comes from the cli

* Added a new conditional to ClientStore to account for user API keys

* Wrote unit tests for new user API Key methods

* Added a refresh of dbo.UserView to new migration script for ApiKey

* Let client_credentials grants into the custom token logic

* Cleanup for ApiKey auth in the CLI feature

* Created user API key on registration

* Removed uneeded code for user API keys

* Changed a .Contains() to a .StartsWith() in ClientStore

* Changed index that an array is searched on

* Added more claims to the user apikey clients

* Moved some claim finding logic to a helper method
2020-11-10 15:15:29 -05:00

86 lines
3.1 KiB
C#

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 System.Linq;
using System;
using IdentityModel;
using Bit.Core.Utilities;
namespace Bit.Core.IdentityServer
{
public class ProfileService : IProfileService
{
private readonly IUserService _userService;
private readonly IOrganizationUserRepository _organizationUserRepository;
private readonly ILicensingService _licensingService;
private readonly CurrentContext _currentContext;
public ProfileService(
IUserService userService,
IOrganizationUserRepository organizationUserRepository,
ILicensingService licensingService,
CurrentContext currentContext)
{
_userService = userService;
_organizationUserRepository = organizationUserRepository;
_licensingService = licensingService;
_currentContext = currentContext;
}
public async Task GetProfileDataAsync(ProfileDataRequestContext context)
{
var existingClaims = context.Subject.Claims;
var newClaims = new List<Claim>();
var user = await _userService.GetUserByPrincipalAsync(context.Subject);
if (user != null)
{
var isPremium = await _licensingService.ValidateUserPremiumAsync(user);
var orgs = await _currentContext.OrganizationMembershipAsync(_organizationUserRepository, user.Id);
foreach (var claim in CoreHelpers.BuildIdentityClaims(user, orgs, isPremium))
{
var upperValue = claim.Value.ToUpperInvariant();
var isBool = upperValue == "TRUE" || upperValue == "FALSE";
newClaims.Add(isBool ?
new Claim(claim.Key, claim.Value, ClaimValueTypes.Boolean) :
new Claim(claim.Key, claim.Value)
);
}
}
// 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.IssuedClaims.AddRange(newClaims);
}
}
public async Task IsActiveAsync(IsActiveContext context)
{
var securityTokenClaim = context.Subject?.Claims.FirstOrDefault(c => c.Type == "sstamp");
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;
}
}
}
}