EC-265 - Initial stubs for SCIM config UI
This commit is contained in:
parent
af8f83980f
commit
664b8dc13e
|
@ -86,6 +86,9 @@ export class OrganizationLayoutComponent implements OnInit, OnDestroy {
|
||||||
case this.organization.canManageSso:
|
case this.organization.canManageSso:
|
||||||
route = "manage/sso";
|
route = "manage/sso";
|
||||||
break;
|
break;
|
||||||
|
case this.organization.canManageScim:
|
||||||
|
route = "manage/scim";
|
||||||
|
break;
|
||||||
case this.organization.canAccessEventLogs:
|
case this.organization.canAccessEventLogs:
|
||||||
route = "manage/events";
|
route = "manage/events";
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -52,6 +52,14 @@
|
||||||
>
|
>
|
||||||
{{ "eventLogs" | i18n }}
|
{{ "eventLogs" | i18n }}
|
||||||
</a>
|
</a>
|
||||||
|
<a
|
||||||
|
routerLink="scim"
|
||||||
|
class="list-group-item"
|
||||||
|
routerLinkActive="active"
|
||||||
|
*ngIf="organization.canManageScim && accessScim"
|
||||||
|
>
|
||||||
|
{{ "singleSignOn" | i18n }}
|
||||||
|
</a>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -14,6 +14,7 @@ export class ManageComponent implements OnInit {
|
||||||
accessGroups = false;
|
accessGroups = false;
|
||||||
accessEvents = false;
|
accessEvents = false;
|
||||||
accessSso = false;
|
accessSso = false;
|
||||||
|
accessScim = false;
|
||||||
|
|
||||||
constructor(private route: ActivatedRoute, private organizationService: OrganizationService) {}
|
constructor(private route: ActivatedRoute, private organizationService: OrganizationService) {}
|
||||||
|
|
||||||
|
@ -24,6 +25,7 @@ export class ManageComponent implements OnInit {
|
||||||
this.accessSso = this.organization.useSso;
|
this.accessSso = this.organization.useSso;
|
||||||
this.accessEvents = this.organization.useEvents;
|
this.accessEvents = this.organization.useEvents;
|
||||||
this.accessGroups = this.organization.useGroups;
|
this.accessGroups = this.organization.useGroups;
|
||||||
|
this.accessScim = this.organization.useScim;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ const permissions = {
|
||||||
Permissions.ManageUsers,
|
Permissions.ManageUsers,
|
||||||
Permissions.ManagePolicies,
|
Permissions.ManagePolicies,
|
||||||
Permissions.ManageSso,
|
Permissions.ManageSso,
|
||||||
|
Permissions.ManageScim,
|
||||||
],
|
],
|
||||||
tools: [Permissions.AccessImportExport, Permissions.AccessReports],
|
tools: [Permissions.AccessImportExport, Permissions.AccessReports],
|
||||||
settings: [Permissions.ManageOrganization],
|
settings: [Permissions.ManageOrganization],
|
||||||
|
|
|
@ -5165,5 +5165,9 @@
|
||||||
"example": "My Email"
|
"example": "My Email"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"scim": {
|
||||||
|
"message": "SCIM Provisioning",
|
||||||
|
"description": "This text, 'SCIM', is an acronymn and should not be translated."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
<div class="page-header d-flex">
|
||||||
|
<h1>{{ "scim" | i18n }}</h1>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<ng-container *ngIf="loading">
|
||||||
|
<i
|
||||||
|
class="bwi bwi-spinner bwi-spin text-muted"
|
||||||
|
title="{{ 'loading' | i18n }}"
|
||||||
|
aria-hidden="true"
|
||||||
|
></i>
|
||||||
|
<span class="sr-only">{{ "loading" | i18n }}</span>
|
||||||
|
</ng-container>
|
||||||
|
|
||||||
|
<form #form (ngSubmit)="submit()" [appApiAction]="formPromise" *ngIf="!loading">
|
||||||
|
<p><!-- TODO: Need to fill in this component, just a placeholder for now --></p>
|
||||||
|
</form>
|
|
@ -0,0 +1,63 @@
|
||||||
|
import { Component, OnInit } from "@angular/core";
|
||||||
|
import { ActivatedRoute } from "@angular/router";
|
||||||
|
|
||||||
|
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
||||||
|
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
||||||
|
import { OrganizationService } from "@bitwarden/common/abstractions/organization.service";
|
||||||
|
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
|
||||||
|
import { OrganizationApiKeyType } from "@bitwarden/common/enums/organizationApiKeyType";
|
||||||
|
import { OrganizationConnectionType } from "@bitwarden/common/enums/organizationConnectionType";
|
||||||
|
import { ScimConfigApi } from "@bitwarden/common/models/api/scimConfigApi";
|
||||||
|
import { Organization } from "@bitwarden/common/models/domain/organization";
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: "app-org-manage-scim",
|
||||||
|
templateUrl: "scim.component.html",
|
||||||
|
})
|
||||||
|
export class ScimComponent implements OnInit {
|
||||||
|
loading = true;
|
||||||
|
organizationId: string;
|
||||||
|
organization: Organization;
|
||||||
|
formPromise: Promise<any>;
|
||||||
|
|
||||||
|
endpointUrl: string;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
private route: ActivatedRoute,
|
||||||
|
private apiService: ApiService,
|
||||||
|
private platformUtilsService: PlatformUtilsService,
|
||||||
|
private i18nService: I18nService,
|
||||||
|
private organizationService: OrganizationService
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async ngOnInit() {
|
||||||
|
this.route.parent.parent.params.subscribe(async (params) => {
|
||||||
|
this.organizationId = params.organizationId;
|
||||||
|
await this.load();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
async load() {
|
||||||
|
this.organization = await this.organizationService.get(this.organizationId);
|
||||||
|
//TODO: Load the SCIM configuration (connection) and API Key
|
||||||
|
this.endpointUrl = "https://example.com/callback/scim";
|
||||||
|
this.loading = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
async submit() {
|
||||||
|
//this.validateForm();
|
||||||
|
|
||||||
|
//TODO: POST data update to server and update display
|
||||||
|
//this.formPromise = POST;
|
||||||
|
|
||||||
|
try {
|
||||||
|
//const response = await this.formPromise;
|
||||||
|
//this.populateForm(response);
|
||||||
|
this.platformUtilsService.showToast("success", null, this.i18nService.t("scimSettingsSaved"));
|
||||||
|
} catch {
|
||||||
|
// Logged by appApiAction, do nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
this.formPromise = null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,6 +9,7 @@ import { OrganizationLayoutComponent } from "src/app/organizations/layouts/organ
|
||||||
import { ManageComponent } from "src/app/organizations/manage/manage.component";
|
import { ManageComponent } from "src/app/organizations/manage/manage.component";
|
||||||
import { NavigationPermissionsService } from "src/app/organizations/services/navigation-permissions.service";
|
import { NavigationPermissionsService } from "src/app/organizations/services/navigation-permissions.service";
|
||||||
|
|
||||||
|
import { ScimComponent } from "./manage/scim.component";
|
||||||
import { SsoComponent } from "./manage/sso.component";
|
import { SsoComponent } from "./manage/sso.component";
|
||||||
|
|
||||||
const routes: Routes = [
|
const routes: Routes = [
|
||||||
|
@ -33,6 +34,14 @@ const routes: Routes = [
|
||||||
permissions: [Permissions.ManageSso],
|
permissions: [Permissions.ManageSso],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: "scim",
|
||||||
|
component: ScimComponent,
|
||||||
|
canActivate: [PermissionsGuard],
|
||||||
|
data: {
|
||||||
|
permissions: [Permissions.ManageScim],
|
||||||
|
},
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { OrganizationApiKeyType } from "../enums/organizationApiKeyType";
|
||||||
import { OrganizationConnectionType } from "../enums/organizationConnectionType";
|
import { OrganizationConnectionType } from "../enums/organizationConnectionType";
|
||||||
import { PolicyType } from "../enums/policyType";
|
import { PolicyType } from "../enums/policyType";
|
||||||
import { SetKeyConnectorKeyRequest } from "../models/request/account/setKeyConnectorKeyRequest";
|
import { SetKeyConnectorKeyRequest } from "../models/request/account/setKeyConnectorKeyRequest";
|
||||||
|
@ -569,7 +570,8 @@ export abstract class ApiService {
|
||||||
request: OrganizationApiKeyRequest
|
request: OrganizationApiKeyRequest
|
||||||
) => Promise<ApiKeyResponse>;
|
) => Promise<ApiKeyResponse>;
|
||||||
getOrganizationApiKeyInformation: (
|
getOrganizationApiKeyInformation: (
|
||||||
id: string
|
id: string,
|
||||||
|
type?: OrganizationApiKeyType
|
||||||
) => Promise<ListResponse<OrganizationApiKeyInformationResponse>>;
|
) => Promise<ListResponse<OrganizationApiKeyInformationResponse>>;
|
||||||
postOrganizationRotateApiKey: (
|
postOrganizationRotateApiKey: (
|
||||||
id: string,
|
id: string,
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
export enum OrganizationApiKeyType {
|
export enum OrganizationApiKeyType {
|
||||||
Default = 0,
|
Default = 0,
|
||||||
BillingSync = 1,
|
BillingSync = 1,
|
||||||
|
Scim = 2,
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
export enum OrganizationConnectionType {
|
export enum OrganizationConnectionType {
|
||||||
CloudBillingSync = 1,
|
CloudBillingSync = 1,
|
||||||
|
Scim = 2,
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,4 +25,5 @@ export enum Permissions {
|
||||||
DeleteAssignedCollections,
|
DeleteAssignedCollections,
|
||||||
ManageSso,
|
ManageSso,
|
||||||
ManageBilling,
|
ManageBilling,
|
||||||
|
ManageScim,
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
export enum ScimProviderType {
|
||||||
|
AzureAd = 0,
|
||||||
|
Okta = 1,
|
||||||
|
OneLogin = 2,
|
||||||
|
JumpCloud = 3,
|
||||||
|
GoogleWorkspace = 4,
|
||||||
|
Rippling = 5,
|
||||||
|
}
|
|
@ -25,6 +25,7 @@ export class PermissionsApi extends BaseResponse {
|
||||||
managePolicies: boolean;
|
managePolicies: boolean;
|
||||||
manageUsers: boolean;
|
manageUsers: boolean;
|
||||||
manageResetPassword: boolean;
|
manageResetPassword: boolean;
|
||||||
|
manageScim: boolean;
|
||||||
|
|
||||||
constructor(data: any = null) {
|
constructor(data: any = null) {
|
||||||
super(data);
|
super(data);
|
||||||
|
@ -51,5 +52,6 @@ export class PermissionsApi extends BaseResponse {
|
||||||
this.managePolicies = this.getResponseProperty("ManagePolicies");
|
this.managePolicies = this.getResponseProperty("ManagePolicies");
|
||||||
this.manageUsers = this.getResponseProperty("ManageUsers");
|
this.manageUsers = this.getResponseProperty("ManageUsers");
|
||||||
this.manageResetPassword = this.getResponseProperty("ManageResetPassword");
|
this.manageResetPassword = this.getResponseProperty("ManageResetPassword");
|
||||||
|
this.manageScim = this.getResponseProperty("ManageScim");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
import { ScimProviderType } from "@bitwarden/common/enums/scimProviderType";
|
||||||
|
|
||||||
|
import { BaseResponse } from "../response/baseResponse";
|
||||||
|
|
||||||
|
export class ScimConfigApi extends BaseResponse {
|
||||||
|
enabled: boolean;
|
||||||
|
scimProvider: ScimProviderType;
|
||||||
|
|
||||||
|
constructor(data: any) {
|
||||||
|
super(data);
|
||||||
|
if (data == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
this.enabled = this.getResponseProperty("Enabled");
|
||||||
|
this.scimProvider = this.getResponseProperty("ScimProvider");
|
||||||
|
}
|
||||||
|
}
|
|
@ -19,6 +19,7 @@ export class OrganizationData {
|
||||||
useApi: boolean;
|
useApi: boolean;
|
||||||
useSso: boolean;
|
useSso: boolean;
|
||||||
useKeyConnector: boolean;
|
useKeyConnector: boolean;
|
||||||
|
useScim: boolean;
|
||||||
useResetPassword: boolean;
|
useResetPassword: boolean;
|
||||||
selfHost: boolean;
|
selfHost: boolean;
|
||||||
usersGetPremium: boolean;
|
usersGetPremium: boolean;
|
||||||
|
@ -58,6 +59,7 @@ export class OrganizationData {
|
||||||
this.useApi = response.useApi;
|
this.useApi = response.useApi;
|
||||||
this.useSso = response.useSso;
|
this.useSso = response.useSso;
|
||||||
this.useKeyConnector = response.useKeyConnector;
|
this.useKeyConnector = response.useKeyConnector;
|
||||||
|
this.useScim = response.useScim;
|
||||||
this.useResetPassword = response.useResetPassword;
|
this.useResetPassword = response.useResetPassword;
|
||||||
this.selfHost = response.selfHost;
|
this.selfHost = response.selfHost;
|
||||||
this.usersGetPremium = response.usersGetPremium;
|
this.usersGetPremium = response.usersGetPremium;
|
||||||
|
|
|
@ -20,6 +20,7 @@ export class Organization {
|
||||||
useApi: boolean;
|
useApi: boolean;
|
||||||
useSso: boolean;
|
useSso: boolean;
|
||||||
useKeyConnector: boolean;
|
useKeyConnector: boolean;
|
||||||
|
useScim: boolean;
|
||||||
useResetPassword: boolean;
|
useResetPassword: boolean;
|
||||||
selfHost: boolean;
|
selfHost: boolean;
|
||||||
usersGetPremium: boolean;
|
usersGetPremium: boolean;
|
||||||
|
@ -63,6 +64,7 @@ export class Organization {
|
||||||
this.useApi = obj.useApi;
|
this.useApi = obj.useApi;
|
||||||
this.useSso = obj.useSso;
|
this.useSso = obj.useSso;
|
||||||
this.useKeyConnector = obj.useKeyConnector;
|
this.useKeyConnector = obj.useKeyConnector;
|
||||||
|
this.useScim = obj.useScim;
|
||||||
this.useResetPassword = obj.useResetPassword;
|
this.useResetPassword = obj.useResetPassword;
|
||||||
this.selfHost = obj.selfHost;
|
this.selfHost = obj.selfHost;
|
||||||
this.usersGetPremium = obj.usersGetPremium;
|
this.usersGetPremium = obj.usersGetPremium;
|
||||||
|
@ -173,6 +175,10 @@ export class Organization {
|
||||||
return this.isAdmin || this.permissions.manageSso;
|
return this.isAdmin || this.permissions.manageSso;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get canManageScim() {
|
||||||
|
return this.isAdmin || this.permissions.manageScim;
|
||||||
|
}
|
||||||
|
|
||||||
get canManagePolicies() {
|
get canManagePolicies() {
|
||||||
return this.isAdmin || this.permissions.managePolicies;
|
return this.isAdmin || this.permissions.managePolicies;
|
||||||
}
|
}
|
||||||
|
@ -207,6 +213,7 @@ export class Organization {
|
||||||
(permissions.includes(Permissions.ManageUsers) && this.canManageUsers) ||
|
(permissions.includes(Permissions.ManageUsers) && this.canManageUsers) ||
|
||||||
(permissions.includes(Permissions.ManageUsersPassword) && this.canManageUsersPassword) ||
|
(permissions.includes(Permissions.ManageUsersPassword) && this.canManageUsersPassword) ||
|
||||||
(permissions.includes(Permissions.ManageSso) && this.canManageSso) ||
|
(permissions.includes(Permissions.ManageSso) && this.canManageSso) ||
|
||||||
|
(permissions.includes(Permissions.ManageScim) && this.canManageScim) ||
|
||||||
(permissions.includes(Permissions.ManageBilling) && this.canManageBilling);
|
(permissions.includes(Permissions.ManageBilling) && this.canManageBilling);
|
||||||
|
|
||||||
return specifiedPermissions && (this.enabled || this.isOwner);
|
return specifiedPermissions && (this.enabled || this.isOwner);
|
||||||
|
|
|
@ -17,6 +17,7 @@ export class ProfileOrganizationResponse extends BaseResponse {
|
||||||
useApi: boolean;
|
useApi: boolean;
|
||||||
useSso: boolean;
|
useSso: boolean;
|
||||||
useKeyConnector: boolean;
|
useKeyConnector: boolean;
|
||||||
|
useScim: boolean;
|
||||||
useResetPassword: boolean;
|
useResetPassword: boolean;
|
||||||
selfHost: boolean;
|
selfHost: boolean;
|
||||||
usersGetPremium: boolean;
|
usersGetPremium: boolean;
|
||||||
|
@ -57,6 +58,7 @@ export class ProfileOrganizationResponse extends BaseResponse {
|
||||||
this.useApi = this.getResponseProperty("UseApi");
|
this.useApi = this.getResponseProperty("UseApi");
|
||||||
this.useSso = this.getResponseProperty("UseSso");
|
this.useSso = this.getResponseProperty("UseSso");
|
||||||
this.useKeyConnector = this.getResponseProperty("UseKeyConnector") ?? false;
|
this.useKeyConnector = this.getResponseProperty("UseKeyConnector") ?? false;
|
||||||
|
this.useScim = this.getResponseProperty("UseScim") ?? false;
|
||||||
this.useResetPassword = this.getResponseProperty("UseResetPassword");
|
this.useResetPassword = this.getResponseProperty("UseResetPassword");
|
||||||
this.selfHost = this.getResponseProperty("SelfHost");
|
this.selfHost = this.getResponseProperty("SelfHost");
|
||||||
this.usersGetPremium = this.getResponseProperty("UsersGetPremium");
|
this.usersGetPremium = this.getResponseProperty("UsersGetPremium");
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { EnvironmentService } from "../abstractions/environment.service";
|
||||||
import { PlatformUtilsService } from "../abstractions/platformUtils.service";
|
import { PlatformUtilsService } from "../abstractions/platformUtils.service";
|
||||||
import { TokenService } from "../abstractions/token.service";
|
import { TokenService } from "../abstractions/token.service";
|
||||||
import { DeviceType } from "../enums/deviceType";
|
import { DeviceType } from "../enums/deviceType";
|
||||||
|
import { OrganizationApiKeyType } from "../enums/organizationApiKeyType";
|
||||||
import { OrganizationConnectionType } from "../enums/organizationConnectionType";
|
import { OrganizationConnectionType } from "../enums/organizationConnectionType";
|
||||||
import { PolicyType } from "../enums/policyType";
|
import { PolicyType } from "../enums/policyType";
|
||||||
import { Utils } from "../misc/utils";
|
import { Utils } from "../misc/utils";
|
||||||
|
@ -1840,15 +1841,14 @@ export class ApiService implements ApiServiceAbstraction {
|
||||||
}
|
}
|
||||||
|
|
||||||
async getOrganizationApiKeyInformation(
|
async getOrganizationApiKeyInformation(
|
||||||
id: string
|
id: string,
|
||||||
|
type: OrganizationApiKeyType = null
|
||||||
): Promise<ListResponse<OrganizationApiKeyInformationResponse>> {
|
): Promise<ListResponse<OrganizationApiKeyInformationResponse>> {
|
||||||
const r = await this.send(
|
const uri =
|
||||||
"GET",
|
type === null
|
||||||
"/organizations/" + id + "/api-key-information",
|
? "/organizations/" + id + "/api-key-information"
|
||||||
null,
|
: "/organizations/" + id + "/api-key-information/" + type;
|
||||||
true,
|
const r = await this.send("GET", uri, null, true, true);
|
||||||
true
|
|
||||||
);
|
|
||||||
return new ListResponse(r, OrganizationApiKeyInformationResponse);
|
return new ListResponse(r, OrganizationApiKeyInformationResponse);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue