1
0
mirror of https://github.com/bitwarden/server.git synced 2025-07-02 00:22:50 -05:00

Create sso user api (#886)

* facilitate linking/unlinking existing users from an sso enabled org

* added user_identifier to identity methods for sso

* moved sso user delete method to account controller

* fixed a broken test

* Update AccountsController.cs

* facilitate linking/unlinking existing users from an sso enabled org

* added user_identifier to identity methods for sso

* moved sso user delete method to account controller

* fixed a broken test

* added a token to the existing user sso link flow

* added a token to the existing user sso link flow

* fixed a typo

* added an event log for unlink ssoUser records

* fixed a merge issue

* fixed a busted test

* fixed a busted test

* ran a formatter over everything & changed .vscode settings in .gitignore

* chagned a variable to use string interpolation

* removed a blank line

* Changed TokenPurpose enum to a static class of strings

* code review cleanups

* formatting fix

* Changed parameters & logging for delete sso user

* changed th method used to get organization user for deleting sso user records

Co-authored-by: Kyle Spearrin <kspearrin@users.noreply.github.com>
This commit is contained in:
Addison Beck
2020-08-26 14:12:04 -04:00
committed by GitHub
parent 7cc9ce7bd5
commit 59f8467f7c
18 changed files with 214 additions and 64 deletions

View File

@ -4,4 +4,9 @@
{
public const int BypassFiltersEventId = 12482444;
}
public static class TokenPurposes
{
public const string LinkSso = "LinkSso";
}
}

View File

@ -42,6 +42,7 @@
OrganizationUser_Updated = 1502,
OrganizationUser_Removed = 1503,
OrganizationUser_UpdatedGroups = 1504,
OrganizationUser_UnlinkedSso = 1505,
Organization_Updated = 1600,
Organization_PurgedVault = 1601,

View File

@ -28,6 +28,7 @@ namespace Bit.Core.Models.Api
Type = organization.Type;
Enabled = organization.Enabled;
SsoBound = !string.IsNullOrWhiteSpace(organization.SsoExternalId);
Identifier = organization.Identifier;
}
public string Id { get; set; }
@ -51,5 +52,6 @@ namespace Bit.Core.Models.Api
public OrganizationUserType Type { get; set; }
public bool Enabled { get; set; }
public bool SsoBound { get; set; }
public string Identifier { get; set; }
}
}

View File

@ -25,5 +25,6 @@ namespace Bit.Core.Models.Data
public Enums.OrganizationUserType Type { get; set; }
public bool Enabled { get; set; }
public string SsoExternalId { get; set; }
public string Identifier { get; set; }
}
}

View File

@ -1,8 +1,11 @@
using Bit.Core.Models.Table;
using Bit.Core.Models.Table;
using System;
using System.Threading.Tasks;
namespace Bit.Core.Repositories
{
public interface ISsoUserRepository : IRepository<SsoUser, long>
{
Task DeleteAsync(Guid userId, Guid? organizationId);
}
}

View File

@ -1,4 +1,9 @@
using Bit.Core.Models.Table;
using Dapper;
using System;
using System.Threading.Tasks;
using System.Data.SqlClient;
using System.Data;
namespace Bit.Core.Repositories.SqlServer
{
@ -11,5 +16,16 @@ namespace Bit.Core.Repositories.SqlServer
public SsoUserRepository(string connectionString, string readOnlyConnectionString)
: base(connectionString, readOnlyConnectionString)
{ }
public async Task DeleteAsync(Guid userId, Guid? organizationId)
{
using (var connection = new SqlConnection(ConnectionString))
{
var results = await connection.ExecuteAsync(
$"[{Schema}].[SsoUser_Delete]",
new { UserId = userId, OrganizationId = organizationId },
commandType: CommandType.StoredProcedure);
}
}
}
}

View File

@ -51,5 +51,6 @@ namespace Bit.Core.Services
IEnumerable<ImportedOrganizationUser> newUsers, IEnumerable<string> removeUserExternalIds,
bool overwriteExisting);
Task RotateApiKeyAsync(Organization organization);
Task DeleteSsoUserAsync(Guid userId, Guid? organizationId);
}
}

View File

@ -68,5 +68,6 @@ namespace Bit.Core.Services
Task<bool> TwoFactorIsEnabledAsync(ITwoFactorProvidersUser user);
Task<bool> TwoFactorProviderIsEnabledAsync(TwoFactorProviderType provider, ITwoFactorProvidersUser user);
Task<string> GenerateEnterprisePortalSignInTokenAsync(User user);
Task<string> GenerateSignInTokenAsync(User user, string purpose);
}
}

View File

@ -35,6 +35,7 @@ namespace Bit.Core.Services
private readonly IPaymentService _paymentService;
private readonly IPolicyRepository _policyRepository;
private readonly ISsoConfigRepository _ssoConfigRepository;
private readonly ISsoUserRepository _ssoUserRepository;
private readonly IReferenceEventService _referenceEventService;
private readonly GlobalSettings _globalSettings;
@ -56,6 +57,7 @@ namespace Bit.Core.Services
IPaymentService paymentService,
IPolicyRepository policyRepository,
ISsoConfigRepository ssoConfigRepository,
ISsoUserRepository ssoUserRepository,
IReferenceEventService referenceEventService,
GlobalSettings globalSettings)
{
@ -76,6 +78,7 @@ namespace Bit.Core.Services
_paymentService = paymentService;
_policyRepository = policyRepository;
_ssoConfigRepository = ssoConfigRepository;
_ssoUserRepository = ssoUserRepository;
_referenceEventService = referenceEventService;
_globalSettings = globalSettings;
}
@ -1497,6 +1500,19 @@ namespace Bit.Core.Services
await ReplaceAndUpdateCache(organization);
}
public async Task DeleteSsoUserAsync(Guid userId, Guid? organizationId)
{
await _ssoUserRepository.DeleteAsync(userId, organizationId);
if (organizationId.HasValue)
{
var organizationUser = await _organizationUserRepository.GetByOrganizationAsync(organizationId.Value, userId);
if (organizationUser != null)
{
await _eventService.LogOrganizationUserEventAsync(organizationUser, EventType.OrganizationUser_UnlinkedSso);
}
}
}
private async Task UpdateUsersAsync(Group group, HashSet<string> groupUsers,
Dictionary<string, Guid> existingUsersIdDict, HashSet<Guid> existingUsers = null)
{

View File

@ -1087,6 +1087,7 @@ namespace Bit.Core.Services
return await CanAccessPremium(user);
}
//TODO refactor this to use the below method and enum
public async Task<string> GenerateEnterprisePortalSignInTokenAsync(User user)
{
var token = await GenerateUserTokenAsync(user, Options.Tokens.PasswordResetTokenProvider,
@ -1094,6 +1095,14 @@ namespace Bit.Core.Services
return token;
}
public async Task<string> GenerateSignInTokenAsync(User user, string purpose)
{
var token = await GenerateUserTokenAsync(user, Options.Tokens.PasswordResetTokenProvider,
purpose);
return token;
}
private async Task<IdentityResult> UpdatePasswordHash(User user, string newPassword,
bool validatePassword = true, bool refreshStamp = true)
{