From 1d60e881ee534356ebddf19be7ba738f58343271 Mon Sep 17 00:00:00 2001 From: Kyle Spearrin Date: Thu, 5 Jul 2018 10:48:51 -0400 Subject: [PATCH] admin cipher attachments --- jslib | 2 +- src/app/app.module.ts | 3 ++ src/app/organizations/add-edit.component.ts | 27 +++++----- .../organizations/attachments.component.ts | 52 +++++++++++++++++++ src/app/organizations/ciphers.component.ts | 38 +++++++------- src/app/organizations/groupings.component.ts | 48 ++++++++--------- src/app/organizations/vault.component.ts | 26 ++++++++++ src/scss/styles.scss | 7 +++ 8 files changed, 144 insertions(+), 59 deletions(-) create mode 100644 src/app/organizations/attachments.component.ts diff --git a/jslib b/jslib index ef5eebba66..47ab71e730 160000 --- a/jslib +++ b/jslib @@ -1 +1 @@ -Subproject commit ef5eebba66d606d53cf4bc730426ea393276e801 +Subproject commit 47ab71e73098d1b83a1bdcbae3cd3e2b1d9ccacc diff --git a/src/app/app.module.ts b/src/app/app.module.ts index 17d57eb354..bc64db0f00 100644 --- a/src/app/app.module.ts +++ b/src/app/app.module.ts @@ -33,6 +33,7 @@ import { TwoFactorOptionsComponent } from './accounts/two-factor-options.compone import { TwoFactorComponent } from './accounts/two-factor.component'; import { AddEditComponent as OrgAddEditComponent } from './organizations/add-edit.component'; +import { AttachmentsComponent as OrgAttachmentsComponent } from './organizations/attachments.component'; import { CiphersComponent as OrgCiphersComponent } from './organizations/ciphers.component'; import { GroupingsComponent as OrgGroupingsComponent } from './organizations/groupings.component'; import { VaultComponent as OrgVaultComponent } from './organizations/vault.component'; @@ -154,6 +155,7 @@ import { SearchCiphersPipe } from 'jslib/angular/pipes/search-ciphers.pipe'; NavbarComponent, OptionsComponent, OrgAddEditComponent, + OrgAttachmentsComponent, OrgCiphersComponent, OrgGroupingsComponent, OrganizationsComponent, @@ -200,6 +202,7 @@ import { SearchCiphersPipe } from 'jslib/angular/pipes/search-ciphers.pipe'; FolderAddEditComponent, ModalComponent, OrgAddEditComponent, + OrgAttachmentsComponent, PasswordGeneratorHistoryComponent, PurgeVaultComponent, ShareComponent, diff --git a/src/app/organizations/add-edit.component.ts b/src/app/organizations/add-edit.component.ts index 66e8341367..5f25274442 100644 --- a/src/app/organizations/add-edit.component.ts +++ b/src/app/organizations/add-edit.component.ts @@ -42,12 +42,11 @@ export class AddEditComponent extends BaseAddEditComponent implements OnInit { } protected async loadCipher() { - if (this.organization.isAdmin) { - const response = await this.apiService.getCipherAdmin(this.cipherId); - return new Cipher(new CipherData(response)); - } else { + if (!this.organization.isAdmin) { return await super.loadCipher(); } + const response = await this.apiService.getCipherAdmin(this.cipherId); + return new Cipher(new CipherData(response)); } protected encryptCipher() { @@ -58,23 +57,21 @@ export class AddEditComponent extends BaseAddEditComponent implements OnInit { } protected async saveCipher(cipher: Cipher) { - if (this.organization.isAdmin) { - const request = new CipherRequest(cipher); - if (this.editMode) { - return this.apiService.putCipherAdmin(this.cipherId, request); - } else { - return this.apiService.postCipherAdmin(request); - } - } else { + if (!this.organization.isAdmin) { return super.saveCipher(cipher); } + const request = new CipherRequest(cipher); + if (this.editMode) { + return this.apiService.putCipherAdmin(this.cipherId, request); + } else { + return this.apiService.postCipherAdmin(request); + } } protected async deleteCipher() { - if (this.organization.isAdmin) { - return this.apiService.deleteCipherAdmin(this.cipherId); - } else { + if (!this.organization.isAdmin) { return super.deleteCipher(); } + return this.apiService.deleteCipherAdmin(this.cipherId); } } diff --git a/src/app/organizations/attachments.component.ts b/src/app/organizations/attachments.component.ts new file mode 100644 index 0000000000..76e3c88224 --- /dev/null +++ b/src/app/organizations/attachments.component.ts @@ -0,0 +1,52 @@ +import { Component } from '@angular/core'; + +import { ToasterService } from 'angular2-toaster'; +import { Angulartics2 } from 'angulartics2'; + +import { ApiService } from 'jslib/abstractions/api.service'; +import { CipherService } from 'jslib/abstractions/cipher.service'; +import { CryptoService } from 'jslib/abstractions/crypto.service'; +import { I18nService } from 'jslib/abstractions/i18n.service'; +import { PlatformUtilsService } from 'jslib/abstractions/platformUtils.service'; +import { TokenService } from 'jslib/abstractions/token.service'; + +import { CipherData } from 'jslib/models/data/cipherData'; +import { Cipher } from 'jslib/models/domain/cipher'; +import { Organization } from 'jslib/models/domain/organization'; + +import { AttachmentsComponent as BaseAttachmentsComponent } from '../vault/attachments.component'; + +@Component({ + selector: 'app-org-vault-attachments', + templateUrl: '../vault/attachments.component.html', +}) +export class AttachmentsComponent extends BaseAttachmentsComponent { + organization: Organization; + + constructor(cipherService: CipherService, analytics: Angulartics2, + toasterService: ToasterService, i18nService: I18nService, + cryptoService: CryptoService, tokenService: TokenService, + platformUtilsService: PlatformUtilsService, private apiService: ApiService) { + super(cipherService, analytics, toasterService, i18nService, cryptoService, tokenService, + platformUtilsService); + } + + protected async loadCipher() { + if (!this.organization.isAdmin) { + return await super.loadCipher(); + } + const response = await this.apiService.getCipherAdmin(this.cipherId); + return new Cipher(new CipherData(response)); + } + + protected saveCipherAttachment(file: File) { + return this.cipherService.saveAttachmentWithServer(this.cipherDomain, file, this.organization.isAdmin); + } + + protected deleteCipherAttachment(attachmentId: string) { + if (!this.organization.isAdmin) { + return super.deleteCipherAttachment(attachmentId); + } + return this.apiService.deleteCipherAttachmentAdmin(this.cipherId, attachmentId); + } +} diff --git a/src/app/organizations/ciphers.component.ts b/src/app/organizations/ciphers.component.ts index a2d9e31e1a..690bd76ab1 100644 --- a/src/app/organizations/ciphers.component.ts +++ b/src/app/organizations/ciphers.component.ts @@ -29,27 +29,27 @@ export class CiphersComponent extends BaseCiphersComponent { } async load(filter: (cipher: CipherView) => boolean = null) { - if (this.organization.isAdmin) { - const ciphers = await this.apiService.getCiphersOrganization(this.organization.id); - if (ciphers != null && ciphers.data != null && ciphers.data.length) { - const decCiphers: CipherView[] = []; - const promises: any[] = []; - ciphers.data.forEach((r) => { - const data = new CipherData(r); - const cipher = new Cipher(data); - promises.push(cipher.decrypt().then((c) => decCiphers.push(c))); - }); - await Promise.all(promises); - decCiphers.sort(this.cipherService.getLocaleSortingFunction()); - this.allCiphers = decCiphers; - } else { - this.allCiphers = []; - } - this.applyFilter(filter); - this.loaded = true; - } else { + if (!this.organization.isAdmin) { await super.load(); + return; } + const ciphers = await this.apiService.getCiphersOrganization(this.organization.id); + if (ciphers != null && ciphers.data != null && ciphers.data.length) { + const decCiphers: CipherView[] = []; + const promises: any[] = []; + ciphers.data.forEach((r) => { + const data = new CipherData(r); + const cipher = new Cipher(data); + promises.push(cipher.decrypt().then((c) => decCiphers.push(c))); + }); + await Promise.all(promises); + decCiphers.sort(this.cipherService.getLocaleSortingFunction()); + this.allCiphers = decCiphers; + } else { + this.allCiphers = []; + } + this.applyFilter(filter); + this.loaded = true; } applyFilter(filter: (cipher: CipherView) => boolean = null) { diff --git a/src/app/organizations/groupings.component.ts b/src/app/organizations/groupings.component.ts index 0f17ca6348..e2c3732dc2 100644 --- a/src/app/organizations/groupings.component.ts +++ b/src/app/organizations/groupings.component.ts @@ -25,31 +25,31 @@ export class GroupingsComponent extends BaseGroupingsComponent { } async loadCollections() { - if (this.organization.isAdmin) { - const collections = await this.apiService.getCollections(this.organization.id); - if (collections != null && collections.data != null && collections.data.length) { - const decCollections: CollectionView[] = []; - const promises: any[] = []; - collections.data.forEach((r) => { - const data = new CollectionData(r); - const collection = new Collection(data); - promises.push(collection.decrypt().then((c) => decCollections.push(c))); - }); - await Promise.all(promises); - decCollections.sort(this.collectionService.getLocaleSortingFunction()); - this.collections = decCollections; - } else { - this.collections = []; - } - - const unassignedCollection = new CollectionView(); - unassignedCollection.name = this.i18nService.t('unassigned'); - unassignedCollection.id = 'unassigned'; - unassignedCollection.organizationId = this.organization.id; - unassignedCollection.readOnly = true; - this.collections.push(unassignedCollection); - } else { + if (!this.organization.isAdmin) { await super.loadCollections(this.organization.id); + return; } + const collections = await this.apiService.getCollections(this.organization.id); + if (collections != null && collections.data != null && collections.data.length) { + const decCollections: CollectionView[] = []; + const promises: any[] = []; + collections.data.forEach((r) => { + const data = new CollectionData(r); + const collection = new Collection(data); + promises.push(collection.decrypt().then((c) => decCollections.push(c))); + }); + await Promise.all(promises); + decCollections.sort(this.collectionService.getLocaleSortingFunction()); + this.collections = decCollections; + } else { + this.collections = []; + } + + const unassignedCollection = new CollectionView(); + unassignedCollection.name = this.i18nService.t('unassigned'); + unassignedCollection.id = 'unassigned'; + unassignedCollection.organizationId = this.organization.id; + unassignedCollection.readOnly = true; + this.collections.push(unassignedCollection); } } diff --git a/src/app/organizations/vault.component.ts b/src/app/organizations/vault.component.ts index 6efafb8081..8ec55c5fd4 100644 --- a/src/app/organizations/vault.component.ts +++ b/src/app/organizations/vault.component.ts @@ -23,6 +23,7 @@ import { CipherType } from 'jslib/enums/cipherType'; import { ModalComponent } from '../modal.component'; import { AddEditComponent } from './add-edit.component'; +import { AttachmentsComponent } from './attachments.component'; import { CiphersComponent } from './ciphers.component'; import { GroupingsComponent } from './groupings.component'; @@ -33,6 +34,7 @@ import { GroupingsComponent } from './groupings.component'; export class VaultComponent implements OnInit { @ViewChild(GroupingsComponent) groupingsComponent: GroupingsComponent; @ViewChild(CiphersComponent) ciphersComponent: CiphersComponent; + @ViewChild('attachments', { read: ViewContainerRef }) attachmentsModalRef: ViewContainerRef; @ViewChild('cipherAddEdit', { read: ViewContainerRef }) cipherAddEditModalRef: ViewContainerRef; organization: Organization; @@ -125,6 +127,30 @@ export class VaultComponent implements OnInit { this.ciphersComponent.searchText = searchText; } + editCipherAttachments(cipher: CipherView) { + if (this.modal != null) { + this.modal.close(); + } + + const factory = this.componentFactoryResolver.resolveComponentFactory(ModalComponent); + this.modal = this.attachmentsModalRef.createComponent(factory).instance; + const childComponent = this.modal.show(AttachmentsComponent, this.attachmentsModalRef); + + childComponent.organization = this.organization; + childComponent.cipherId = cipher.id; + let madeAttachmentChanges = false; + childComponent.onUploadedAttachment.subscribe(() => madeAttachmentChanges = true); + childComponent.onDeletedAttachment.subscribe(() => madeAttachmentChanges = true); + + this.modal.onClosed.subscribe(async () => { + this.modal = null; + if (madeAttachmentChanges) { + await this.ciphersComponent.refresh(); + } + madeAttachmentChanges = false; + }); + } + addCipher() { const component = this.editCipher(null); component.type = this.type; diff --git a/src/scss/styles.scss b/src/scss/styles.scss index 76d0c90f0f..7fef3c0b43 100644 --- a/src/scss/styles.scss +++ b/src/scss/styles.scss @@ -86,6 +86,12 @@ body { } } +h1, h2, h3, h4, h5 { + small { + font-size: 80%; + } +} + .secondary-header, .spaced-header { margin-top: 4rem; } @@ -130,6 +136,7 @@ body { small { font-weight: normal; text-transform: none; + @extend .text-muted; } }