api support for sharing
This commit is contained in:
parent
5db55496c2
commit
b3f71ed8e4
|
@ -1,6 +1,7 @@
|
||||||
import { EnvironmentUrls } from '../models/domain/environmentUrls';
|
import { EnvironmentUrls } from '../models/domain/environmentUrls';
|
||||||
|
|
||||||
import { CipherRequest } from '../models/request/cipherRequest';
|
import { CipherRequest } from '../models/request/cipherRequest';
|
||||||
|
import { CipherShareRequest } from '../models/request/cipherShareRequest';
|
||||||
import { FolderRequest } from '../models/request/folderRequest';
|
import { FolderRequest } from '../models/request/folderRequest';
|
||||||
import { ImportDirectoryRequest } from '../models/request/importDirectoryRequest';
|
import { ImportDirectoryRequest } from '../models/request/importDirectoryRequest';
|
||||||
import { PasswordHintRequest } from '../models/request/passwordHintRequest';
|
import { PasswordHintRequest } from '../models/request/passwordHintRequest';
|
||||||
|
@ -15,6 +16,8 @@ import { IdentityTwoFactorResponse } from '../models/response/identityTwoFactorR
|
||||||
import { ProfileResponse } from '../models/response/profileResponse';
|
import { ProfileResponse } from '../models/response/profileResponse';
|
||||||
import { SyncResponse } from '../models/response/syncResponse';
|
import { SyncResponse } from '../models/response/syncResponse';
|
||||||
|
|
||||||
|
import { AttachmentView } from '../models/view/attachmentView';
|
||||||
|
|
||||||
export abstract class ApiService {
|
export abstract class ApiService {
|
||||||
urlsSet: boolean;
|
urlsSet: boolean;
|
||||||
baseUrl: string;
|
baseUrl: string;
|
||||||
|
@ -34,8 +37,10 @@ export abstract class ApiService {
|
||||||
deleteFolder: (id: string) => Promise<any>;
|
deleteFolder: (id: string) => Promise<any>;
|
||||||
postCipher: (request: CipherRequest) => Promise<CipherResponse>;
|
postCipher: (request: CipherRequest) => Promise<CipherResponse>;
|
||||||
putCipher: (id: string, request: CipherRequest) => Promise<CipherResponse>;
|
putCipher: (id: string, request: CipherRequest) => Promise<CipherResponse>;
|
||||||
|
shareCipher: (id: string, request: CipherShareRequest) => Promise<any>;
|
||||||
deleteCipher: (id: string) => Promise<any>;
|
deleteCipher: (id: string) => Promise<any>;
|
||||||
postCipherAttachment: (id: string, data: FormData) => Promise<CipherResponse>;
|
postCipherAttachment: (id: string, data: FormData) => Promise<CipherResponse>;
|
||||||
|
shareCipherAttachment: (id: string, attachmentId: string, data: FormData, organizationId: string) => Promise<any>;
|
||||||
deleteCipherAttachment: (id: string, attachmentId: string) => Promise<any>;
|
deleteCipherAttachment: (id: string, attachmentId: string) => Promise<any>;
|
||||||
getSync: () => Promise<SyncResponse>;
|
getSync: () => Promise<SyncResponse>;
|
||||||
postImportDirectory: (organizationId: string, request: ImportDirectoryRequest) => Promise<any>;
|
postImportDirectory: (organizationId: string, request: ImportDirectoryRequest) => Promise<any>;
|
||||||
|
|
|
@ -6,6 +6,7 @@ import { Cipher } from '../models/domain/cipher';
|
||||||
import { Field } from '../models/domain/field';
|
import { Field } from '../models/domain/field';
|
||||||
import { SymmetricCryptoKey } from '../models/domain/symmetricCryptoKey';
|
import { SymmetricCryptoKey } from '../models/domain/symmetricCryptoKey';
|
||||||
|
|
||||||
|
import { AttachmentView } from '../models/view/attachmentView';
|
||||||
import { CipherView } from '../models/view/cipherView';
|
import { CipherView } from '../models/view/cipherView';
|
||||||
import { FieldView } from '../models/view/fieldView';
|
import { FieldView } from '../models/view/fieldView';
|
||||||
|
|
||||||
|
@ -25,6 +26,9 @@ export abstract class CipherService {
|
||||||
updateLastUsedDate: (id: string) => Promise<void>;
|
updateLastUsedDate: (id: string) => Promise<void>;
|
||||||
saveNeverDomain: (domain: string) => Promise<void>;
|
saveNeverDomain: (domain: string) => Promise<void>;
|
||||||
saveWithServer: (cipher: Cipher) => Promise<any>;
|
saveWithServer: (cipher: Cipher) => Promise<any>;
|
||||||
|
shareWithServer: (cipher: Cipher) => Promise<any>;
|
||||||
|
shareAttachmentWithServer: (attachmentView: AttachmentView, cipherId: string,
|
||||||
|
organizationId: string) => Promise<any>;
|
||||||
saveAttachmentWithServer: (cipher: Cipher, unencryptedFile: any) => Promise<Cipher>;
|
saveAttachmentWithServer: (cipher: Cipher, unencryptedFile: any) => Promise<Cipher>;
|
||||||
saveAttachmentRawWithServer: (cipher: Cipher, filename: string, data: ArrayBuffer) => Promise<Cipher>;
|
saveAttachmentRawWithServer: (cipher: Cipher, filename: string, data: ArrayBuffer) => Promise<Cipher>;
|
||||||
upsert: (cipher: CipherData | CipherData[]) => Promise<any>;
|
upsert: (cipher: CipherData | CipherData[]) => Promise<any>;
|
||||||
|
|
|
@ -7,7 +7,10 @@ export class AttachmentData {
|
||||||
size: number;
|
size: number;
|
||||||
sizeName: string;
|
sizeName: string;
|
||||||
|
|
||||||
constructor(response: AttachmentResponse) {
|
constructor(response?: AttachmentResponse) {
|
||||||
|
if (response == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.id = response.id;
|
this.id = response.id;
|
||||||
this.url = response.url;
|
this.url = response.url;
|
||||||
this.fileName = response.fileName;
|
this.fileName = response.fileName;
|
||||||
|
|
|
@ -8,7 +8,11 @@ export class CardData {
|
||||||
expYear: string;
|
expYear: string;
|
||||||
code: string;
|
code: string;
|
||||||
|
|
||||||
constructor(data: CardApi) {
|
constructor(data?: CardApi) {
|
||||||
|
if (data == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.cardholderName = data.cardholderName;
|
this.cardholderName = data.cardholderName;
|
||||||
this.brand = data.brand;
|
this.brand = data.brand;
|
||||||
this.number = data.number;
|
this.number = data.number;
|
||||||
|
|
|
@ -17,7 +17,7 @@ export class CipherData {
|
||||||
edit: boolean;
|
edit: boolean;
|
||||||
organizationUseTotp: boolean;
|
organizationUseTotp: boolean;
|
||||||
favorite: boolean;
|
favorite: boolean;
|
||||||
revisionDate: string;
|
revisionDate: Date;
|
||||||
type: CipherType;
|
type: CipherType;
|
||||||
sizeName: string;
|
sizeName: string;
|
||||||
name: string;
|
name: string;
|
||||||
|
@ -30,7 +30,11 @@ export class CipherData {
|
||||||
attachments?: AttachmentData[];
|
attachments?: AttachmentData[];
|
||||||
collectionIds?: string[];
|
collectionIds?: string[];
|
||||||
|
|
||||||
constructor(response: CipherResponse, userId: string, collectionIds?: string[]) {
|
constructor(response?: CipherResponse, userId?: string, collectionIds?: string[]) {
|
||||||
|
if (response == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.id = response.id;
|
this.id = response.id;
|
||||||
this.organizationId = response.organizationId;
|
this.organizationId = response.organizationId;
|
||||||
this.folderId = response.folderId;
|
this.folderId = response.folderId;
|
||||||
|
|
|
@ -7,7 +7,10 @@ export class FieldData {
|
||||||
name: string;
|
name: string;
|
||||||
value: string;
|
value: string;
|
||||||
|
|
||||||
constructor(response: FieldApi) {
|
constructor(response?: FieldApi) {
|
||||||
|
if (response == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.type = response.type;
|
this.type = response.type;
|
||||||
this.name = response.name;
|
this.name = response.name;
|
||||||
this.value = response.value;
|
this.value = response.value;
|
||||||
|
|
|
@ -20,7 +20,11 @@ export class IdentityData {
|
||||||
passportNumber: string;
|
passportNumber: string;
|
||||||
licenseNumber: string;
|
licenseNumber: string;
|
||||||
|
|
||||||
constructor(data: IdentityApi) {
|
constructor(data?: IdentityApi) {
|
||||||
|
if (data == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.title = data.title;
|
this.title = data.title;
|
||||||
this.firstName = data.firstName;
|
this.firstName = data.firstName;
|
||||||
this.middleName = data.middleName;
|
this.middleName = data.middleName;
|
||||||
|
|
|
@ -9,7 +9,11 @@ export class LoginData {
|
||||||
password: string;
|
password: string;
|
||||||
totp: string;
|
totp: string;
|
||||||
|
|
||||||
constructor(data: LoginApi) {
|
constructor(data?: LoginApi) {
|
||||||
|
if (data == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.username = data.username;
|
this.username = data.username;
|
||||||
this.password = data.password;
|
this.password = data.password;
|
||||||
this.totp = data.totp;
|
this.totp = data.totp;
|
||||||
|
|
|
@ -6,7 +6,10 @@ export class LoginUriData {
|
||||||
uri: string;
|
uri: string;
|
||||||
match: UriMatchType = null;
|
match: UriMatchType = null;
|
||||||
|
|
||||||
constructor(data: LoginUriApi) {
|
constructor(data?: LoginUriApi) {
|
||||||
|
if (data == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.uri = data.uri;
|
this.uri = data.uri;
|
||||||
this.match = data.match;
|
this.match = data.match;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,11 @@ import { SecureNoteApi } from '../api/secureNoteApi';
|
||||||
export class SecureNoteData {
|
export class SecureNoteData {
|
||||||
type: SecureNoteType;
|
type: SecureNoteType;
|
||||||
|
|
||||||
constructor(data: SecureNoteApi) {
|
constructor(data?: SecureNoteApi) {
|
||||||
|
if (data == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.type = data.type;
|
this.type = data.type;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -32,4 +32,15 @@ export class Attachment extends Domain {
|
||||||
fileName: null,
|
fileName: null,
|
||||||
}, orgId);
|
}, orgId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toAttachmentData(): AttachmentData {
|
||||||
|
const a = new AttachmentData();
|
||||||
|
this.buildDataModel(this, a, {
|
||||||
|
id: null,
|
||||||
|
url: null,
|
||||||
|
sizeName: null,
|
||||||
|
fileName: null,
|
||||||
|
}, ['id', 'url', 'sizeName']);
|
||||||
|
return a;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,4 +39,17 @@ export class Card extends Domain {
|
||||||
code: null,
|
code: null,
|
||||||
}, orgId);
|
}, orgId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toCardData(): CardData {
|
||||||
|
const c = new CardData();
|
||||||
|
this.buildDataModel(this, c, {
|
||||||
|
cardholderName: null,
|
||||||
|
brand: null,
|
||||||
|
number: null,
|
||||||
|
expMonth: null,
|
||||||
|
expYear: null,
|
||||||
|
code: null,
|
||||||
|
});
|
||||||
|
return c;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@ export class Cipher extends Domain {
|
||||||
favorite: boolean;
|
favorite: boolean;
|
||||||
organizationUseTotp: boolean;
|
organizationUseTotp: boolean;
|
||||||
edit: boolean;
|
edit: boolean;
|
||||||
|
revisionDate: Date;
|
||||||
localData: any;
|
localData: any;
|
||||||
login: Login;
|
login: Login;
|
||||||
identity: Identity;
|
identity: Identity;
|
||||||
|
@ -40,16 +41,18 @@ export class Cipher extends Domain {
|
||||||
|
|
||||||
this.buildDomainModel(this, obj, {
|
this.buildDomainModel(this, obj, {
|
||||||
id: null,
|
id: null,
|
||||||
|
userId: null,
|
||||||
organizationId: null,
|
organizationId: null,
|
||||||
folderId: null,
|
folderId: null,
|
||||||
name: null,
|
name: null,
|
||||||
notes: null,
|
notes: null,
|
||||||
}, alreadyEncrypted, ['id', 'organizationId', 'folderId']);
|
}, alreadyEncrypted, ['id', 'userId', 'organizationId', 'folderId']);
|
||||||
|
|
||||||
this.type = obj.type;
|
this.type = obj.type;
|
||||||
this.favorite = obj.favorite;
|
this.favorite = obj.favorite;
|
||||||
this.organizationUseTotp = obj.organizationUseTotp;
|
this.organizationUseTotp = obj.organizationUseTotp;
|
||||||
this.edit = obj.edit;
|
this.edit = obj.edit;
|
||||||
|
this.revisionDate = obj.revisionDate;
|
||||||
this.collectionIds = obj.collectionIds;
|
this.collectionIds = obj.collectionIds;
|
||||||
this.localData = localData;
|
this.localData = localData;
|
||||||
|
|
||||||
|
@ -142,4 +145,55 @@ export class Cipher extends Domain {
|
||||||
|
|
||||||
return model;
|
return model;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toCipherData(userId: string): CipherData {
|
||||||
|
const c = new CipherData();
|
||||||
|
c.id = this.id;
|
||||||
|
c.organizationId = this.organizationId;
|
||||||
|
c.folderId = this.folderId;
|
||||||
|
c.userId = this.organizationId != null ? userId : null;
|
||||||
|
c.edit = this.edit;
|
||||||
|
c.organizationUseTotp = this.organizationUseTotp;
|
||||||
|
c.favorite = this.favorite;
|
||||||
|
c.revisionDate = this.revisionDate;
|
||||||
|
c.type = this.type;
|
||||||
|
c.collectionIds = this.collectionIds;
|
||||||
|
|
||||||
|
this.buildDataModel(this, c, {
|
||||||
|
name: null,
|
||||||
|
notes: null,
|
||||||
|
});
|
||||||
|
|
||||||
|
switch (c.type) {
|
||||||
|
case CipherType.Login:
|
||||||
|
c.login = this.login.toLoginData();
|
||||||
|
break;
|
||||||
|
case CipherType.SecureNote:
|
||||||
|
c.secureNote = this.secureNote.toSecureNoteData();
|
||||||
|
break;
|
||||||
|
case CipherType.Card:
|
||||||
|
c.card = this.card.toCardData();
|
||||||
|
break;
|
||||||
|
case CipherType.Identity:
|
||||||
|
c.identity = this.identity.toIdentityData();
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.fields != null) {
|
||||||
|
c.fields = [];
|
||||||
|
this.fields.forEach((field) => {
|
||||||
|
c.fields.push(field.toFieldData());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.attachments != null) {
|
||||||
|
c.attachments = [];
|
||||||
|
this.attachments.forEach((attachment) => {
|
||||||
|
c.attachments.push(attachment.toAttachmentData());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return c;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,20 @@ export default abstract class Domain {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
protected buildDataModel<D extends Domain>(domain: D, dataObj: any, map: any, notCipherStringList: any[] = []) {
|
||||||
|
for (const prop in map) {
|
||||||
|
if (!map.hasOwnProperty(prop)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
const objProp = (domain as any)[(map[prop] || prop)];
|
||||||
|
if (notCipherStringList.indexOf(prop) > -1) {
|
||||||
|
(dataObj as any)[prop] = objProp != null ? objProp : null;
|
||||||
|
} else {
|
||||||
|
(dataObj as any)[prop] = objProp != null ? (objProp as CipherString).encryptedString : null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected async decryptObj<T extends View>(viewModel: T, map: any, orgId: string): Promise<T> {
|
protected async decryptObj<T extends View>(viewModel: T, map: any, orgId: string): Promise<T> {
|
||||||
const promises = [];
|
const promises = [];
|
||||||
|
|
|
@ -31,4 +31,14 @@ export class Field extends Domain {
|
||||||
value: null,
|
value: null,
|
||||||
}, orgId);
|
}, orgId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toFieldData(): FieldData {
|
||||||
|
const f = new FieldData();
|
||||||
|
this.buildDataModel(this, f, {
|
||||||
|
name: null,
|
||||||
|
value: null,
|
||||||
|
type: null,
|
||||||
|
}, ['type']);
|
||||||
|
return f;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,4 +75,29 @@ export class Identity extends Domain {
|
||||||
licenseNumber: null,
|
licenseNumber: null,
|
||||||
}, orgId);
|
}, orgId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toIdentityData(): IdentityData {
|
||||||
|
const i = new IdentityData();
|
||||||
|
this.buildDataModel(this, i, {
|
||||||
|
title: null,
|
||||||
|
firstName: null,
|
||||||
|
middleName: null,
|
||||||
|
lastName: null,
|
||||||
|
address1: null,
|
||||||
|
address2: null,
|
||||||
|
address3: null,
|
||||||
|
city: null,
|
||||||
|
state: null,
|
||||||
|
postalCode: null,
|
||||||
|
country: null,
|
||||||
|
company: null,
|
||||||
|
email: null,
|
||||||
|
phone: null,
|
||||||
|
ssn: null,
|
||||||
|
username: null,
|
||||||
|
passportNumber: null,
|
||||||
|
licenseNumber: null,
|
||||||
|
});
|
||||||
|
return i;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,4 +51,22 @@ export class Login extends Domain {
|
||||||
|
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toLoginData(): LoginData {
|
||||||
|
const l = new LoginData();
|
||||||
|
this.buildDataModel(this, l, {
|
||||||
|
username: null,
|
||||||
|
password: null,
|
||||||
|
totp: null,
|
||||||
|
});
|
||||||
|
|
||||||
|
if (this.uris != null && this.uris.length > 0) {
|
||||||
|
l.uris = [];
|
||||||
|
this.uris.forEach((u) => {
|
||||||
|
l.uris.push(u.toLoginUriData());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return l;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -28,4 +28,12 @@ export class LoginUri extends Domain {
|
||||||
uri: null,
|
uri: null,
|
||||||
}, orgId);
|
}, orgId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toLoginUriData(): LoginUriData {
|
||||||
|
const u = new LoginUriData();
|
||||||
|
this.buildDataModel(this, u, {
|
||||||
|
uri: null,
|
||||||
|
}, ['match']);
|
||||||
|
return u;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,4 +21,10 @@ export class SecureNote extends Domain {
|
||||||
decrypt(orgId: string): Promise<SecureNoteView> {
|
decrypt(orgId: string): Promise<SecureNoteView> {
|
||||||
return Promise.resolve(new SecureNoteView(this));
|
return Promise.resolve(new SecureNoteView(this));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
toSecureNoteData(): SecureNoteData {
|
||||||
|
const n = new SecureNoteData();
|
||||||
|
n.type = this.type;
|
||||||
|
return n;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
export class CipherBulkDeleteRequest {
|
||||||
|
ids: string[];
|
||||||
|
|
||||||
|
constructor(ids: string[]) {
|
||||||
|
this.ids = ids;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
export class CipherBulkMoveRequest {
|
||||||
|
ids: string[];
|
||||||
|
folderId: string;
|
||||||
|
|
||||||
|
constructor(ids: string[], folderId: string) {
|
||||||
|
this.ids = ids;
|
||||||
|
this.folderId = folderId;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
export class CipherCollectionsRequest {
|
||||||
|
collectionIds: string[];
|
||||||
|
|
||||||
|
constructor(collectionIds: string[]) {
|
||||||
|
this.collectionIds = collectionIds;
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,6 +21,7 @@ export class CipherRequest {
|
||||||
card: CardApi;
|
card: CardApi;
|
||||||
identity: IdentityApi;
|
identity: IdentityApi;
|
||||||
fields: FieldApi[];
|
fields: FieldApi[];
|
||||||
|
attachments: { [id: string]: string; };
|
||||||
|
|
||||||
constructor(cipher: Cipher) {
|
constructor(cipher: Cipher) {
|
||||||
this.type = cipher.type;
|
this.type = cipher.type;
|
||||||
|
@ -101,5 +102,12 @@ export class CipherRequest {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (cipher.attachments) {
|
||||||
|
this.attachments = {};
|
||||||
|
cipher.attachments.forEach((attachment) => {
|
||||||
|
this.attachments[attachment.id] = attachment.fileName ? attachment.fileName.encryptedString : null;
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
import { CipherRequest } from './cipherRequest';
|
||||||
|
|
||||||
|
import { Cipher } from '../domain/cipher';
|
||||||
|
|
||||||
|
export class CipherShareRequest {
|
||||||
|
cipher: CipherRequest;
|
||||||
|
collectionIds: string[];
|
||||||
|
|
||||||
|
constructor(cipher: Cipher) {
|
||||||
|
this.cipher = new CipherRequest(cipher);
|
||||||
|
this.collectionIds = cipher.collectionIds;
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,7 +21,7 @@ export class CipherResponse {
|
||||||
favorite: boolean;
|
favorite: boolean;
|
||||||
edit: boolean;
|
edit: boolean;
|
||||||
organizationUseTotp: boolean;
|
organizationUseTotp: boolean;
|
||||||
revisionDate: string;
|
revisionDate: Date;
|
||||||
attachments: AttachmentResponse[];
|
attachments: AttachmentResponse[];
|
||||||
collectionIds: string[];
|
collectionIds: string[];
|
||||||
|
|
||||||
|
@ -35,7 +35,7 @@ export class CipherResponse {
|
||||||
this.favorite = response.Favorite;
|
this.favorite = response.Favorite;
|
||||||
this.edit = response.Edit;
|
this.edit = response.Edit;
|
||||||
this.organizationUseTotp = response.OrganizationUseTotp;
|
this.organizationUseTotp = response.OrganizationUseTotp;
|
||||||
this.revisionDate = response.RevisionDate;
|
this.revisionDate = new Date(response.RevisionDate);
|
||||||
|
|
||||||
if (response.Login != null) {
|
if (response.Login != null) {
|
||||||
this.login = new LoginApi(response.Login);
|
this.login = new LoginApi(response.Login);
|
||||||
|
|
|
@ -7,6 +7,7 @@ import { TokenService } from '../abstractions/token.service';
|
||||||
import { EnvironmentUrls } from '../models/domain/environmentUrls';
|
import { EnvironmentUrls } from '../models/domain/environmentUrls';
|
||||||
|
|
||||||
import { CipherRequest } from '../models/request/cipherRequest';
|
import { CipherRequest } from '../models/request/cipherRequest';
|
||||||
|
import { CipherShareRequest } from '../models/request/cipherShareRequest';
|
||||||
import { FolderRequest } from '../models/request/folderRequest';
|
import { FolderRequest } from '../models/request/folderRequest';
|
||||||
import { ImportDirectoryRequest } from '../models/request/importDirectoryRequest';
|
import { ImportDirectoryRequest } from '../models/request/importDirectoryRequest';
|
||||||
import { PasswordHintRequest } from '../models/request/passwordHintRequest';
|
import { PasswordHintRequest } from '../models/request/passwordHintRequest';
|
||||||
|
@ -334,6 +335,27 @@ export class ApiService implements ApiServiceAbstraction {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async shareCipher(id: string, request: CipherShareRequest): Promise<any> {
|
||||||
|
const authHeader = await this.handleTokenState();
|
||||||
|
const response = await fetch(new Request(this.baseUrl + '/ciphers/' + id + '/share', {
|
||||||
|
body: JSON.stringify(request),
|
||||||
|
cache: 'no-cache',
|
||||||
|
credentials: this.getCredentials(),
|
||||||
|
headers: new Headers({
|
||||||
|
'Accept': 'application/json',
|
||||||
|
'Authorization': authHeader,
|
||||||
|
'Content-Type': 'application/json; charset=utf-8',
|
||||||
|
'Device-Type': this.deviceType,
|
||||||
|
}),
|
||||||
|
method: 'PUT',
|
||||||
|
}));
|
||||||
|
|
||||||
|
if (response.status !== 200) {
|
||||||
|
const error = await this.handleError(response, false);
|
||||||
|
return Promise.reject(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async deleteCipher(id: string): Promise<any> {
|
async deleteCipher(id: string): Promise<any> {
|
||||||
const authHeader = await this.handleTokenState();
|
const authHeader = await this.handleTokenState();
|
||||||
const response = await fetch(new Request(this.baseUrl + '/ciphers/' + id, {
|
const response = await fetch(new Request(this.baseUrl + '/ciphers/' + id, {
|
||||||
|
@ -377,6 +399,28 @@ export class ApiService implements ApiServiceAbstraction {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async shareCipherAttachment(id: string, attachmentId: string, data: FormData,
|
||||||
|
organizationId: string): Promise<any> {
|
||||||
|
const authHeader = await this.handleTokenState();
|
||||||
|
const response = await fetch(new Request(this.baseUrl + '/ciphers/' + id + '/attachment/' +
|
||||||
|
attachmentId + '/share?organizationId=' + organizationId, {
|
||||||
|
body: data,
|
||||||
|
cache: 'no-cache',
|
||||||
|
credentials: this.getCredentials(),
|
||||||
|
headers: new Headers({
|
||||||
|
'Accept': 'application/json',
|
||||||
|
'Authorization': authHeader,
|
||||||
|
'Device-Type': this.deviceType,
|
||||||
|
}),
|
||||||
|
method: 'POST',
|
||||||
|
}));
|
||||||
|
|
||||||
|
if (response.status !== 200) {
|
||||||
|
const error = await this.handleError(response, false);
|
||||||
|
return Promise.reject(error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async deleteCipherAttachment(id: string, attachmentId: string): Promise<any> {
|
async deleteCipherAttachment(id: string, attachmentId: string): Promise<any> {
|
||||||
const authHeader = await this.handleTokenState();
|
const authHeader = await this.handleTokenState();
|
||||||
const response = await fetch(new Request(this.baseUrl + '/ciphers/' + id + '/attachment/' + attachmentId, {
|
const response = await fetch(new Request(this.baseUrl + '/ciphers/' + id + '/attachment/' + attachmentId, {
|
||||||
|
|
|
@ -3,6 +3,7 @@ import { UriMatchType } from '../enums/uriMatchType';
|
||||||
|
|
||||||
import { CipherData } from '../models/data/cipherData';
|
import { CipherData } from '../models/data/cipherData';
|
||||||
|
|
||||||
|
import { Attachment } from '../models/domain/attachment';
|
||||||
import { Card } from '../models/domain/card';
|
import { Card } from '../models/domain/card';
|
||||||
import { Cipher } from '../models/domain/cipher';
|
import { Cipher } from '../models/domain/cipher';
|
||||||
import { CipherString } from '../models/domain/cipherString';
|
import { CipherString } from '../models/domain/cipherString';
|
||||||
|
@ -19,6 +20,7 @@ import { CipherRequest } from '../models/request/cipherRequest';
|
||||||
import { CipherResponse } from '../models/response/cipherResponse';
|
import { CipherResponse } from '../models/response/cipherResponse';
|
||||||
import { ErrorResponse } from '../models/response/errorResponse';
|
import { ErrorResponse } from '../models/response/errorResponse';
|
||||||
|
|
||||||
|
import { AttachmentView } from '../models/view/attachmentView';
|
||||||
import { CardView } from '../models/view/cardView';
|
import { CardView } from '../models/view/cardView';
|
||||||
import { CipherView } from '../models/view/cipherView';
|
import { CipherView } from '../models/view/cipherView';
|
||||||
import { FieldView } from '../models/view/fieldView';
|
import { FieldView } from '../models/view/fieldView';
|
||||||
|
@ -38,6 +40,7 @@ import { StorageService } from '../abstractions/storage.service';
|
||||||
import { UserService } from '../abstractions/user.service';
|
import { UserService } from '../abstractions/user.service';
|
||||||
|
|
||||||
import { Utils } from '../misc/utils';
|
import { Utils } from '../misc/utils';
|
||||||
|
import { CipherShareRequest } from '../models/request/cipherShareRequest';
|
||||||
|
|
||||||
const Keys = {
|
const Keys = {
|
||||||
ciphersPrefix: 'ciphers_',
|
ciphersPrefix: 'ciphers_',
|
||||||
|
@ -77,11 +80,39 @@ export class CipherService implements CipherServiceAbstraction {
|
||||||
this.encryptFields(model.fields, key).then((fields) => {
|
this.encryptFields(model.fields, key).then((fields) => {
|
||||||
cipher.fields = fields;
|
cipher.fields = fields;
|
||||||
}),
|
}),
|
||||||
|
this.encryptAttachments(model.attachments, key).then((attachments) => {
|
||||||
|
cipher.attachments = attachments;
|
||||||
|
}),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
return cipher;
|
return cipher;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async encryptAttachments(attachmentsModel: AttachmentView[], key: SymmetricCryptoKey): Promise<Attachment[]> {
|
||||||
|
if (attachmentsModel == null || attachmentsModel.length === 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const promises: Array<Promise<any>> = [];
|
||||||
|
const encAttachments: Attachment[] = [];
|
||||||
|
attachmentsModel.forEach(async (model) => {
|
||||||
|
const attachment = new Attachment();
|
||||||
|
attachment.id = model.id;
|
||||||
|
attachment.size = model.size;
|
||||||
|
attachment.sizeName = model.sizeName;
|
||||||
|
attachment.url = model.url;
|
||||||
|
const promise = this.encryptObjProperty(model, attachment, {
|
||||||
|
fileName: null,
|
||||||
|
}, key).then(() => {
|
||||||
|
encAttachments.push(attachment);
|
||||||
|
});
|
||||||
|
promises.push(promise);
|
||||||
|
});
|
||||||
|
|
||||||
|
await Promise.all(promises);
|
||||||
|
return encAttachments;
|
||||||
|
}
|
||||||
|
|
||||||
async encryptFields(fieldsModel: FieldView[], key: SymmetricCryptoKey): Promise<Field[]> {
|
async encryptFields(fieldsModel: FieldView[], key: SymmetricCryptoKey): Promise<Field[]> {
|
||||||
if (!fieldsModel || !fieldsModel.length) {
|
if (!fieldsModel || !fieldsModel.length) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -324,6 +355,49 @@ export class CipherService implements CipherServiceAbstraction {
|
||||||
await this.upsert(data);
|
await this.upsert(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async shareWithServer(cipher: Cipher): Promise<any> {
|
||||||
|
const request = new CipherShareRequest(cipher);
|
||||||
|
await this.apiService.shareCipher(cipher.id, request);
|
||||||
|
const userId = await this.userService.getUserId();
|
||||||
|
await this.upsert(cipher.toCipherData(userId));
|
||||||
|
}
|
||||||
|
|
||||||
|
async shareAttachmentWithServer(attachmentView: AttachmentView, cipherId: string,
|
||||||
|
organizationId: string): Promise<any> {
|
||||||
|
const attachmentResponse = await fetch(new Request(attachmentView.url, { cache: 'no-cache' }));
|
||||||
|
if (attachmentResponse.status !== 200) {
|
||||||
|
throw Error('Failed to download attachment: ' + attachmentResponse.status.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
const buf = await attachmentResponse.arrayBuffer();
|
||||||
|
const decBuf = await this.cryptoService.decryptFromBytes(buf, null);
|
||||||
|
const key = await this.cryptoService.getOrgKey(organizationId);
|
||||||
|
const encData = await this.cryptoService.encryptToBytes(decBuf, key);
|
||||||
|
const encFileName = await this.cryptoService.encrypt(attachmentView.fileName, key);
|
||||||
|
|
||||||
|
const fd = new FormData();
|
||||||
|
try {
|
||||||
|
const blob = new Blob([encData], { type: 'application/octet-stream' });
|
||||||
|
fd.append('data', blob, encFileName.encryptedString);
|
||||||
|
} catch (e) {
|
||||||
|
if (Utils.isNode && !Utils.isBrowser) {
|
||||||
|
fd.append('data', new Buffer(encData) as any, {
|
||||||
|
filepath: encFileName.encryptedString,
|
||||||
|
contentType: 'application/octet-stream',
|
||||||
|
} as any);
|
||||||
|
} else {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let response: CipherResponse;
|
||||||
|
try {
|
||||||
|
response = await this.apiService.shareCipherAttachment(cipherId, attachmentView.id, fd, organizationId);
|
||||||
|
} catch (e) {
|
||||||
|
throw new Error((e as ErrorResponse).getSingleMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
saveAttachmentWithServer(cipher: Cipher, unencryptedFile: any): Promise<Cipher> {
|
saveAttachmentWithServer(cipher: Cipher, unencryptedFile: any): Promise<Cipher> {
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
|
|
Loading…
Reference in New Issue