mirror of
https://github.com/bitwarden/browser.git
synced 2024-12-25 16:59:17 +01:00
api support for sharing
This commit is contained in:
parent
5db55496c2
commit
b3f71ed8e4
@ -1,6 +1,7 @@
|
||||
import { EnvironmentUrls } from '../models/domain/environmentUrls';
|
||||
|
||||
import { CipherRequest } from '../models/request/cipherRequest';
|
||||
import { CipherShareRequest } from '../models/request/cipherShareRequest';
|
||||
import { FolderRequest } from '../models/request/folderRequest';
|
||||
import { ImportDirectoryRequest } from '../models/request/importDirectoryRequest';
|
||||
import { PasswordHintRequest } from '../models/request/passwordHintRequest';
|
||||
@ -15,6 +16,8 @@ import { IdentityTwoFactorResponse } from '../models/response/identityTwoFactorR
|
||||
import { ProfileResponse } from '../models/response/profileResponse';
|
||||
import { SyncResponse } from '../models/response/syncResponse';
|
||||
|
||||
import { AttachmentView } from '../models/view/attachmentView';
|
||||
|
||||
export abstract class ApiService {
|
||||
urlsSet: boolean;
|
||||
baseUrl: string;
|
||||
@ -34,8 +37,10 @@ export abstract class ApiService {
|
||||
deleteFolder: (id: string) => Promise<any>;
|
||||
postCipher: (request: CipherRequest) => Promise<CipherResponse>;
|
||||
putCipher: (id: string, request: CipherRequest) => Promise<CipherResponse>;
|
||||
shareCipher: (id: string, request: CipherShareRequest) => Promise<any>;
|
||||
deleteCipher: (id: string) => Promise<any>;
|
||||
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>;
|
||||
getSync: () => Promise<SyncResponse>;
|
||||
postImportDirectory: (organizationId: string, request: ImportDirectoryRequest) => Promise<any>;
|
||||
|
@ -6,6 +6,7 @@ import { Cipher } from '../models/domain/cipher';
|
||||
import { Field } from '../models/domain/field';
|
||||
import { SymmetricCryptoKey } from '../models/domain/symmetricCryptoKey';
|
||||
|
||||
import { AttachmentView } from '../models/view/attachmentView';
|
||||
import { CipherView } from '../models/view/cipherView';
|
||||
import { FieldView } from '../models/view/fieldView';
|
||||
|
||||
@ -25,6 +26,9 @@ export abstract class CipherService {
|
||||
updateLastUsedDate: (id: string) => Promise<void>;
|
||||
saveNeverDomain: (domain: string) => Promise<void>;
|
||||
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>;
|
||||
saveAttachmentRawWithServer: (cipher: Cipher, filename: string, data: ArrayBuffer) => Promise<Cipher>;
|
||||
upsert: (cipher: CipherData | CipherData[]) => Promise<any>;
|
||||
|
@ -7,7 +7,10 @@ export class AttachmentData {
|
||||
size: number;
|
||||
sizeName: string;
|
||||
|
||||
constructor(response: AttachmentResponse) {
|
||||
constructor(response?: AttachmentResponse) {
|
||||
if (response == null) {
|
||||
return;
|
||||
}
|
||||
this.id = response.id;
|
||||
this.url = response.url;
|
||||
this.fileName = response.fileName;
|
||||
|
@ -8,7 +8,11 @@ export class CardData {
|
||||
expYear: string;
|
||||
code: string;
|
||||
|
||||
constructor(data: CardApi) {
|
||||
constructor(data?: CardApi) {
|
||||
if (data == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.cardholderName = data.cardholderName;
|
||||
this.brand = data.brand;
|
||||
this.number = data.number;
|
||||
|
@ -17,7 +17,7 @@ export class CipherData {
|
||||
edit: boolean;
|
||||
organizationUseTotp: boolean;
|
||||
favorite: boolean;
|
||||
revisionDate: string;
|
||||
revisionDate: Date;
|
||||
type: CipherType;
|
||||
sizeName: string;
|
||||
name: string;
|
||||
@ -30,7 +30,11 @@ export class CipherData {
|
||||
attachments?: AttachmentData[];
|
||||
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.organizationId = response.organizationId;
|
||||
this.folderId = response.folderId;
|
||||
|
@ -7,7 +7,10 @@ export class FieldData {
|
||||
name: string;
|
||||
value: string;
|
||||
|
||||
constructor(response: FieldApi) {
|
||||
constructor(response?: FieldApi) {
|
||||
if (response == null) {
|
||||
return;
|
||||
}
|
||||
this.type = response.type;
|
||||
this.name = response.name;
|
||||
this.value = response.value;
|
||||
|
@ -20,7 +20,11 @@ export class IdentityData {
|
||||
passportNumber: string;
|
||||
licenseNumber: string;
|
||||
|
||||
constructor(data: IdentityApi) {
|
||||
constructor(data?: IdentityApi) {
|
||||
if (data == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.title = data.title;
|
||||
this.firstName = data.firstName;
|
||||
this.middleName = data.middleName;
|
||||
|
@ -9,7 +9,11 @@ export class LoginData {
|
||||
password: string;
|
||||
totp: string;
|
||||
|
||||
constructor(data: LoginApi) {
|
||||
constructor(data?: LoginApi) {
|
||||
if (data == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.username = data.username;
|
||||
this.password = data.password;
|
||||
this.totp = data.totp;
|
||||
|
@ -6,7 +6,10 @@ export class LoginUriData {
|
||||
uri: string;
|
||||
match: UriMatchType = null;
|
||||
|
||||
constructor(data: LoginUriApi) {
|
||||
constructor(data?: LoginUriApi) {
|
||||
if (data == null) {
|
||||
return;
|
||||
}
|
||||
this.uri = data.uri;
|
||||
this.match = data.match;
|
||||
}
|
||||
|
@ -5,7 +5,11 @@ import { SecureNoteApi } from '../api/secureNoteApi';
|
||||
export class SecureNoteData {
|
||||
type: SecureNoteType;
|
||||
|
||||
constructor(data: SecureNoteApi) {
|
||||
constructor(data?: SecureNoteApi) {
|
||||
if (data == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.type = data.type;
|
||||
}
|
||||
}
|
||||
|
@ -32,4 +32,15 @@ export class Attachment extends Domain {
|
||||
fileName: null,
|
||||
}, 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,
|
||||
}, 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;
|
||||
organizationUseTotp: boolean;
|
||||
edit: boolean;
|
||||
revisionDate: Date;
|
||||
localData: any;
|
||||
login: Login;
|
||||
identity: Identity;
|
||||
@ -40,16 +41,18 @@ export class Cipher extends Domain {
|
||||
|
||||
this.buildDomainModel(this, obj, {
|
||||
id: null,
|
||||
userId: null,
|
||||
organizationId: null,
|
||||
folderId: null,
|
||||
name: null,
|
||||
notes: null,
|
||||
}, alreadyEncrypted, ['id', 'organizationId', 'folderId']);
|
||||
}, alreadyEncrypted, ['id', 'userId', 'organizationId', 'folderId']);
|
||||
|
||||
this.type = obj.type;
|
||||
this.favorite = obj.favorite;
|
||||
this.organizationUseTotp = obj.organizationUseTotp;
|
||||
this.edit = obj.edit;
|
||||
this.revisionDate = obj.revisionDate;
|
||||
this.collectionIds = obj.collectionIds;
|
||||
this.localData = localData;
|
||||
|
||||
@ -142,4 +145,55 @@ export class Cipher extends Domain {
|
||||
|
||||
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> {
|
||||
const promises = [];
|
||||
|
@ -31,4 +31,14 @@ export class Field extends Domain {
|
||||
value: null,
|
||||
}, 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,
|
||||
}, 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;
|
||||
}
|
||||
|
||||
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,
|
||||
}, 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> {
|
||||
return Promise.resolve(new SecureNoteView(this));
|
||||
}
|
||||
|
||||
toSecureNoteData(): SecureNoteData {
|
||||
const n = new SecureNoteData();
|
||||
n.type = this.type;
|
||||
return n;
|
||||
}
|
||||
}
|
||||
|
7
src/models/request/cipherBulkDeleteRequest.ts
Normal file
7
src/models/request/cipherBulkDeleteRequest.ts
Normal file
@ -0,0 +1,7 @@
|
||||
export class CipherBulkDeleteRequest {
|
||||
ids: string[];
|
||||
|
||||
constructor(ids: string[]) {
|
||||
this.ids = ids;
|
||||
}
|
||||
}
|
9
src/models/request/cipherBulkMoveRequest.ts
Normal file
9
src/models/request/cipherBulkMoveRequest.ts
Normal file
@ -0,0 +1,9 @@
|
||||
export class CipherBulkMoveRequest {
|
||||
ids: string[];
|
||||
folderId: string;
|
||||
|
||||
constructor(ids: string[], folderId: string) {
|
||||
this.ids = ids;
|
||||
this.folderId = folderId;
|
||||
}
|
||||
}
|
7
src/models/request/cipherCollectionsRequest.ts
Normal file
7
src/models/request/cipherCollectionsRequest.ts
Normal file
@ -0,0 +1,7 @@
|
||||
export class CipherCollectionsRequest {
|
||||
collectionIds: string[];
|
||||
|
||||
constructor(collectionIds: string[]) {
|
||||
this.collectionIds = collectionIds;
|
||||
}
|
||||
}
|
@ -21,6 +21,7 @@ export class CipherRequest {
|
||||
card: CardApi;
|
||||
identity: IdentityApi;
|
||||
fields: FieldApi[];
|
||||
attachments: { [id: string]: string; };
|
||||
|
||||
constructor(cipher: Cipher) {
|
||||
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;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
13
src/models/request/cipherShareRequest.ts
Normal file
13
src/models/request/cipherShareRequest.ts
Normal file
@ -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;
|
||||
edit: boolean;
|
||||
organizationUseTotp: boolean;
|
||||
revisionDate: string;
|
||||
revisionDate: Date;
|
||||
attachments: AttachmentResponse[];
|
||||
collectionIds: string[];
|
||||
|
||||
@ -35,7 +35,7 @@ export class CipherResponse {
|
||||
this.favorite = response.Favorite;
|
||||
this.edit = response.Edit;
|
||||
this.organizationUseTotp = response.OrganizationUseTotp;
|
||||
this.revisionDate = response.RevisionDate;
|
||||
this.revisionDate = new Date(response.RevisionDate);
|
||||
|
||||
if (response.Login != null) {
|
||||
this.login = new LoginApi(response.Login);
|
||||
|
@ -7,6 +7,7 @@ import { TokenService } from '../abstractions/token.service';
|
||||
import { EnvironmentUrls } from '../models/domain/environmentUrls';
|
||||
|
||||
import { CipherRequest } from '../models/request/cipherRequest';
|
||||
import { CipherShareRequest } from '../models/request/cipherShareRequest';
|
||||
import { FolderRequest } from '../models/request/folderRequest';
|
||||
import { ImportDirectoryRequest } from '../models/request/importDirectoryRequest';
|
||||
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> {
|
||||
const authHeader = await this.handleTokenState();
|
||||
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> {
|
||||
const authHeader = await this.handleTokenState();
|
||||
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 { Attachment } from '../models/domain/attachment';
|
||||
import { Card } from '../models/domain/card';
|
||||
import { Cipher } from '../models/domain/cipher';
|
||||
import { CipherString } from '../models/domain/cipherString';
|
||||
@ -19,6 +20,7 @@ import { CipherRequest } from '../models/request/cipherRequest';
|
||||
import { CipherResponse } from '../models/response/cipherResponse';
|
||||
import { ErrorResponse } from '../models/response/errorResponse';
|
||||
|
||||
import { AttachmentView } from '../models/view/attachmentView';
|
||||
import { CardView } from '../models/view/cardView';
|
||||
import { CipherView } from '../models/view/cipherView';
|
||||
import { FieldView } from '../models/view/fieldView';
|
||||
@ -38,6 +40,7 @@ import { StorageService } from '../abstractions/storage.service';
|
||||
import { UserService } from '../abstractions/user.service';
|
||||
|
||||
import { Utils } from '../misc/utils';
|
||||
import { CipherShareRequest } from '../models/request/cipherShareRequest';
|
||||
|
||||
const Keys = {
|
||||
ciphersPrefix: 'ciphers_',
|
||||
@ -77,11 +80,39 @@ export class CipherService implements CipherServiceAbstraction {
|
||||
this.encryptFields(model.fields, key).then((fields) => {
|
||||
cipher.fields = fields;
|
||||
}),
|
||||
this.encryptAttachments(model.attachments, key).then((attachments) => {
|
||||
cipher.attachments = attachments;
|
||||
}),
|
||||
]);
|
||||
|
||||
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[]> {
|
||||
if (!fieldsModel || !fieldsModel.length) {
|
||||
return null;
|
||||
@ -324,6 +355,49 @@ export class CipherService implements CipherServiceAbstraction {
|
||||
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> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
|
Loading…
Reference in New Issue
Block a user