Merge pull request #811 from ente-io/add-encrypted-collection-type
Add encrypted collection type
This commit is contained in:
commit
05c1af8ec0
|
@ -28,6 +28,8 @@ import {
|
|||
CollectionSummaries,
|
||||
CollectionSummary,
|
||||
CollectionFilesCount,
|
||||
EncryptedCollection,
|
||||
CollectionMagicMetadata,
|
||||
} from 'types/collection';
|
||||
import {
|
||||
COLLECTION_SORT_BY,
|
||||
|
@ -50,14 +52,14 @@ const COLLECTION_TABLE = 'collections';
|
|||
const COLLECTION_UPDATION_TIME = 'collection-updation-time';
|
||||
|
||||
const getCollectionWithSecrets = async (
|
||||
collection: Collection,
|
||||
collection: EncryptedCollection,
|
||||
masterKey: string
|
||||
) => {
|
||||
): Promise<Collection> => {
|
||||
const worker = await new CryptoWorker();
|
||||
const userID = getData(LS_KEYS.USER).id;
|
||||
let decryptedKey: string;
|
||||
let collectionKey: string;
|
||||
if (collection.owner.id === userID) {
|
||||
decryptedKey = await worker.decryptB64(
|
||||
collectionKey = await worker.decryptB64(
|
||||
collection.encryptedKey,
|
||||
collection.keyDecryptionNonce,
|
||||
masterKey
|
||||
|
@ -69,30 +71,36 @@ const getCollectionWithSecrets = async (
|
|||
keyAttributes.secretKeyDecryptionNonce,
|
||||
masterKey
|
||||
);
|
||||
decryptedKey = await worker.boxSealOpen(
|
||||
collectionKey = await worker.boxSealOpen(
|
||||
collection.encryptedKey,
|
||||
keyAttributes.publicKey,
|
||||
secretKey
|
||||
);
|
||||
}
|
||||
collection.name =
|
||||
const collectionName =
|
||||
collection.name ||
|
||||
(await worker.decryptToUTF8(
|
||||
collection.encryptedName,
|
||||
collection.nameDecryptionNonce,
|
||||
decryptedKey
|
||||
collectionKey
|
||||
));
|
||||
|
||||
let collectionMagicMetadata: CollectionMagicMetadata;
|
||||
if (collection.magicMetadata?.data) {
|
||||
collection.magicMetadata.data = await worker.decryptMetadata(
|
||||
collection.magicMetadata.data,
|
||||
collection.magicMetadata.header,
|
||||
decryptedKey
|
||||
);
|
||||
collectionMagicMetadata = {
|
||||
...collection.magicMetadata,
|
||||
data: await worker.decryptMetadata(
|
||||
collection.magicMetadata.data,
|
||||
collection.magicMetadata.header,
|
||||
collectionKey
|
||||
),
|
||||
};
|
||||
}
|
||||
return {
|
||||
...collection,
|
||||
key: decryptedKey,
|
||||
name: collectionName,
|
||||
key: collectionKey,
|
||||
magicMetadata: collectionMagicMetadata,
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -109,27 +117,25 @@ const getCollections = async (
|
|||
},
|
||||
{ 'X-Auth-Token': token }
|
||||
);
|
||||
const promises: Promise<Collection>[] = resp.data.collections.map(
|
||||
async (collection: Collection) => {
|
||||
if (collection.isDeleted) {
|
||||
return collection;
|
||||
const decryptedCollections: Collection[] = await Promise.all(
|
||||
resp.data.collections.map(
|
||||
async (collection: EncryptedCollection) => {
|
||||
if (collection.isDeleted) {
|
||||
return collection;
|
||||
}
|
||||
try {
|
||||
return await getCollectionWithSecrets(collection, key);
|
||||
} catch (e) {
|
||||
logError(e, `decryption failed for collection`, {
|
||||
collectionID: collection.id,
|
||||
});
|
||||
return collection;
|
||||
}
|
||||
}
|
||||
let collectionWithSecrets = collection;
|
||||
try {
|
||||
collectionWithSecrets = await getCollectionWithSecrets(
|
||||
collection,
|
||||
key
|
||||
);
|
||||
} catch (e) {
|
||||
logError(e, `decryption failed for collection`, {
|
||||
collectionID: collection.id,
|
||||
});
|
||||
}
|
||||
return collectionWithSecrets;
|
||||
}
|
||||
)
|
||||
);
|
||||
// only allow deleted or collection with key, filtering out collection whose decryption failed
|
||||
const collections = (await Promise.all(promises)).filter(
|
||||
const collections = decryptedCollections.filter(
|
||||
(collection) => collection.isDeleted || collection.key
|
||||
);
|
||||
return collections;
|
||||
|
@ -292,7 +298,7 @@ export const createCollection = async (
|
|||
collectionName,
|
||||
collectionKey
|
||||
);
|
||||
const newCollection: Collection = {
|
||||
const newCollection: EncryptedCollection = {
|
||||
id: null,
|
||||
owner: null,
|
||||
encryptedKey,
|
||||
|
@ -306,15 +312,12 @@ export const createCollection = async (
|
|||
isDeleted: false,
|
||||
magicMetadata: null,
|
||||
};
|
||||
let createdCollection: Collection = await postCollection(
|
||||
newCollection,
|
||||
token
|
||||
);
|
||||
createdCollection = await getCollectionWithSecrets(
|
||||
const createdCollection = await postCollection(newCollection, token);
|
||||
const decryptedCreatedCollection = await getCollectionWithSecrets(
|
||||
createdCollection,
|
||||
encryptionKey
|
||||
);
|
||||
return createdCollection;
|
||||
return decryptedCreatedCollection;
|
||||
} catch (e) {
|
||||
logError(e, 'create collection failed');
|
||||
throw e;
|
||||
|
@ -322,9 +325,9 @@ export const createCollection = async (
|
|||
};
|
||||
|
||||
const postCollection = async (
|
||||
collectionData: Collection,
|
||||
collectionData: EncryptedCollection,
|
||||
token: string
|
||||
): Promise<Collection> => {
|
||||
): Promise<EncryptedCollection> => {
|
||||
try {
|
||||
const response = await HTTPService.post(
|
||||
`${ENDPOINT}/collections`,
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { getEndpoint } from 'utils/common/apiUtil';
|
||||
import localForage from 'utils/storage/localForage';
|
||||
import { Collection } from 'types/collection';
|
||||
import { Collection, EncryptedCollection } from 'types/collection';
|
||||
import HTTPService from './HTTPService';
|
||||
import { logError } from 'utils/sentry';
|
||||
import { decryptFile, mergeMetadata, sortFiles } from 'utils/file';
|
||||
|
@ -340,7 +340,7 @@ export const verifyPublicCollectionPassword = async (
|
|||
};
|
||||
|
||||
const decryptCollectionName = async (
|
||||
collection: Collection,
|
||||
collection: EncryptedCollection,
|
||||
collectionKey: string
|
||||
) => {
|
||||
const worker = await new CryptoWorker();
|
||||
|
|
|
@ -2,28 +2,43 @@ import { User } from 'types/user';
|
|||
import { EnteFile } from 'types/file';
|
||||
import { CollectionSummaryType, CollectionType } from 'constants/collection';
|
||||
import {
|
||||
EncryptedMagicMetadata,
|
||||
MagicMetadataCore,
|
||||
SUB_TYPE,
|
||||
VISIBILITY_STATE,
|
||||
} from 'types/magicMetadata';
|
||||
|
||||
export interface Collection {
|
||||
export interface EncryptedCollection {
|
||||
id: number;
|
||||
owner: User;
|
||||
key?: string;
|
||||
// collection name was unencrypted in the past, so we need to keep it as optional
|
||||
name?: string;
|
||||
encryptedName?: string;
|
||||
nameDecryptionNonce?: string;
|
||||
encryptedKey: string;
|
||||
keyDecryptionNonce: string;
|
||||
encryptedName: string;
|
||||
nameDecryptionNonce: string;
|
||||
type: CollectionType;
|
||||
attributes: collectionAttributes;
|
||||
sharees: User[];
|
||||
updationTime: number;
|
||||
encryptedKey: string;
|
||||
keyDecryptionNonce: string;
|
||||
isDeleted: boolean;
|
||||
isSharedCollection?: boolean;
|
||||
publicURLs?: PublicURL[];
|
||||
magicMetadata?: CollectionMagicMetadata;
|
||||
updationTime: number;
|
||||
isDeleted: boolean;
|
||||
magicMetadata: EncryptedMagicMetadata;
|
||||
}
|
||||
|
||||
export interface Collection
|
||||
extends Omit<
|
||||
EncryptedCollection,
|
||||
| 'encryptedKey'
|
||||
| 'keyDecryptionNonce'
|
||||
| 'encryptedName'
|
||||
| 'nameDecryptionNonce'
|
||||
| 'magicMetadata'
|
||||
> {
|
||||
key: string;
|
||||
name: string;
|
||||
isSharedCollection?: boolean;
|
||||
magicMetadata: CollectionMagicMetadata;
|
||||
}
|
||||
|
||||
export interface PublicURL {
|
||||
|
|
Loading…
Reference in a new issue