diff --git a/src/abstractions/api.service.ts b/src/abstractions/api.service.ts index 6e1a21261c..15c6a51b37 100644 --- a/src/abstractions/api.service.ts +++ b/src/abstractions/api.service.ts @@ -34,6 +34,7 @@ import { UpdateTwoFactorYubioOtpRequest } from '../models/request/updateTwoFacto import { BillingResponse } from '../models/response/billingResponse'; import { CipherResponse } from '../models/response/cipherResponse'; +import { CollectionResponse } from '../models/response/collectionResponse'; import { DomainsResponse } from '../models/response/domainsResponse'; import { FolderResponse } from '../models/response/folderResponse'; import { IdentityTokenResponse } from '../models/response/identityTokenResponse'; @@ -79,6 +80,7 @@ export abstract class ApiService { postFolder: (request: FolderRequest) => Promise; putFolder: (id: string, request: FolderRequest) => Promise; deleteFolder: (id: string) => Promise; + getCiphersOrganization: (organizationId: string) => Promise>; postCipher: (request: CipherRequest) => Promise; putCipher: (id: string, request: CipherRequest) => Promise; deleteCipher: (id: string) => Promise; @@ -94,6 +96,7 @@ export abstract class ApiService { deleteCipherAttachment: (id: string, attachmentId: string) => Promise; postShareCipherAttachment: (id: string, attachmentId: string, data: FormData, organizationId: string) => Promise; + getCollections: (organizationId: string) => Promise>; getSync: () => Promise; postImportDirectory: (organizationId: string, request: ImportDirectoryRequest) => Promise; getSettingsDomains: () => Promise; diff --git a/src/abstractions/cipher.service.ts b/src/abstractions/cipher.service.ts index c1072e484d..5fa873017d 100644 --- a/src/abstractions/cipher.service.ts +++ b/src/abstractions/cipher.service.ts @@ -44,4 +44,5 @@ export abstract class CipherService { deleteAttachmentWithServer: (id: string, attachmentId: string) => Promise; sortCiphersByLastUsed: (a: any, b: any) => number; sortCiphersByLastUsedThenName: (a: any, b: any) => number; + getLocaleSortingFunction: () => (a: CipherView, b: CipherView) => number; } diff --git a/src/abstractions/collection.service.ts b/src/abstractions/collection.service.ts index 6c51478555..7960dfd7bd 100644 --- a/src/abstractions/collection.service.ts +++ b/src/abstractions/collection.service.ts @@ -15,4 +15,5 @@ export abstract class CollectionService { replace: (collections: { [id: string]: CollectionData; }) => Promise; clear: (userId: string) => Promise; delete: (id: string | string[]) => Promise; + getLocaleSortingFunction: () => (a: CollectionView, b: CollectionView) => number; } diff --git a/src/angular/components/ciphers.component.ts b/src/angular/components/ciphers.component.ts index 40f1899346..50f460be43 100644 --- a/src/angular/components/ciphers.component.ts +++ b/src/angular/components/ciphers.component.ts @@ -19,20 +19,15 @@ export class CiphersComponent { ciphers: CipherView[] = []; searchText: string; searchPlaceholder: string = null; - private filter: (cipher: CipherView) => boolean = null; + + protected allCiphers: CipherView[] = []; + protected filter: (cipher: CipherView) => boolean = null; constructor(protected cipherService: CipherService) { } async load(filter: (cipher: CipherView) => boolean = null) { - this.filter = filter; - const ciphers = await this.cipherService.getAllDecrypted(); - - if (this.filter == null) { - this.ciphers = ciphers; - } else { - this.ciphers = ciphers.filter(this.filter); - } - + this.allCiphers = await this.cipherService.getAllDecrypted(); + this.applyFilter(filter); this.loaded = true; } @@ -42,6 +37,15 @@ export class CiphersComponent { await this.load(this.filter); } + async applyFilter(filter: (cipher: CipherView) => boolean = null) { + this.filter = filter; + if (this.filter == null) { + this.ciphers = this.allCiphers; + } else { + this.ciphers = this.allCiphers.filter(this.filter); + } + } + selectCipher(cipher: CipherView) { this.onCipherClicked.emit(cipher); } diff --git a/src/angular/components/groupings.component.ts b/src/angular/components/groupings.component.ts index 0e3a000afe..605ce4bc58 100644 --- a/src/angular/components/groupings.component.ts +++ b/src/angular/components/groupings.component.ts @@ -14,6 +14,10 @@ import { CollectionService } from '../../abstractions/collection.service'; import { FolderService } from '../../abstractions/folder.service'; export class GroupingsComponent { + @Input() showFolders = true; + @Input() showCollections = true; + @Input() showFavorites = true; + @Output() onAllClicked = new EventEmitter(); @Output() onFavoritesClicked = new EventEmitter(); @Output() onCipherTypeClicked = new EventEmitter(); @@ -44,11 +48,22 @@ export class GroupingsComponent { } } - async loadCollections() { - this.collections = await this.collectionService.getAllDecrypted(); + async loadCollections(organizationId?: string) { + if (!this.showCollections) { + return; + } + const collections = await this.collectionService.getAllDecrypted(); + if (organizationId != null) { + this.collections = collections.filter((c) => c.organizationId === organizationId); + } else { + this.collections = collections; + } } async loadFolders() { + if (!this.showFolders) { + return; + } this.folders = await this.folderService.getAllDecrypted(); } diff --git a/src/models/domain/organization.ts b/src/models/domain/organization.ts index 69f93b34c0..ce243e838a 100644 --- a/src/models/domain/organization.ts +++ b/src/models/domain/organization.ts @@ -21,4 +21,15 @@ export class Organization { this.type = obj.type; this.enabled = obj.enabled; } + + get canAccess() { + if (this.type === OrganizationUserType.Owner) { + return true; + } + return this.enabled && this.status === OrganizationUserStatusType.Confirmed; + } + + get isAdmin() { + return this.type === OrganizationUserType.Owner || this.type === OrganizationUserType.Admin; + } } diff --git a/src/models/response/cipherResponse.ts b/src/models/response/cipherResponse.ts index 61c8907310..452a88d1b5 100644 --- a/src/models/response/cipherResponse.ts +++ b/src/models/response/cipherResponse.ts @@ -28,12 +28,12 @@ export class CipherResponse { constructor(response: any) { this.id = response.Id; this.organizationId = response.OrganizationId; - this.folderId = response.FolderId; + this.folderId = response.FolderId || null; this.type = response.Type; this.name = response.Name; this.notes = response.Notes; - this.favorite = response.Favorite; - this.edit = response.Edit; + this.favorite = response.Favorite || false; + this.edit = response.Edit || true; this.organizationUseTotp = response.OrganizationUseTotp; this.revisionDate = new Date(response.RevisionDate); diff --git a/src/services/api.service.ts b/src/services/api.service.ts index 0380749014..d6938f47e7 100644 --- a/src/services/api.service.ts +++ b/src/services/api.service.ts @@ -40,6 +40,7 @@ import { UpdateTwoFactorYubioOtpRequest } from '../models/request/updateTwoFacto import { BillingResponse } from '../models/response/billingResponse'; import { CipherResponse } from '../models/response/cipherResponse'; +import { CollectionResponse } from '../models/response/collectionResponse'; import { DomainsResponse } from '../models/response/domainsResponse'; import { ErrorResponse } from '../models/response/errorResponse'; import { FolderResponse } from '../models/response/folderResponse'; @@ -241,6 +242,12 @@ export class ApiService implements ApiServiceAbstraction { // Cipher APIs + async getCiphersOrganization(organizationId: string): Promise> { + const r = await this.send('GET', '/ciphers/organization-details?organizationId=' + organizationId, + null, true, true); + return new ListResponse(r, CipherResponse); + } + async postCipher(request: CipherRequest): Promise { const r = await this.send('POST', '/ciphers', request, true, true); return new CipherResponse(r); @@ -304,6 +311,13 @@ export class ApiService implements ApiServiceAbstraction { attachmentId + '/share?organizationId=' + organizationId, data, true, false); } + // Collections APIs + + async getCollections(organizationId: string): Promise> { + const r = await this.send('GET', '/organizations/' + organizationId + '/collections', null, true, true); + return new ListResponse(r, CollectionResponse); + } + // Sync APIs async getSync(): Promise { diff --git a/src/services/cipher.service.ts b/src/services/cipher.service.ts index e1d6152bf3..6215fbd2fa 100644 --- a/src/services/cipher.service.ts +++ b/src/services/cipher.service.ts @@ -619,9 +619,7 @@ export class CipherService implements CipherServiceAbstraction { return this.getLocaleSortingFunction()(a, b); } - // Helpers - - private getLocaleSortingFunction(): (a: CipherView, b: CipherView) => number { + getLocaleSortingFunction(): (a: CipherView, b: CipherView) => number { return (a, b) => { let aName = a.name; let bName = b.name; @@ -656,6 +654,8 @@ export class CipherService implements CipherServiceAbstraction { }; } + // Helpers + private async encryptObjProperty(model: V, obj: D, map: any, key: SymmetricCryptoKey): Promise { const promises = []; diff --git a/src/services/collection.service.ts b/src/services/collection.service.ts index 02821bcba9..c76fbb6299 100644 --- a/src/services/collection.service.ts +++ b/src/services/collection.service.ts @@ -125,7 +125,7 @@ export class CollectionService implements CollectionServiceAbstraction { this.decryptedCollectionCache = null; } - private getLocaleSortingFunction(): (a: CollectionView, b: CollectionView) => number { + getLocaleSortingFunction(): (a: CollectionView, b: CollectionView) => number { return (a, b) => { if (a.name == null && b.name != null) { return -1;