-
-
Notifications
You must be signed in to change notification settings - Fork 736
/
Copy pathEncryptedFileObjectStore.java
94 lines (78 loc) · 3.38 KB
/
EncryptedFileObjectStore.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
package com.parse;
import android.content.Context;
import androidx.security.crypto.EncryptedFile;
import androidx.security.crypto.MasterKey;
import com.parse.boltsinternal.Task;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.File;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.concurrent.Callable;
public class EncryptedFileObjectStore<T extends ParseObject> implements ParseObjectStore<T> {
private final String className;
private final File file;
private final EncryptedFile encryptedFile;
private final ParseObjectCurrentCoder coder;
public EncryptedFileObjectStore(Class<T> clazz, File file, ParseObjectCurrentCoder coder) {
this(getSubclassingController().getClassName(clazz), file, coder);
}
public EncryptedFileObjectStore(String className, File file, ParseObjectCurrentCoder coder) {
this.className = className;
this.file = file;
this.coder = coder;
Context context = ParsePlugins.get().applicationContext();
try {
encryptedFile = new EncryptedFile.Builder(context, file, new MasterKey.Builder(context).setKeyScheme(MasterKey.KeyScheme.AES256_GCM).build(), EncryptedFile.FileEncryptionScheme.AES256_GCM_HKDF_4KB).build();
} catch (GeneralSecurityException | IOException e) {
throw new RuntimeException(e.getMessage());
}
}
private static ParseObjectSubclassingController getSubclassingController() {
return ParseCorePlugins.getInstance().getSubclassingController();
}
private void saveToDisk(ParseObject current) throws IOException, GeneralSecurityException {
JSONObject json = coder.encode(current.getState(), null, PointerEncoder.get());
ParseFileUtils.writeJSONObjectToFile(encryptedFile, json);
}
private T getFromDisk() throws GeneralSecurityException, JSONException, IOException {
return ParseObject.from(coder.decode(ParseObject.State.newBuilder(className), ParseFileUtils.readFileToJSONObject(encryptedFile), ParseDecoder.get()).isComplete(true).build());
}
@Override
public Task<T> getAsync() {
return Task.call(new Callable<T>() {
@Override
public T call() throws Exception {
if (!file.exists()) return null;
try {
return getFromDisk();
} catch (GeneralSecurityException | JSONException | IOException e) {
throw new RuntimeException(e);
}
}
}, ParseExecutors.io());
}
@Override
public Task<Void> setAsync(T object) {
return Task.call(() -> {
if (file.exists() && !ParseFileUtils.deleteQuietly(file)) throw new RuntimeException("Unable to delete");
try {
saveToDisk(object);
} catch (IOException | GeneralSecurityException e) {
throw new RuntimeException(e);
}
return null;
}, ParseExecutors.io());
}
@Override
public Task<Boolean> existsAsync() {
return Task.call(file::exists, ParseExecutors.io());
}
@Override
public Task<Void> deleteAsync() {
return Task.call(() -> {
if (file.exists() && !ParseFileUtils.deleteQuietly(file)) throw new RuntimeException("Unable to delete");
return null;
}, ParseExecutors.io());
}
}