mirror of
https://github.com/bitwarden/server.git
synced 2025-07-03 00:52:49 -05:00
[EC-261] SCIM (#2105)
* scim project stub * some scim models and v2 controllers * implement some v2 scim endpoints * fix spacing * api key auth * EC-261 - SCIM Org API Key and connection type config * EC-261 - Fix lint errors/formatting * updates for okta implementation testing * fix var ref * updates from testing with Okta * implement scim context via provider parsing * support single and list of ids for add/remove groups * log ops not handled * touch up scim context * group list filtering * EC-261 - Additional SCIM provider types * EC-265 - UseScim flag and license update * EC-265 - SCIM provider type of default (0) * EC-265 - Add Scim URL and update connection validation * EC-265 - Model validation and cleanup for SCIM keys * implement scim org connection * EC-265 - Ensure ServiceUrl is not persisted to DB * EC-265 - Exclude provider type from DB if not configured * EC-261 - EF Migrations for SCIM * add docker builds for scim * EC-261 - Fix failing permissions tests * EC-261 - Fix unit tests and pgsql migrations * Formatting fixes from linter * EC-265 - Remove service URL from scim config * EC-265 - Fix unit tests, removed wayward validation * EC-265 - Require self-hosted for billing sync org conn * EC-265 - Fix formatting issues - whitespace * EC-261 - PR feedback and cleanup * scim constants rename * no scim settings right now * update project name * delete package lock * update appsettings configs for scim * use default scim provider for context Co-authored-by: Kyle Spearrin <kyle.spearrin@gmail.com>
This commit is contained in:
@ -493,7 +493,7 @@ namespace Bit.Api.Controllers
|
||||
public async Task<ApiKeyResponseModel> ApiKey(string id, [FromBody] OrganizationApiKeyRequestModel model)
|
||||
{
|
||||
var orgIdGuid = new Guid(id);
|
||||
if (!await _currentContext.OrganizationOwner(orgIdGuid))
|
||||
if (!await HasApiKeyAccessAsync(orgIdGuid, model.Type))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
@ -504,9 +504,9 @@ namespace Bit.Api.Controllers
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
if (model.Type == OrganizationApiKeyType.BillingSync)
|
||||
if (model.Type == OrganizationApiKeyType.BillingSync || model.Type == OrganizationApiKeyType.Scim)
|
||||
{
|
||||
// Non-enterprise orgs should not be able to create or view an apikey of billing sync key type
|
||||
// Non-enterprise orgs should not be able to create or view an apikey of billing sync/scim key types
|
||||
var plan = StaticStore.GetPlan(organization.PlanType);
|
||||
if (plan.Product != ProductType.Enterprise)
|
||||
{
|
||||
@ -523,7 +523,8 @@ namespace Bit.Api.Controllers
|
||||
throw new UnauthorizedAccessException();
|
||||
}
|
||||
|
||||
if (!await _userService.VerifySecretAsync(user, model.Secret))
|
||||
if (model.Type != OrganizationApiKeyType.Scim
|
||||
&& !await _userService.VerifySecretAsync(user, model.Secret))
|
||||
{
|
||||
await Task.Delay(2000);
|
||||
throw new BadRequestException("MasterPasswordHash", "Invalid password.");
|
||||
@ -535,15 +536,15 @@ namespace Bit.Api.Controllers
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet("{id}/api-key-information")]
|
||||
public async Task<ListResponseModel<OrganizationApiKeyInformation>> ApiKeyInformation(Guid id)
|
||||
[HttpGet("{id}/api-key-information/{type?}")]
|
||||
public async Task<ListResponseModel<OrganizationApiKeyInformation>> ApiKeyInformation(Guid id, OrganizationApiKeyType? type)
|
||||
{
|
||||
if (!await _currentContext.OrganizationOwner(id))
|
||||
if (!await HasApiKeyAccessAsync(id, type))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
|
||||
var apiKeys = await _organizationApiKeyRepository.GetManyByOrganizationIdTypeAsync(id);
|
||||
var apiKeys = await _organizationApiKeyRepository.GetManyByOrganizationIdTypeAsync(id, type);
|
||||
|
||||
return new ListResponseModel<OrganizationApiKeyInformation>(
|
||||
apiKeys.Select(k => new OrganizationApiKeyInformation(k)));
|
||||
@ -553,7 +554,7 @@ namespace Bit.Api.Controllers
|
||||
public async Task<ApiKeyResponseModel> RotateApiKey(string id, [FromBody] OrganizationApiKeyRequestModel model)
|
||||
{
|
||||
var orgIdGuid = new Guid(id);
|
||||
if (!await _currentContext.OrganizationOwner(orgIdGuid))
|
||||
if (!await HasApiKeyAccessAsync(orgIdGuid, model.Type))
|
||||
{
|
||||
throw new NotFoundException();
|
||||
}
|
||||
@ -573,7 +574,8 @@ namespace Bit.Api.Controllers
|
||||
throw new UnauthorizedAccessException();
|
||||
}
|
||||
|
||||
if (!await _userService.VerifySecretAsync(user, model.Secret))
|
||||
if (model.Type != OrganizationApiKeyType.Scim
|
||||
&& !await _userService.VerifySecretAsync(user, model.Secret))
|
||||
{
|
||||
await Task.Delay(2000);
|
||||
throw new BadRequestException("MasterPasswordHash", "Invalid password.");
|
||||
@ -586,6 +588,15 @@ namespace Bit.Api.Controllers
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<bool> HasApiKeyAccessAsync(Guid orgId, OrganizationApiKeyType? type)
|
||||
{
|
||||
return type switch
|
||||
{
|
||||
OrganizationApiKeyType.Scim => await _currentContext.ManageScim(orgId),
|
||||
_ => await _currentContext.OrganizationOwner(orgId),
|
||||
};
|
||||
}
|
||||
|
||||
[HttpGet("{id}/tax")]
|
||||
[SelfHosted(NotSelfHostedOnly = true)]
|
||||
public async Task<TaxInfoResponseModel> GetTaxInfo(string id)
|
||||
|
Reference in New Issue
Block a user