diff --git a/jslib b/jslib
index cea09a22e5..6ac6df75d7 160000
--- a/jslib
+++ b/jslib
@@ -1 +1 @@
-Subproject commit cea09a22e533ef3598bb497ba0503c2fcd5b2dc1
+Subproject commit 6ac6df75d7a9bd5ea58f5d8310f1b3e34abd2bde
diff --git a/src/app/app-routing.module.ts b/src/app/app-routing.module.ts
index cf40fcc0aa..c8d497b798 100644
--- a/src/app/app-routing.module.ts
+++ b/src/app/app-routing.module.ts
@@ -91,9 +91,10 @@ import { UnauthGuardService } from './services/unauth-guard.service';
import { AuthGuardService } from 'jslib/angular/services/auth-guard.service';
-import { OrganizationUserType } from 'jslib/enums/organizationUserType';
-import { EmergencyAccessComponent } from './settings/emergency-access.component';
+import { Permissions } from 'jslib/enums/permissions';
+
import { EmergencyAccessViewComponent } from './settings/emergency-access-view.component';
+import { EmergencyAccessComponent } from './settings/emergency-access.component';
const routes: Routes = [
{
@@ -259,35 +260,75 @@ const routes: Routes = [
path: 'tools',
component: OrgToolsComponent,
canActivate: [OrganizationTypeGuardService],
- data: { allowedTypes: [OrganizationUserType.Owner, OrganizationUserType.Admin] },
+ data: { permissions: [Permissions.AccessImportExport, Permissions.AccessReports] },
children: [
- { path: '', pathMatch: 'full', redirectTo: 'import' },
- { path: 'import', component: OrgImportComponent, data: { titleId: 'importData' } },
- { path: 'export', component: OrgExportComponent, data: { titleId: 'exportVault' } },
+ {
+ path: '',
+ pathMatch: 'full',
+ redirectTo: 'import',
+ },
+ {
+ path: 'import',
+ component: OrgImportComponent,
+ canActivate: [OrganizationTypeGuardService],
+ data: {
+ titleId: 'importData',
+ permissions: [Permissions.AccessImportExport],
+ },
+ },
+ {
+ path: 'export',
+ component: OrgExportComponent,
+ canActivate: [OrganizationTypeGuardService],
+ data: {
+ titleId: 'exportVault',
+ permissions: [Permissions.AccessImportExport],
+ },
+ },
{
path: 'exposed-passwords-report',
component: OrgExposedPasswordsReportComponent,
- data: { titleId: 'exposedPasswordsReport' },
+ canActivate: [OrganizationTypeGuardService],
+ data: {
+ titleId: 'exposedPasswordsReport',
+ permissions: [Permissions.AccessReports],
+ },
},
{
path: 'inactive-two-factor-report',
component: OrgInactiveTwoFactorReportComponent,
- data: { titleId: 'inactive2faReport' },
+ canActivate: [OrganizationTypeGuardService],
+ data: {
+ titleId: 'inactive2faReport',
+ permissions: [Permissions.AccessReports],
+ },
},
{
path: 'reused-passwords-report',
component: OrgReusedPasswordsReportComponent,
- data: { titleId: 'reusedPasswordsReport' },
+ canActivate: [OrganizationTypeGuardService],
+ data: {
+ titleId: 'reusedPasswordsReport',
+ permissions: [Permissions.AccessReports],
+ },
},
{
path: 'unsecured-websites-report',
component: OrgUnsecuredWebsitesReportComponent,
- data: { titleId: 'unsecuredWebsitesReport' },
+ canActivate: [OrganizationTypeGuardService],
+ data: {
+ titleId: 'unsecuredWebsitesReport',
+ permissions: [Permissions.AccessReports],
+ },
},
{
path: 'weak-passwords-report',
component: OrgWeakPasswordsReportComponent,
- data: { titleId: 'weakPasswordsReport' },
+ canActivate: [OrganizationTypeGuardService],
+ data: {
+ titleId: 'weakPasswordsReport',
+ permissions: [Permissions.AccessReports],
+ },
},
],
},
@@ -296,26 +337,73 @@ const routes: Routes = [
component: OrgManageComponent,
canActivate: [OrganizationTypeGuardService],
data: {
- allowedTypes: [
- OrganizationUserType.Owner,
- OrganizationUserType.Admin,
- OrganizationUserType.Manager,
+ permissions: [
+ Permissions.ManageAssignedCollections,
+ Permissions.ManageAllCollections,
+ Permissions.AccessEventLogs,
+ Permissions.ManageGroups,
+ Permissions.ManageUsers,
+ Permissions.ManagePolicies,
],
},
children: [
- { path: '', pathMatch: 'full', redirectTo: 'people' },
- { path: 'collections', component: OrgManageCollectionsComponent, data: { titleId: 'collections' } },
- { path: 'events', component: OrgEventsComponent, data: { titleId: 'eventLogs' } },
- { path: 'groups', component: OrgGroupsComponent, data: { titleId: 'groups' } },
- { path: 'people', component: OrgPeopleComponent, data: { titleId: 'people' } },
- { path: 'policies', component: OrgPoliciesComponent, data: { titleId: 'policies' } },
+ {
+ path: '',
+ pathMatch: 'full',
+ redirectTo: 'people',
+ },
+ {
+ path: 'collections',
+ component: OrgManageCollectionsComponent,
+ canActivate: [OrganizationTypeGuardService],
+ data: {
+ titleId: 'collections',
+ permissions: [Permissions.ManageAssignedCollections, Permissions.ManageAllCollections],
+ },
+ },
+ {
+ path: 'events',
+ component: OrgEventsComponent,
+ canActivate: [OrganizationTypeGuardService],
+ data: {
+ titleId: 'eventLogs',
+ permissions: [Permissions.AccessEventLogs],
+ },
+ },
+ {
+ path: 'groups',
+ component: OrgGroupsComponent,
+ canActivate: [OrganizationTypeGuardService],
+ data: {
+ titleId: 'groups',
+ permissions: [Permissions.ManageGroups],
+ },
+ },
+ {
+ path: 'people',
+ component: OrgPeopleComponent,
+ canActivate: [OrganizationTypeGuardService],
+ data: {
+ titleId: 'people',
+ permissions: [Permissions.ManageUsers],
+ },
+ },
+ {
+ path: 'policies',
+ component: OrgPoliciesComponent,
+ canActivate: [OrganizationTypeGuardService],
+ data: {
+ titleId: 'policies',
+ permissions: [Permissions.ManagePolicies],
+ },
+ },
],
},
{
path: 'settings',
component: OrgSettingsComponent,
canActivate: [OrganizationTypeGuardService],
- data: { allowedTypes: [OrganizationUserType.Owner] },
+ data: { permissions: [Permissions.ManageOrganization] },
children: [
{ path: '', pathMatch: 'full', redirectTo: 'account' },
{ path: 'account', component: OrgAccountComponent, data: { titleId: 'myOrganization' } },
@@ -340,6 +428,7 @@ const routes: Routes = [
@NgModule({
imports: [RouterModule.forRoot(routes, {
useHash: true,
+ paramsInheritanceStrategy: 'always',
/*enableTracing: true,*/
})],
exports: [RouterModule],
diff --git a/src/app/layouts/organization-layout.component.html b/src/app/layouts/organization-layout.component.html
index c7843f1e90..93f18d5d97 100644
--- a/src/app/layouts/organization-layout.component.html
+++ b/src/app/layouts/organization-layout.component.html
@@ -15,21 +15,21 @@
-
+
-
diff --git a/src/app/layouts/organization-layout.component.ts b/src/app/layouts/organization-layout.component.ts
index 5c3e36a203..e5d326775b 100644
--- a/src/app/layouts/organization-layout.component.ts
+++ b/src/app/layouts/organization-layout.component.ts
@@ -24,9 +24,9 @@ const BroadcasterSubscriptionId = 'OrganizationLayoutComponent';
})
export class OrganizationLayoutComponent implements OnInit, OnDestroy {
organization: Organization;
- enterpriseTokenPromise: Promise
;
+ businessTokenPromise: Promise;
private organizationId: string;
- private enterpriseUrl: string;
+ private businessUrl: string;
constructor(private route: ActivatedRoute, private userService: UserService,
private broadcasterService: BroadcasterService, private ngZone: NgZone,
@@ -34,11 +34,11 @@ export class OrganizationLayoutComponent implements OnInit, OnDestroy {
private environmentService: EnvironmentService) { }
ngOnInit() {
- this.enterpriseUrl = 'https://portal.bitwarden.com';
+ this.businessUrl = 'https://portal.bitwarden.com';
if (this.environmentService.enterpriseUrl != null) {
- this.enterpriseUrl = this.environmentService.enterpriseUrl;
+ this.businessUrl = this.environmentService.enterpriseUrl;
} else if (this.environmentService.baseUrl != null) {
- this.enterpriseUrl = this.environmentService.baseUrl + '/portal';
+ this.businessUrl = this.environmentService.baseUrl + '/portal';
}
document.body.classList.remove('layout_frontend');
@@ -65,19 +65,68 @@ export class OrganizationLayoutComponent implements OnInit, OnDestroy {
this.organization = await this.userService.getOrganization(this.organizationId);
}
- async goToEnterprisePortal() {
- if (this.enterpriseTokenPromise != null) {
+ async goToBusinessPortal() {
+ if (this.businessTokenPromise != null) {
return;
}
try {
- this.enterpriseTokenPromise = this.apiService.getEnterprisePortalSignInToken();
- const token = await this.enterpriseTokenPromise;
+ this.businessTokenPromise = this.apiService.getEnterprisePortalSignInToken();
+ const token = await this.businessTokenPromise;
if (token != null) {
const userId = await this.userService.getUserId();
- this.platformUtilsService.launchUri(this.enterpriseUrl + '/login?userId=' + userId +
+ this.platformUtilsService.launchUri(this.businessUrl + '/login?userId=' + userId +
'&token=' + (window as any).encodeURIComponent(token) + '&organizationId=' + this.organization.id);
}
} catch { }
- this.enterpriseTokenPromise = null;
+ this.businessTokenPromise = null;
+ }
+
+ get showMenuBar() {
+ return this.showManageTab || this.showToolsTab || this.organization.isOwner;
+ }
+
+ get showManageTab(): boolean {
+ return this.organization.canManageUsers ||
+ this.organization.canManageAssignedCollections ||
+ this.organization.canManageAllCollections ||
+ this.organization.canManageGroups ||
+ this.organization.canManagePolicies ||
+ this.organization.canAccessEventLogs;
+ }
+
+ get showToolsTab(): boolean {
+ return this.organization.canAccessImportExport || this.organization.canAccessReports;
+ }
+
+ get showBusinessPortalButton(): boolean {
+ return this.organization.useBusinessPortal && this.organization.canAccessBusinessPortal;
+ }
+
+ get toolsRoute(): string {
+ return this.organization.canAccessImportExport ?
+ 'tools/import' :
+ 'tools/exposed-passwords-report';
+ }
+
+ get manageRoute(): string {
+ let route: string;
+ switch (true) {
+ case this.organization.canManageUsers:
+ route = 'manage/people';
+ break;
+ case this.organization.canManageAssignedCollections || this.organization.canManageAllCollections:
+ route = 'manage/collections';
+ break;
+ case this.organization.canManageGroups:
+ route = 'manage/groups';
+ break;
+ case this.organization.canManagePolicies:
+ route = 'manage/policies';
+ break;
+ case this.organization.canAccessEventLogs:
+ route = 'manage/events';
+ break;
+ }
+ return route;
}
}
diff --git a/src/app/organizations/manage/collections.component.ts b/src/app/organizations/manage/collections.component.ts
index 0da67ba26e..32fe50bf15 100644
--- a/src/app/organizations/manage/collections.component.ts
+++ b/src/app/organizations/manage/collections.component.ts
@@ -72,7 +72,7 @@ export class CollectionsComponent implements OnInit {
async load() {
const organization = await this.userService.getOrganization(this.organizationId);
let response: ListResponse;
- if (organization.isAdmin) {
+ if (organization.canManageAllCollections) {
response = await this.apiService.getCollections(this.organizationId);
} else {
response = await this.apiService.getUserCollections();
diff --git a/src/app/organizations/manage/manage.component.html b/src/app/organizations/manage/manage.component.html
index c54f1a117f..da0052a394 100644
--- a/src/app/organizations/manage/manage.component.html
+++ b/src/app/organizations/manage/manage.component.html
@@ -5,22 +5,23 @@
diff --git a/src/app/organizations/manage/people.component.html b/src/app/organizations/manage/people.component.html
index 5473f75e3e..82ea5b70eb 100644
--- a/src/app/organizations/manage/people.component.html
+++ b/src/app/organizations/manage/people.component.html
@@ -69,6 +69,7 @@
{{'admin' | i18n}}
{{'manager' | i18n}}
{{'user' | i18n}}
+ {{'custom' | i18n}}
diff --git a/src/app/organizations/manage/people.component.ts b/src/app/organizations/manage/people.component.ts
index f27305d52c..e3687cc378 100644
--- a/src/app/organizations/manage/people.component.ts
+++ b/src/app/organizations/manage/people.component.ts
@@ -79,7 +79,7 @@ export class PeopleComponent implements OnInit {
this.route.parent.parent.params.subscribe(async (params) => {
this.organizationId = params.organizationId;
const organization = await this.userService.getOrganization(this.organizationId);
- if (!organization.isAdmin) {
+ if (!organization.canManageUsers) {
this.router.navigate(['../collections'], { relativeTo: this.route });
return;
}
diff --git a/src/app/organizations/manage/user-add-edit.component.html b/src/app/organizations/manage/user-add-edit.component.html
index 573d038dde..9f2745c921 100644
--- a/src/app/organizations/manage/user-add-edit.component.html
+++ b/src/app/organizations/manage/user-add-edit.component.html
@@ -63,8 +63,127 @@
{{'ownerDesc' | i18n}}
+
+
+
+
+
+
+ {{'permissions' | i18n}}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
|
- {{c.name}}
+
+ {{c.name}}
+
+
+ {{c.name}}
+
{{'shared' | i18n}}
diff --git a/src/app/tools/exposed-passwords-report.component.ts b/src/app/tools/exposed-passwords-report.component.ts
index b1d6cde0d2..a5fd0863b6 100644
--- a/src/app/tools/exposed-passwords-report.component.ts
+++ b/src/app/tools/exposed-passwords-report.component.ts
@@ -61,4 +61,9 @@ export class ExposedPasswordsReportComponent extends CipherReportComponent imple
protected getAllCiphers(): Promise {
return this.cipherService.getAllDecrypted();
}
+
+ protected canManageCipher(c: CipherView): boolean {
+ // this will only ever be false from the org view;
+ return true;
+ }
}
diff --git a/src/app/tools/reused-passwords-report.component.html b/src/app/tools/reused-passwords-report.component.html
index 7f1938b389..ed527d2241 100644
--- a/src/app/tools/reused-passwords-report.component.html
+++ b/src/app/tools/reused-passwords-report.component.html
@@ -27,7 +27,12 @@
|
- {{c.name}}
+
+ {{c.name}}
+
+
+ {{c.name}}
+
{{'shared' | i18n}}
diff --git a/src/app/tools/reused-passwords-report.component.ts b/src/app/tools/reused-passwords-report.component.ts
index cb935acf6e..af33cc9c58 100644
--- a/src/app/tools/reused-passwords-report.component.ts
+++ b/src/app/tools/reused-passwords-report.component.ts
@@ -55,4 +55,9 @@ export class ReusedPasswordsReportComponent extends CipherReportComponent implem
protected getAllCiphers(): Promise {
return this.cipherService.getAllDecrypted();
}
+
+ protected canManageCipher(c: CipherView): boolean {
+ // this will only ever be false from an organization view
+ return true;
+ }
}
diff --git a/src/app/tools/weak-passwords-report.component.html b/src/app/tools/weak-passwords-report.component.html
index 85bcf35f03..83232e4c29 100644
--- a/src/app/tools/weak-passwords-report.component.html
+++ b/src/app/tools/weak-passwords-report.component.html
@@ -27,7 +27,12 @@
|
- {{c.name}}
+
+ {{c.name}}
+
+
+ {{c.name}}
+
{{'shared' | i18n}}
diff --git a/src/app/tools/weak-passwords-report.component.ts b/src/app/tools/weak-passwords-report.component.ts
index 17982e64f7..a6ddb848a9 100644
--- a/src/app/tools/weak-passwords-report.component.ts
+++ b/src/app/tools/weak-passwords-report.component.ts
@@ -75,6 +75,11 @@ export class WeakPasswordsReportComponent extends CipherReportComponent implemen
return this.cipherService.getAllDecrypted();
}
+ protected canManageCipher(c: CipherView): boolean {
+ // this will only ever be false from the org view;
+ return true;
+ }
+
private scoreKey(score: number): [string, string] {
switch (score) {
case 4:
diff --git a/src/app/vault/bulk-delete.component.ts b/src/app/vault/bulk-delete.component.ts
index fc6584f7c5..f4e9ae28e0 100644
--- a/src/app/vault/bulk-delete.component.ts
+++ b/src/app/vault/bulk-delete.component.ts
@@ -31,7 +31,7 @@ export class BulkDeleteComponent {
private apiService: ApiService) { }
async submit() {
- if (!this.organization || !this.organization.isAdmin) {
+ if (!this.organization || !this.organization.canManageAllCollections) {
await this.deleteCiphers();
} else {
await this.deleteCiphersAdmin();
diff --git a/src/locales/en/messages.json b/src/locales/en/messages.json
index 6f05f75bda..45f5cdeb47 100644
--- a/src/locales/en/messages.json
+++ b/src/locales/en/messages.json
@@ -3577,6 +3577,45 @@
"estimatedTax": {
"message": "Estimated tax"
},
+ "custom": {
+ "message": "Custom"
+ },
+ "customDesc": {
+ "message": "Allows more granular control of user permissions for advanced configurations."
+ },
+ "permissions": {
+ "message": "Permissions"
+ },
+ "accessBusinessPortal": {
+ "message": "Access Business Portal"
+ },
+ "accessEventLogs": {
+ "message": "Access Event Logs"
+ },
+ "accessImportExport": {
+ "message": "Access Import/Export"
+ },
+ "accessReports": {
+ "message": "Access Reports"
+ },
+ "manageAllCollections": {
+ "message": "Manage All Collections"
+ },
+ "manageAssignedCollections": {
+ "message": "Manage Assigned Collections"
+ },
+ "manageGroups": {
+ "message": "Manage Groups"
+ },
+ "managePolicies": {
+ "message": "Manage Policies"
+ },
+ "manageSso": {
+ "message": "Manage Sso"
+ },
+ "manageUsers": {
+ "message": "Manage Users"
+ },
"disableRequireSsoError": {
"message": "You must manually disable the Single Sign-On Authentication policy before this policy can be disabled."
},
diff --git a/tslint.json b/tslint.json
index cb3f6b016f..c28f3d1a62 100644
--- a/tslint.json
+++ b/tslint.json
@@ -53,6 +53,13 @@
"semicolon": [
true,
"always"
+ ],
+ "trailing-comma": [
+ true,
+ {
+ "multiline": "always",
+ "singleline": "never"
+ }
]
}
}
|