;
organizationUserType = OrganizationUserType;
+ manageAllCollectionsCheckboxes = [
+ {
+ id: 'createNewCollections',
+ get: () => this.permissions.createNewCollections,
+ set: (v: boolean) => this.permissions.createNewCollections = v,
+ },
+ {
+ id: 'editAnyCollection',
+ get: () => this.permissions.editAnyCollection,
+ set: (v: boolean) => this.permissions.editAnyCollection = v,
+ },
+ {
+ id: 'deleteAnyCollection',
+ get: () => this.permissions.deleteAnyCollection,
+ set: (v: boolean) => this.permissions.deleteAnyCollection = v,
+ },
+ ];
+
+ manageAssignedCollectionsCheckboxes = [
+ {
+ id: 'editAssignedCollections',
+ get: () => this.permissions.editAssignedCollections,
+ set: (v: boolean) => this.permissions.editAssignedCollections = v,
+ },
+ {
+ id: 'deleteAssignedCollections',
+ get: () => this.permissions.deleteAssignedCollections,
+ set: (v: boolean) => this.permissions.deleteAssignedCollections = v,
+ },
+ ];
+
+ get fallbackToManageAllCollections() {
+ return this.permissions.createNewCollections == null &&
+ this.permissions.editAnyCollection == null &&
+ this.permissions.deleteAnyCollection == null;
+ }
+
+ get fallbackToManageAssignedCollections() {
+ return this.permissions.editAssignedCollections == null &&
+ this.permissions.deleteAssignedCollections == null;
+ }
+
get customUserTypeSelected(): boolean {
return this.type === OrganizationUserType.Custom;
}
@@ -107,39 +149,7 @@ export class UserAddEditComponent implements OnInit {
}
setRequestPermissions(p: PermissionsApi, clearPermissions: boolean) {
- p.accessBusinessPortal = clearPermissions ?
- false :
- this.permissions.accessBusinessPortal;
- p.accessEventLogs = this.permissions.accessEventLogs = clearPermissions ?
- false :
- this.permissions.accessEventLogs;
- p.accessImportExport = clearPermissions ?
- false :
- this.permissions.accessImportExport;
- p.accessReports = clearPermissions ?
- false :
- this.permissions.accessReports;
- p.manageAllCollections = clearPermissions ?
- false :
- this.permissions.manageAllCollections;
- p.manageAssignedCollections = clearPermissions ?
- false :
- this.permissions.manageAssignedCollections;
- p.manageGroups = clearPermissions ?
- false :
- this.permissions.manageGroups;
- p.manageSso = clearPermissions ?
- false :
- this.permissions.manageSso;
- p.managePolicies = clearPermissions ?
- false :
- this.permissions.managePolicies;
- p.manageUsers = clearPermissions ?
- false :
- this.permissions.manageUsers;
- p.manageResetPassword = clearPermissions ?
- false :
- this.permissions.manageResetPassword;
+ Object.assign(p, clearPermissions ? new PermissionsApi() : this.permissions);
return p;
}
@@ -203,5 +213,4 @@ export class UserAddEditComponent implements OnInit {
this.onDeletedUser.emit();
} catch { }
}
-
}
diff --git a/src/app/organizations/vault/add-edit.component.ts b/src/app/organizations/vault/add-edit.component.ts
index 4dce9e2b057..aa392255d58 100644
--- a/src/app/organizations/vault/add-edit.component.ts
+++ b/src/app/organizations/vault/add-edit.component.ts
@@ -46,7 +46,7 @@ export class AddEditComponent extends BaseAddEditComponent {
protected allowOwnershipAssignment() {
if (this.ownershipOptions != null && (this.ownershipOptions.length > 1 || !this.allowPersonal)) {
if (this.organization != null) {
- return this.cloneMode && this.organization.canManageAllCollections;
+ return this.cloneMode && this.organization.canEditAnyCollection;
} else {
return !this.editMode || this.cloneMode;
}
@@ -55,14 +55,14 @@ export class AddEditComponent extends BaseAddEditComponent {
}
protected loadCollections() {
- if (!this.organization.canManageAllCollections) {
+ if (!this.organization.canEditAnyCollection) {
return super.loadCollections();
}
return Promise.resolve(this.collections);
}
protected async loadCipher() {
- if (!this.organization.canManageAllCollections) {
+ if (!this.organization.canEditAnyCollection) {
return await super.loadCipher();
}
const response = await this.apiService.getCipherAdmin(this.cipherId);
@@ -72,14 +72,14 @@ export class AddEditComponent extends BaseAddEditComponent {
}
protected encryptCipher() {
- if (!this.organization.canManageAllCollections) {
+ if (!this.organization.canEditAnyCollection) {
return super.encryptCipher();
}
return this.cipherService.encrypt(this.cipher, null, this.originalCipher);
}
protected async saveCipher(cipher: Cipher) {
- if (!this.organization.canManageAllCollections || cipher.organizationId == null) {
+ if (!this.organization.canEditAnyCollection || cipher.organizationId == null) {
return super.saveCipher(cipher);
}
if (this.editMode && !this.cloneMode) {
@@ -92,7 +92,7 @@ export class AddEditComponent extends BaseAddEditComponent {
}
protected async deleteCipher() {
- if (!this.organization.canManageAllCollections) {
+ if (!this.organization.canEditAnyCollection) {
return super.deleteCipher();
}
return this.cipher.isDeleted ? this.apiService.deleteCipherAdmin(this.cipherId)
diff --git a/src/app/organizations/vault/attachments.component.ts b/src/app/organizations/vault/attachments.component.ts
index 9c11b247679..66cca981e10 100644
--- a/src/app/organizations/vault/attachments.component.ts
+++ b/src/app/organizations/vault/attachments.component.ts
@@ -30,13 +30,13 @@ export class AttachmentsComponent extends BaseAttachmentsComponent {
}
protected async reupload(attachment: AttachmentView) {
- if (this.organization.canManageAllCollections && this.showFixOldAttachments(attachment)) {
+ if (this.organization.canEditAnyCollection && this.showFixOldAttachments(attachment)) {
await super.reuploadCipherAttachment(attachment, true);
}
}
protected async loadCipher() {
- if (!this.organization.canManageAllCollections) {
+ if (!this.organization.canEditAnyCollection) {
return await super.loadCipher();
}
const response = await this.apiService.getCipherAdmin(this.cipherId);
@@ -44,17 +44,17 @@ export class AttachmentsComponent extends BaseAttachmentsComponent {
}
protected saveCipherAttachment(file: File) {
- return this.cipherService.saveAttachmentWithServer(this.cipherDomain, file, this.organization.canManageAllCollections);
+ return this.cipherService.saveAttachmentWithServer(this.cipherDomain, file, this.organization.canEditAnyCollection);
}
protected deleteCipherAttachment(attachmentId: string) {
- if (!this.organization.canManageAllCollections) {
+ if (!this.organization.canEditAnyCollection) {
return super.deleteCipherAttachment(attachmentId);
}
return this.apiService.deleteCipherAttachmentAdmin(this.cipherId, attachmentId);
}
protected showFixOldAttachments(attachment: AttachmentView) {
- return attachment.key == null && this.organization.canManageAllCollections;
+ return attachment.key == null && this.organization.canEditAnyCollection;
}
}
diff --git a/src/app/organizations/vault/ciphers.component.ts b/src/app/organizations/vault/ciphers.component.ts
index e83e6ba135f..0096743ff29 100644
--- a/src/app/organizations/vault/ciphers.component.ts
+++ b/src/app/organizations/vault/ciphers.component.ts
@@ -42,7 +42,7 @@ export class CiphersComponent extends BaseCiphersComponent {
}
async load(filter: (cipher: CipherView) => boolean = null) {
- if (this.organization.canManageAllCollections) {
+ if (this.organization.canViewAllCollections) {
this.accessEvents = this.organization.useEvents;
this.allCiphers = await this.cipherService.getAllFromApiForOrganization(this.organization.id);
} else {
@@ -54,7 +54,7 @@ export class CiphersComponent extends BaseCiphersComponent {
}
async applyFilter(filter: (cipher: CipherView) => boolean = null) {
- if (this.organization.canManageAllCollections) {
+ if (this.organization.canViewAllCollections) {
await super.applyFilter(filter);
} else {
const f = (c: CipherView) => c.organizationId === this.organization.id && (filter == null || filter(c));
@@ -70,13 +70,13 @@ export class CiphersComponent extends BaseCiphersComponent {
}
protected deleteCipher(id: string) {
- if (!this.organization.canManageAllCollections) {
+ if (!this.organization.canEditAnyCollection) {
return super.deleteCipher(id, this.deleted);
}
return this.deleted ? this.apiService.deleteCipherAdmin(id) : this.apiService.putDeleteCipherAdmin(id);
}
protected showFixOldAttachments(c: CipherView) {
- return this.organization.canManageAllCollections && c.hasOldAttachments;
+ return this.organization.canEditAnyCollection && c.hasOldAttachments;
}
}
diff --git a/src/app/organizations/vault/collections.component.ts b/src/app/organizations/vault/collections.component.ts
index 5bdcedc9051..85b10378143 100644
--- a/src/app/organizations/vault/collections.component.ts
+++ b/src/app/organizations/vault/collections.component.ts
@@ -28,7 +28,7 @@ export class CollectionsComponent extends BaseCollectionsComponent {
}
protected async loadCipher() {
- if (!this.organization.canManageAllCollections) {
+ if (!this.organization.canViewAllCollections) {
return await super.loadCipher();
}
const response = await this.apiService.getCipherAdmin(this.cipherId);
@@ -36,21 +36,21 @@ export class CollectionsComponent extends BaseCollectionsComponent {
}
protected loadCipherCollections() {
- if (!this.organization.canManageAllCollections) {
+ if (!this.organization.canViewAllCollections) {
return super.loadCipherCollections();
}
return this.collectionIds;
}
protected loadCollections() {
- if (!this.organization.canManageAllCollections) {
+ if (!this.organization.canViewAllCollections) {
return super.loadCollections();
}
return Promise.resolve(this.collections);
}
protected saveCollections() {
- if (this.organization.canManageAllCollections) {
+ if (this.organization.canEditAnyCollection) {
const request = new CipherCollectionsRequest(this.cipherDomain.collectionIds);
return this.apiService.putCipherCollectionsAdmin(this.cipherId, request);
} else {
diff --git a/src/app/organizations/vault/groupings.component.ts b/src/app/organizations/vault/groupings.component.ts
index 34a0543791e..845b007df60 100644
--- a/src/app/organizations/vault/groupings.component.ts
+++ b/src/app/organizations/vault/groupings.component.ts
@@ -29,7 +29,7 @@ export class GroupingsComponent extends BaseGroupingsComponent {
}
async loadCollections() {
- if (!this.organization.canManageAllCollections) {
+ if (!this.organization.canViewAllCollections) {
await super.loadCollections(this.organization.id);
return;
}
diff --git a/src/app/organizations/vault/vault.component.ts b/src/app/organizations/vault/vault.component.ts
index 4d98e38ae9c..288bb28b165 100644
--- a/src/app/organizations/vault/vault.component.ts
+++ b/src/app/organizations/vault/vault.component.ts
@@ -72,7 +72,7 @@ export class VaultComponent implements OnInit, OnDestroy {
const queryParamsSub = this.route.queryParams.subscribe(async qParams => {
this.ciphersComponent.searchText = this.groupingsComponent.searchText = qParams.search;
- if (!this.organization.canManageAllCollections) {
+ if (!this.organization.canViewAllCollections) {
await this.syncService.fullSync(false);
this.broadcasterService.subscribe(BroadcasterSubscriptionId, (message: any) => {
this.ngZone.run(async () => {
@@ -223,7 +223,7 @@ export class VaultComponent implements OnInit, OnDestroy {
async editCipherCollections(cipher: CipherView) {
const [modal] = await this.modalService.openViewRef(CollectionsComponent, this.collectionsModalRef, comp => {
- if (this.organization.canManageAllCollections) {
+ if (this.organization.canEditAnyCollection) {
comp.collectionIds = cipher.collectionIds;
comp.collections = this.groupingsComponent.collections.filter(c => !c.readOnly);
}
@@ -240,7 +240,7 @@ export class VaultComponent implements OnInit, OnDestroy {
const component = await this.editCipher(null);
component.organizationId = this.organization.id;
component.type = this.type;
- if (this.organization.canManageAllCollections) {
+ if (this.organization.canEditAnyCollection) {
component.collections = this.groupingsComponent.collections.filter(c => !c.readOnly);
}
if (this.collectionId != null) {
@@ -273,7 +273,7 @@ export class VaultComponent implements OnInit, OnDestroy {
const component = await this.editCipher(cipher);
component.cloneMode = true;
component.organizationId = this.organization.id;
- if (this.organization.canManageAllCollections) {
+ if (this.organization.canEditAnyCollection) {
component.collections = this.groupingsComponent.collections.filter(c => !c.readOnly);
}
// Regardless of Admin state, the collection Ids need to passed manually as they are not assigned value
diff --git a/src/app/oss-routing.module.ts b/src/app/oss-routing.module.ts
index 85e47a41e18..487e772bac4 100644
--- a/src/app/oss-routing.module.ts
+++ b/src/app/oss-routing.module.ts
@@ -350,8 +350,11 @@ const routes: Routes = [
canActivate: [OrganizationTypeGuardService],
data: {
permissions: [
- Permissions.ManageAssignedCollections,
- Permissions.ManageAllCollections,
+ Permissions.CreateNewCollections,
+ Permissions.EditAnyCollection,
+ Permissions.DeleteAnyCollection,
+ Permissions.EditAssignedCollections,
+ Permissions.DeleteAssignedCollections,
Permissions.AccessEventLogs,
Permissions.ManageGroups,
Permissions.ManageUsers,
@@ -370,7 +373,13 @@ const routes: Routes = [
canActivate: [OrganizationTypeGuardService],
data: {
titleId: 'collections',
- permissions: [Permissions.ManageAssignedCollections, Permissions.ManageAllCollections],
+ permissions: [
+ Permissions.CreateNewCollections,
+ Permissions.EditAnyCollection,
+ Permissions.DeleteAnyCollection,
+ Permissions.EditAssignedCollections,
+ Permissions.DeleteAssignedCollections,
+ ],
},
},
{
diff --git a/src/app/oss.module.ts b/src/app/oss.module.ts
index a4cf7f3337a..79f13863d61 100644
--- a/src/app/oss.module.ts
+++ b/src/app/oss.module.ts
@@ -12,6 +12,7 @@ import { ToasterModule } from 'angular2-toaster';
import { InfiniteScrollModule } from 'ngx-infinite-scroll';
import { AvatarComponent } from './components/avatar.component';
+import { NestedCheckboxComponent } from './components/nested-checkbox.component';
import { PasswordRepromptComponent } from './components/password-reprompt.component';
import { PasswordStrengthComponent } from './components/password-strength.component';
@@ -356,6 +357,7 @@ registerLocaleData(localeZhTw, 'zh-TW');
LockComponent,
LoginComponent,
NavbarComponent,
+ NestedCheckboxComponent,
OptionsComponent,
OrgAccountComponent,
OrgAddEditComponent,
diff --git a/src/app/services/organization-type-guard.service.ts b/src/app/services/organization-type-guard.service.ts
index 8a2ffbeff8c..eeaf23a45dd 100644
--- a/src/app/services/organization-type-guard.service.ts
+++ b/src/app/services/organization-type-guard.service.ts
@@ -22,8 +22,11 @@ export class OrganizationTypeGuardService implements CanActivate {
(permissions.indexOf(Permissions.AccessEventLogs) !== -1 && org.canAccessEventLogs) ||
(permissions.indexOf(Permissions.AccessImportExport) !== -1 && org.canAccessImportExport) ||
(permissions.indexOf(Permissions.AccessReports) !== -1 && org.canAccessReports) ||
- (permissions.indexOf(Permissions.ManageAllCollections) !== -1 && org.canManageAllCollections) ||
- (permissions.indexOf(Permissions.ManageAssignedCollections) !== -1 && org.canManageAssignedCollections) ||
+ (permissions.indexOf(Permissions.CreateNewCollections) !== -1 && org.canCreateNewCollections) ||
+ (permissions.indexOf(Permissions.EditAnyCollection) !== -1 && org.canEditAnyCollection) ||
+ (permissions.indexOf(Permissions.DeleteAnyCollection) !== -1 && org.canDeleteAnyCollection) ||
+ (permissions.indexOf(Permissions.EditAssignedCollections) !== -1 && org.canEditAssignedCollections) ||
+ (permissions.indexOf(Permissions.DeleteAssignedCollections) !== -1 && org.canDeleteAssignedCollections) ||
(permissions.indexOf(Permissions.ManageGroups) !== -1 && org.canManageGroups) ||
(permissions.indexOf(Permissions.ManageOrganization) !== -1 && org.isOwner) ||
(permissions.indexOf(Permissions.ManagePolicies) !== -1 && org.canManagePolicies) ||
diff --git a/src/app/vault/bulk-delete.component.ts b/src/app/vault/bulk-delete.component.ts
index 1e11f58e28b..403e3144bf3 100644
--- a/src/app/vault/bulk-delete.component.ts
+++ b/src/app/vault/bulk-delete.component.ts
@@ -29,7 +29,7 @@ export class BulkDeleteComponent {
private i18nService: I18nService, private apiService: ApiService) { }
async submit() {
- if (!this.organization || !this.organization.canManageAllCollections) {
+ if (!this.organization || !this.organization.canEditAnyCollection) {
await this.deleteCiphers();
} else {
await this.deleteCiphersAdmin();
diff --git a/src/locales/en/messages.json b/src/locales/en/messages.json
index 2b4a9ae4d44..93fe809e20b 100644
--- a/src/locales/en/messages.json
+++ b/src/locales/en/messages.json
@@ -3824,9 +3824,24 @@
"manageAllCollections": {
"message": "Manage All Collections"
},
+ "createNewCollections": {
+ "message": "Create New Collections"
+ },
+ "editAnyCollection": {
+ "message": "Edit Any Collection"
+ },
+ "deleteAnyCollection": {
+ "message": "Delete Any Collection"
+ },
"manageAssignedCollections": {
"message": "Manage Assigned Collections"
},
+ "editAssignedCollections": {
+ "message": "Edit Assigned Collections"
+ },
+ "deleteAssignedCollections": {
+ "message": "Delete Assigned Collections"
+ },
"manageGroups": {
"message": "Manage Groups"
},
diff --git a/src/scss/forms.scss b/src/scss/forms.scss
index 379efc67c5e..1be32529f48 100644
--- a/src/scss/forms.scss
+++ b/src/scss/forms.scss
@@ -58,6 +58,12 @@ label.form-check-label, .form-control-file {
}
}
+.form-group {
+ .form-group-child-check {
+ @extend .ml-4
+ }
+}
+
.form-inline {
input[type='datetime-local'] {
width: 200px;