mirror of
https://github.com/bitwarden/server.git
synced 2024-11-28 13:15:12 +01: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:
parent
7cc9ce7bd5
commit
59f8467f7c
2
.gitignore
vendored
2
.gitignore
vendored
@ -207,3 +207,5 @@ src/Core/Properties/launchSettings.json
|
|||||||
**/*.DS_Store
|
**/*.DS_Store
|
||||||
src/Admin/wwwroot/lib
|
src/Admin/wwwroot/lib
|
||||||
src/Admin/wwwroot/css
|
src/Admin/wwwroot/css
|
||||||
|
.vscode/*
|
||||||
|
**/.vscode/*
|
||||||
|
@ -1,21 +1,22 @@
|
|||||||
using System;
|
using Bit.Api.Utilities;
|
||||||
using System.Threading.Tasks;
|
using Bit.Core;
|
||||||
|
using Bit.Core.Enums;
|
||||||
|
using Bit.Core.Exceptions;
|
||||||
|
using Bit.Core.Models.Api;
|
||||||
|
using Bit.Core.Models.Api.Request.Accounts;
|
||||||
|
using Bit.Core.Models.Business;
|
||||||
|
using Bit.Core.Models.Data;
|
||||||
|
using Bit.Core.Models.Table;
|
||||||
|
using Bit.Core.Repositories;
|
||||||
|
using Bit.Core.Services;
|
||||||
|
using Bit.Core.Utilities;
|
||||||
using Microsoft.AspNetCore.Authorization;
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Bit.Core.Models.Api;
|
using System;
|
||||||
using Bit.Core.Exceptions;
|
|
||||||
using Bit.Core.Services;
|
|
||||||
using Bit.Core.Enums;
|
|
||||||
using System.Linq;
|
|
||||||
using Bit.Core.Repositories;
|
|
||||||
using Bit.Core.Utilities;
|
|
||||||
using Bit.Core;
|
|
||||||
using Bit.Core.Models.Business;
|
|
||||||
using Bit.Api.Utilities;
|
|
||||||
using Bit.Core.Models.Table;
|
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Bit.Core.Models.Api.Request.Accounts;
|
using System.Linq;
|
||||||
using Bit.Core.Models.Data;
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Bit.Api.Controllers
|
namespace Bit.Api.Controllers
|
||||||
{
|
{
|
||||||
@ -23,30 +24,34 @@ namespace Bit.Api.Controllers
|
|||||||
[Authorize("Application")]
|
[Authorize("Application")]
|
||||||
public class AccountsController : Controller
|
public class AccountsController : Controller
|
||||||
{
|
{
|
||||||
private readonly IUserService _userService;
|
private readonly GlobalSettings _globalSettings;
|
||||||
private readonly IUserRepository _userRepository;
|
|
||||||
private readonly ICipherRepository _cipherRepository;
|
private readonly ICipherRepository _cipherRepository;
|
||||||
private readonly IFolderRepository _folderRepository;
|
private readonly IFolderRepository _folderRepository;
|
||||||
|
private readonly IOrganizationService _organizationService;
|
||||||
private readonly IOrganizationUserRepository _organizationUserRepository;
|
private readonly IOrganizationUserRepository _organizationUserRepository;
|
||||||
private readonly IPaymentService _paymentService;
|
private readonly IPaymentService _paymentService;
|
||||||
private readonly GlobalSettings _globalSettings;
|
private readonly IUserRepository _userRepository;
|
||||||
|
private readonly IUserService _userService;
|
||||||
|
|
||||||
public AccountsController(
|
public AccountsController(
|
||||||
IUserService userService,
|
GlobalSettings globalSettings,
|
||||||
IUserRepository userRepository,
|
|
||||||
ICipherRepository cipherRepository,
|
ICipherRepository cipherRepository,
|
||||||
IFolderRepository folderRepository,
|
IFolderRepository folderRepository,
|
||||||
|
IOrganizationService organizationService,
|
||||||
IOrganizationUserRepository organizationUserRepository,
|
IOrganizationUserRepository organizationUserRepository,
|
||||||
IPaymentService paymentService,
|
IPaymentService paymentService,
|
||||||
GlobalSettings globalSettings)
|
ISsoUserRepository ssoUserRepository,
|
||||||
|
IUserRepository userRepository,
|
||||||
|
IUserService userService)
|
||||||
{
|
{
|
||||||
_userService = userService;
|
|
||||||
_userRepository = userRepository;
|
|
||||||
_cipherRepository = cipherRepository;
|
_cipherRepository = cipherRepository;
|
||||||
_folderRepository = folderRepository;
|
_folderRepository = folderRepository;
|
||||||
|
_globalSettings = globalSettings;
|
||||||
|
_organizationService = organizationService;
|
||||||
_organizationUserRepository = organizationUserRepository;
|
_organizationUserRepository = organizationUserRepository;
|
||||||
_paymentService = paymentService;
|
_paymentService = paymentService;
|
||||||
_globalSettings = globalSettings;
|
_userRepository = userRepository;
|
||||||
|
_userService = userService;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpPost("prelogin")]
|
[HttpPost("prelogin")]
|
||||||
@ -708,5 +713,27 @@ namespace Bit.Api.Controllers
|
|||||||
};
|
};
|
||||||
await _paymentService.SaveTaxInfoAsync(user, taxInfo);
|
await _paymentService.SaveTaxInfoAsync(user, taxInfo);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[HttpDelete("sso/{organizationId}")]
|
||||||
|
public async Task DeleteSsoUser(string organizationId)
|
||||||
|
{
|
||||||
|
var userId = _userService.GetProperUserId(User);
|
||||||
|
if (!userId.HasValue)
|
||||||
|
{
|
||||||
|
throw new NotFoundException();
|
||||||
|
}
|
||||||
|
|
||||||
|
await _organizationService.DeleteSsoUserAsync(userId.Value, new Guid(organizationId));
|
||||||
|
}
|
||||||
|
|
||||||
|
[HttpGet("sso/user-identifier")]
|
||||||
|
public async Task<string> GetSsoUserIdentifier()
|
||||||
|
{
|
||||||
|
var user = await _userService.GetUserByPrincipalAsync(User);
|
||||||
|
var token = await _userService.GenerateSignInTokenAsync(user, TokenPurposes.LinkSso);
|
||||||
|
var bytes = Encoding.UTF8.GetBytes($"{user.Id},{token}");
|
||||||
|
var userIdentifier = Convert.ToBase64String(bytes);
|
||||||
|
return userIdentifier;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,4 +4,9 @@
|
|||||||
{
|
{
|
||||||
public const int BypassFiltersEventId = 12482444;
|
public const int BypassFiltersEventId = 12482444;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class TokenPurposes
|
||||||
|
{
|
||||||
|
public const string LinkSso = "LinkSso";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -42,6 +42,7 @@
|
|||||||
OrganizationUser_Updated = 1502,
|
OrganizationUser_Updated = 1502,
|
||||||
OrganizationUser_Removed = 1503,
|
OrganizationUser_Removed = 1503,
|
||||||
OrganizationUser_UpdatedGroups = 1504,
|
OrganizationUser_UpdatedGroups = 1504,
|
||||||
|
OrganizationUser_UnlinkedSso = 1505,
|
||||||
|
|
||||||
Organization_Updated = 1600,
|
Organization_Updated = 1600,
|
||||||
Organization_PurgedVault = 1601,
|
Organization_PurgedVault = 1601,
|
||||||
|
@ -28,6 +28,7 @@ namespace Bit.Core.Models.Api
|
|||||||
Type = organization.Type;
|
Type = organization.Type;
|
||||||
Enabled = organization.Enabled;
|
Enabled = organization.Enabled;
|
||||||
SsoBound = !string.IsNullOrWhiteSpace(organization.SsoExternalId);
|
SsoBound = !string.IsNullOrWhiteSpace(organization.SsoExternalId);
|
||||||
|
Identifier = organization.Identifier;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string Id { get; set; }
|
public string Id { get; set; }
|
||||||
@ -51,5 +52,6 @@ namespace Bit.Core.Models.Api
|
|||||||
public OrganizationUserType Type { get; set; }
|
public OrganizationUserType Type { get; set; }
|
||||||
public bool Enabled { get; set; }
|
public bool Enabled { get; set; }
|
||||||
public bool SsoBound { get; set; }
|
public bool SsoBound { get; set; }
|
||||||
|
public string Identifier { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -25,5 +25,6 @@ namespace Bit.Core.Models.Data
|
|||||||
public Enums.OrganizationUserType Type { get; set; }
|
public Enums.OrganizationUserType Type { get; set; }
|
||||||
public bool Enabled { get; set; }
|
public bool Enabled { get; set; }
|
||||||
public string SsoExternalId { get; set; }
|
public string SsoExternalId { get; set; }
|
||||||
|
public string Identifier { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,8 +1,11 @@
|
|||||||
using Bit.Core.Models.Table;
|
using Bit.Core.Models.Table;
|
||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Bit.Core.Repositories
|
namespace Bit.Core.Repositories
|
||||||
{
|
{
|
||||||
public interface ISsoUserRepository : IRepository<SsoUser, long>
|
public interface ISsoUserRepository : IRepository<SsoUser, long>
|
||||||
{
|
{
|
||||||
|
Task DeleteAsync(Guid userId, Guid? organizationId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,9 @@
|
|||||||
using Bit.Core.Models.Table;
|
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
|
namespace Bit.Core.Repositories.SqlServer
|
||||||
{
|
{
|
||||||
@ -11,5 +16,16 @@ namespace Bit.Core.Repositories.SqlServer
|
|||||||
public SsoUserRepository(string connectionString, string readOnlyConnectionString)
|
public SsoUserRepository(string connectionString, string readOnlyConnectionString)
|
||||||
: base(connectionString, 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -51,5 +51,6 @@ namespace Bit.Core.Services
|
|||||||
IEnumerable<ImportedOrganizationUser> newUsers, IEnumerable<string> removeUserExternalIds,
|
IEnumerable<ImportedOrganizationUser> newUsers, IEnumerable<string> removeUserExternalIds,
|
||||||
bool overwriteExisting);
|
bool overwriteExisting);
|
||||||
Task RotateApiKeyAsync(Organization organization);
|
Task RotateApiKeyAsync(Organization organization);
|
||||||
|
Task DeleteSsoUserAsync(Guid userId, Guid? organizationId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -68,5 +68,6 @@ namespace Bit.Core.Services
|
|||||||
Task<bool> TwoFactorIsEnabledAsync(ITwoFactorProvidersUser user);
|
Task<bool> TwoFactorIsEnabledAsync(ITwoFactorProvidersUser user);
|
||||||
Task<bool> TwoFactorProviderIsEnabledAsync(TwoFactorProviderType provider, ITwoFactorProvidersUser user);
|
Task<bool> TwoFactorProviderIsEnabledAsync(TwoFactorProviderType provider, ITwoFactorProvidersUser user);
|
||||||
Task<string> GenerateEnterprisePortalSignInTokenAsync(User user);
|
Task<string> GenerateEnterprisePortalSignInTokenAsync(User user);
|
||||||
|
Task<string> GenerateSignInTokenAsync(User user, string purpose);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,6 +35,7 @@ namespace Bit.Core.Services
|
|||||||
private readonly IPaymentService _paymentService;
|
private readonly IPaymentService _paymentService;
|
||||||
private readonly IPolicyRepository _policyRepository;
|
private readonly IPolicyRepository _policyRepository;
|
||||||
private readonly ISsoConfigRepository _ssoConfigRepository;
|
private readonly ISsoConfigRepository _ssoConfigRepository;
|
||||||
|
private readonly ISsoUserRepository _ssoUserRepository;
|
||||||
private readonly IReferenceEventService _referenceEventService;
|
private readonly IReferenceEventService _referenceEventService;
|
||||||
private readonly GlobalSettings _globalSettings;
|
private readonly GlobalSettings _globalSettings;
|
||||||
|
|
||||||
@ -56,6 +57,7 @@ namespace Bit.Core.Services
|
|||||||
IPaymentService paymentService,
|
IPaymentService paymentService,
|
||||||
IPolicyRepository policyRepository,
|
IPolicyRepository policyRepository,
|
||||||
ISsoConfigRepository ssoConfigRepository,
|
ISsoConfigRepository ssoConfigRepository,
|
||||||
|
ISsoUserRepository ssoUserRepository,
|
||||||
IReferenceEventService referenceEventService,
|
IReferenceEventService referenceEventService,
|
||||||
GlobalSettings globalSettings)
|
GlobalSettings globalSettings)
|
||||||
{
|
{
|
||||||
@ -76,6 +78,7 @@ namespace Bit.Core.Services
|
|||||||
_paymentService = paymentService;
|
_paymentService = paymentService;
|
||||||
_policyRepository = policyRepository;
|
_policyRepository = policyRepository;
|
||||||
_ssoConfigRepository = ssoConfigRepository;
|
_ssoConfigRepository = ssoConfigRepository;
|
||||||
|
_ssoUserRepository = ssoUserRepository;
|
||||||
_referenceEventService = referenceEventService;
|
_referenceEventService = referenceEventService;
|
||||||
_globalSettings = globalSettings;
|
_globalSettings = globalSettings;
|
||||||
}
|
}
|
||||||
@ -1497,6 +1500,19 @@ namespace Bit.Core.Services
|
|||||||
await ReplaceAndUpdateCache(organization);
|
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,
|
private async Task UpdateUsersAsync(Group group, HashSet<string> groupUsers,
|
||||||
Dictionary<string, Guid> existingUsersIdDict, HashSet<Guid> existingUsers = null)
|
Dictionary<string, Guid> existingUsersIdDict, HashSet<Guid> existingUsers = null)
|
||||||
{
|
{
|
||||||
|
@ -1087,6 +1087,7 @@ namespace Bit.Core.Services
|
|||||||
return await CanAccessPremium(user);
|
return await CanAccessPremium(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//TODO refactor this to use the below method and enum
|
||||||
public async Task<string> GenerateEnterprisePortalSignInTokenAsync(User user)
|
public async Task<string> GenerateEnterprisePortalSignInTokenAsync(User user)
|
||||||
{
|
{
|
||||||
var token = await GenerateUserTokenAsync(user, Options.Tokens.PasswordResetTokenProvider,
|
var token = await GenerateUserTokenAsync(user, Options.Tokens.PasswordResetTokenProvider,
|
||||||
@ -1094,6 +1095,14 @@ namespace Bit.Core.Services
|
|||||||
return token;
|
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,
|
private async Task<IdentityResult> UpdatePasswordHash(User user, string newPassword,
|
||||||
bool validatePassword = true, bool refreshStamp = true)
|
bool validatePassword = true, bool refreshStamp = true)
|
||||||
{
|
{
|
||||||
|
@ -1,62 +1,75 @@
|
|||||||
using System;
|
using Bit.Core.Models.Table;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Security.Claims;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Bit.Core.Models.Table;
|
|
||||||
using Bit.Core.Repositories;
|
using Bit.Core.Repositories;
|
||||||
|
using Bit.Core.Services;
|
||||||
using Bit.Identity.Models;
|
using Bit.Identity.Models;
|
||||||
using IdentityModel;
|
using IdentityModel;
|
||||||
using IdentityServer4;
|
using IdentityServer4;
|
||||||
using IdentityServer4.Services;
|
using IdentityServer4.Services;
|
||||||
using IdentityServer4.Stores;
|
using IdentityServer4.Stores;
|
||||||
using Microsoft.AspNetCore.Authentication;
|
using Microsoft.AspNetCore.Authentication;
|
||||||
|
using Microsoft.AspNetCore.Authorization;
|
||||||
using Microsoft.AspNetCore.Http;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.Extensions.Logging;
|
using Microsoft.Extensions.Logging;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Security.Claims;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
namespace Bit.Identity.Controllers
|
namespace Bit.Identity.Controllers
|
||||||
{
|
{
|
||||||
public class AccountController : Controller
|
public class AccountController : Controller
|
||||||
{
|
{
|
||||||
private readonly IIdentityServerInteractionService _interaction;
|
|
||||||
private readonly IUserRepository _userRepository;
|
|
||||||
private readonly ISsoConfigRepository _ssoConfigRepository;
|
|
||||||
private readonly IClientStore _clientStore;
|
private readonly IClientStore _clientStore;
|
||||||
|
private readonly IIdentityServerInteractionService _interaction;
|
||||||
private readonly ILogger<AccountController> _logger;
|
private readonly ILogger<AccountController> _logger;
|
||||||
|
private readonly ISsoConfigRepository _ssoConfigRepository;
|
||||||
|
private readonly IUserRepository _userRepository;
|
||||||
|
|
||||||
public AccountController(
|
public AccountController(
|
||||||
IIdentityServerInteractionService interaction,
|
|
||||||
IUserRepository userRepository,
|
|
||||||
ISsoConfigRepository ssoConfigRepository,
|
|
||||||
IClientStore clientStore,
|
IClientStore clientStore,
|
||||||
ILogger<AccountController> logger)
|
IIdentityServerInteractionService interaction,
|
||||||
|
ILogger<AccountController> logger,
|
||||||
|
IOrganizationUserRepository organizationUserRepository,
|
||||||
|
ISsoConfigRepository ssoConfigRepository,
|
||||||
|
IUserRepository userRepository,
|
||||||
|
IUserService userService)
|
||||||
{
|
{
|
||||||
_interaction = interaction;
|
|
||||||
_userRepository = userRepository;
|
|
||||||
_ssoConfigRepository = ssoConfigRepository;
|
|
||||||
_clientStore = clientStore;
|
_clientStore = clientStore;
|
||||||
|
_interaction = interaction;
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
|
_ssoConfigRepository = ssoConfigRepository;
|
||||||
|
_userRepository = userRepository;
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
public async Task<IActionResult> Login(string returnUrl)
|
public async Task<IActionResult> Login(string returnUrl)
|
||||||
{
|
{
|
||||||
var context = await _interaction.GetAuthorizationContextAsync(returnUrl);
|
var context = await _interaction.GetAuthorizationContextAsync(returnUrl);
|
||||||
if (context.Parameters.AllKeys.Contains("domain_hint") &&
|
|
||||||
!string.IsNullOrWhiteSpace(context.Parameters["domain_hint"]))
|
var domainHint = context.Parameters.AllKeys.Contains("domain_hint") ?
|
||||||
|
context.Parameters["domain_hint"] : null;
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(domainHint))
|
||||||
{
|
{
|
||||||
return RedirectToAction(nameof(ExternalChallenge),
|
throw new Exception("No domain_hint provided");
|
||||||
new { organizationIdentifier = context.Parameters["domain_hint"], returnUrl = returnUrl });
|
|
||||||
}
|
}
|
||||||
else
|
|
||||||
|
var userIdentifier = context.Parameters.AllKeys.Contains("user_identifier") ?
|
||||||
|
context.Parameters["user_identifier"] : null;
|
||||||
|
|
||||||
|
return RedirectToAction(nameof(ExternalChallenge), new
|
||||||
{
|
{
|
||||||
throw new Exception("No domain_hint provided.");
|
organizationIdentifier = domainHint,
|
||||||
}
|
returnUrl,
|
||||||
|
userIdentifier
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
[HttpGet]
|
[HttpGet]
|
||||||
public async Task<IActionResult> ExternalChallenge(string organizationIdentifier, string returnUrl)
|
public async Task<IActionResult> ExternalChallenge(string organizationIdentifier, string returnUrl,
|
||||||
|
string userIdentifier)
|
||||||
{
|
{
|
||||||
if (string.IsNullOrWhiteSpace(organizationIdentifier))
|
if (string.IsNullOrWhiteSpace(organizationIdentifier))
|
||||||
{
|
{
|
||||||
@ -82,6 +95,11 @@ namespace Bit.Identity.Controllers
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(userIdentifier))
|
||||||
|
{
|
||||||
|
props.Items.Add("user_identifier", userIdentifier);
|
||||||
|
}
|
||||||
|
|
||||||
return Challenge(props, scheme);
|
return Challenge(props, scheme);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -100,6 +100,7 @@ namespace Bit.Identity
|
|||||||
{
|
{
|
||||||
// Pass domain_hint onto the sso idp
|
// Pass domain_hint onto the sso idp
|
||||||
context.ProtocolMessage.DomainHint = context.Properties.Items["domain_hint"];
|
context.ProtocolMessage.DomainHint = context.Properties.Items["domain_hint"];
|
||||||
|
context.ProtocolMessage.SessionState = context.Properties.Items["user_identifier"];
|
||||||
return Task.FromResult(0);
|
return Task.FromResult(0);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
@ -18,6 +18,7 @@ SELECT
|
|||||||
O.[Seats],
|
O.[Seats],
|
||||||
O.[MaxCollections],
|
O.[MaxCollections],
|
||||||
O.[MaxStorageGb],
|
O.[MaxStorageGb],
|
||||||
|
O.[Identifier],
|
||||||
OU.[Key],
|
OU.[Key],
|
||||||
OU.[Status],
|
OU.[Status],
|
||||||
OU.[Type],
|
OU.[Type],
|
||||||
|
@ -1,6 +1,3 @@
|
|||||||
using System;
|
|
||||||
using System.Security.Claims;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using Bit.Api.Controllers;
|
using Bit.Api.Controllers;
|
||||||
using Bit.Core;
|
using Bit.Core;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
@ -12,21 +9,26 @@ using Bit.Core.Repositories;
|
|||||||
using Bit.Core.Services;
|
using Bit.Core.Services;
|
||||||
using Microsoft.AspNetCore.Identity;
|
using Microsoft.AspNetCore.Identity;
|
||||||
using NSubstitute;
|
using NSubstitute;
|
||||||
|
using System;
|
||||||
|
using System.Security.Claims;
|
||||||
|
using System.Threading.Tasks;
|
||||||
using Xunit;
|
using Xunit;
|
||||||
|
|
||||||
namespace Bit.Api.Test.Controllers
|
namespace Bit.Api.Test.Controllers
|
||||||
{
|
{
|
||||||
public class AccountsControllerTests : IDisposable
|
public class AccountsControllerTests : IDisposable
|
||||||
{
|
{
|
||||||
private readonly AccountsController _sut;
|
|
||||||
|
|
||||||
private readonly IUserService _userService;
|
private readonly AccountsController _sut;
|
||||||
private readonly IUserRepository _userRepository;
|
private readonly GlobalSettings _globalSettings;
|
||||||
private readonly ICipherRepository _cipherRepository;
|
private readonly ICipherRepository _cipherRepository;
|
||||||
private readonly IFolderRepository _folderRepository;
|
private readonly IFolderRepository _folderRepository;
|
||||||
|
private readonly IOrganizationService _organizationService;
|
||||||
private readonly IOrganizationUserRepository _organizationUserRepository;
|
private readonly IOrganizationUserRepository _organizationUserRepository;
|
||||||
private readonly IPaymentService _paymentService;
|
private readonly IPaymentService _paymentService;
|
||||||
private readonly GlobalSettings _globalSettings;
|
private readonly ISsoUserRepository _ssoUserRepository;
|
||||||
|
private readonly IUserRepository _userRepository;
|
||||||
|
private readonly IUserService _userService;
|
||||||
|
|
||||||
public AccountsControllerTests()
|
public AccountsControllerTests()
|
||||||
{
|
{
|
||||||
@ -34,17 +36,20 @@ namespace Bit.Api.Test.Controllers
|
|||||||
_userRepository = Substitute.For<IUserRepository>();
|
_userRepository = Substitute.For<IUserRepository>();
|
||||||
_cipherRepository = Substitute.For<ICipherRepository>();
|
_cipherRepository = Substitute.For<ICipherRepository>();
|
||||||
_folderRepository = Substitute.For<IFolderRepository>();
|
_folderRepository = Substitute.For<IFolderRepository>();
|
||||||
|
_organizationService = Substitute.For<IOrganizationService>();
|
||||||
_organizationUserRepository = Substitute.For<IOrganizationUserRepository>();
|
_organizationUserRepository = Substitute.For<IOrganizationUserRepository>();
|
||||||
_paymentService = Substitute.For<IPaymentService>();
|
_paymentService = Substitute.For<IPaymentService>();
|
||||||
_globalSettings = new GlobalSettings();
|
_globalSettings = new GlobalSettings();
|
||||||
_sut = new AccountsController(
|
_sut = new AccountsController(
|
||||||
_userService,
|
_globalSettings,
|
||||||
_userRepository,
|
|
||||||
_cipherRepository,
|
_cipherRepository,
|
||||||
_folderRepository,
|
_folderRepository,
|
||||||
|
_organizationService,
|
||||||
_organizationUserRepository,
|
_organizationUserRepository,
|
||||||
_paymentService,
|
_paymentService,
|
||||||
_globalSettings
|
_ssoUserRepository,
|
||||||
|
_userRepository,
|
||||||
|
_userService
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -33,13 +33,14 @@ namespace Bit.Core.Test.Services
|
|||||||
var paymentService = Substitute.For<IPaymentService>();
|
var paymentService = Substitute.For<IPaymentService>();
|
||||||
var policyRepo = Substitute.For<IPolicyRepository>();
|
var policyRepo = Substitute.For<IPolicyRepository>();
|
||||||
var ssoConfigRepo = Substitute.For<ISsoConfigRepository>();
|
var ssoConfigRepo = Substitute.For<ISsoConfigRepository>();
|
||||||
|
var ssoUserRepo = Substitute.For<ISsoUserRepository>();
|
||||||
var referenceEventService = Substitute.For<IReferenceEventService>();
|
var referenceEventService = Substitute.For<IReferenceEventService>();
|
||||||
var globalSettings = Substitute.For<GlobalSettings>();
|
var globalSettings = Substitute.For<GlobalSettings>();
|
||||||
|
|
||||||
var orgService = new OrganizationService(orgRepo, orgUserRepo, collectionRepo, userRepo,
|
var orgService = new OrganizationService(orgRepo, orgUserRepo, collectionRepo, userRepo,
|
||||||
groupRepo, dataProtector, mailService, pushNotService, pushRegService, deviceRepo,
|
groupRepo, dataProtector, mailService, pushNotService, pushRegService, deviceRepo,
|
||||||
licenseService, eventService, installationRepo, appCacheService, paymentService, policyRepo,
|
licenseService, eventService, installationRepo, appCacheService, paymentService, policyRepo,
|
||||||
ssoConfigRepo, referenceEventService, globalSettings);
|
ssoConfigRepo, ssoUserRepo, referenceEventService, globalSettings);
|
||||||
|
|
||||||
var id = Guid.NewGuid();
|
var id = Guid.NewGuid();
|
||||||
var userId = Guid.NewGuid();
|
var userId = Guid.NewGuid();
|
||||||
@ -93,13 +94,14 @@ namespace Bit.Core.Test.Services
|
|||||||
var paymentService = Substitute.For<IPaymentService>();
|
var paymentService = Substitute.For<IPaymentService>();
|
||||||
var policyRepo = Substitute.For<IPolicyRepository>();
|
var policyRepo = Substitute.For<IPolicyRepository>();
|
||||||
var ssoConfigRepo = Substitute.For<ISsoConfigRepository>();
|
var ssoConfigRepo = Substitute.For<ISsoConfigRepository>();
|
||||||
|
var ssoUserRepo = Substitute.For<ISsoUserRepository>();
|
||||||
var referenceEventService = Substitute.For<IReferenceEventService>();
|
var referenceEventService = Substitute.For<IReferenceEventService>();
|
||||||
var globalSettings = Substitute.For<GlobalSettings>();
|
var globalSettings = Substitute.For<GlobalSettings>();
|
||||||
|
|
||||||
var orgService = new OrganizationService(orgRepo, orgUserRepo, collectionRepo, userRepo,
|
var orgService = new OrganizationService(orgRepo, orgUserRepo, collectionRepo, userRepo,
|
||||||
groupRepo, dataProtector, mailService, pushNotService, pushRegService, deviceRepo,
|
groupRepo, dataProtector, mailService, pushNotService, pushRegService, deviceRepo,
|
||||||
licenseService, eventService, installationRepo, appCacheService, paymentService, policyRepo,
|
licenseService, eventService, installationRepo, appCacheService, paymentService, policyRepo,
|
||||||
ssoConfigRepo, referenceEventService, globalSettings);
|
ssoConfigRepo, ssoUserRepo, referenceEventService, globalSettings);
|
||||||
|
|
||||||
var id = Guid.NewGuid();
|
var id = Guid.NewGuid();
|
||||||
var userId = Guid.NewGuid();
|
var userId = Guid.NewGuid();
|
||||||
|
@ -0,0 +1,39 @@
|
|||||||
|
IF EXISTS(SELECT * FROM sys.views WHERE [Name] = 'OrganizationUserOrganizationDetailsView')
|
||||||
|
BEGIN
|
||||||
|
DROP VIEW [dbo].[OrganizationUserOrganizationDetailsView];
|
||||||
|
END
|
||||||
|
GO
|
||||||
|
|
||||||
|
CREATE VIEW [dbo].[OrganizationUserOrganizationDetailsView]
|
||||||
|
AS
|
||||||
|
SELECT
|
||||||
|
OU.[UserId],
|
||||||
|
OU.[OrganizationId],
|
||||||
|
O.[Name],
|
||||||
|
O.[Enabled],
|
||||||
|
O.[UsePolicies],
|
||||||
|
O.[UseSso],
|
||||||
|
O.[UseGroups],
|
||||||
|
O.[UseDirectory],
|
||||||
|
O.[UseEvents],
|
||||||
|
O.[UseTotp],
|
||||||
|
O.[Use2fa],
|
||||||
|
O.[UseApi],
|
||||||
|
O.[SelfHost],
|
||||||
|
O.[UsersGetPremium],
|
||||||
|
O.[Seats],
|
||||||
|
O.[MaxCollections],
|
||||||
|
O.[MaxStorageGb],
|
||||||
|
O.[Identifier],
|
||||||
|
OU.[Key],
|
||||||
|
OU.[Status],
|
||||||
|
OU.[Type],
|
||||||
|
SU.[ExternalId] SsoExternalId
|
||||||
|
FROM
|
||||||
|
[dbo].[OrganizationUser] OU
|
||||||
|
INNER JOIN
|
||||||
|
[dbo].[Organization] O ON O.[Id] = OU.[OrganizationId]
|
||||||
|
LEFT JOIN
|
||||||
|
[dbo].[SsoUser] SU ON SU.[UserId] = OU.[UserId] AND SU.[OrganizationId] = OU.[OrganizationId]
|
||||||
|
|
||||||
|
GO
|
Loading…
Reference in New Issue
Block a user