mirror of
https://github.com/etesync/android
synced 2025-01-11 00:01:12 +00:00
Journalmanager: add API to interact with the UserInfo
This is where the keypair is stored on the server. Both the public facing public key, and the encrypted private key
This commit is contained in:
parent
e836b4c716
commit
11e37dbd1e
@ -1,16 +1,16 @@
|
||||
package com.etesync.syncadapter.journalmanager;
|
||||
|
||||
import com.etesync.syncadapter.App;
|
||||
import com.etesync.syncadapter.GsonHelper;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
|
||||
import org.spongycastle.util.Arrays;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
import com.etesync.syncadapter.App;
|
||||
import com.etesync.syncadapter.GsonHelper;
|
||||
|
||||
import lombok.Getter;
|
||||
import okhttp3.HttpUrl;
|
||||
import okhttp3.OkHttpClient;
|
||||
|
@ -0,0 +1,150 @@
|
||||
package com.etesync.syncadapter.journalmanager;
|
||||
|
||||
import com.etesync.syncadapter.GsonHelper;
|
||||
|
||||
import org.apache.commons.codec.Charsets;
|
||||
import org.spongycastle.util.Arrays;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.HttpURLConnection;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import okhttp3.HttpUrl;
|
||||
import okhttp3.OkHttpClient;
|
||||
import okhttp3.Request;
|
||||
import okhttp3.RequestBody;
|
||||
import okhttp3.Response;
|
||||
import okhttp3.ResponseBody;
|
||||
|
||||
import static com.etesync.syncadapter.journalmanager.Crypto.CryptoManager.HMAC_SIZE;
|
||||
import static com.etesync.syncadapter.journalmanager.Crypto.toHex;
|
||||
|
||||
public class UserInfoManager extends BaseManager {
|
||||
public UserInfoManager(OkHttpClient httpClient, HttpUrl remote) {
|
||||
this.remote = remote.newBuilder()
|
||||
.addPathSegments("api/v1/user")
|
||||
.addPathSegment("")
|
||||
.build();
|
||||
|
||||
this.client = httpClient;
|
||||
}
|
||||
|
||||
public UserInfo get(Crypto.CryptoManager cryptoManager, String owner) throws Exceptions.HttpException, Exceptions.IntegrityException, Exceptions.GenericCryptoException {
|
||||
HttpUrl remote = this.remote.newBuilder().addPathSegment(owner).addPathSegment("").build();
|
||||
Request request = new Request.Builder()
|
||||
.get()
|
||||
.url(remote)
|
||||
.build();
|
||||
|
||||
Response response;
|
||||
try {
|
||||
response = newCall(request);
|
||||
} catch (Exceptions.HttpException e) {
|
||||
if (e.status == HttpURLConnection.HTTP_NOT_FOUND) {
|
||||
return null;
|
||||
} else {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
ResponseBody body = response.body();
|
||||
UserInfo ret = GsonHelper.gson.fromJson(body.charStream(), UserInfo.class);
|
||||
ret.verify(cryptoManager);
|
||||
ret.setOwner(owner);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
public void delete(UserInfo userInfo) throws Exceptions.HttpException {
|
||||
HttpUrl remote = this.remote.newBuilder().addPathSegment(userInfo.getOwner()).addPathSegment("").build();
|
||||
Request request = new Request.Builder()
|
||||
.delete()
|
||||
.url(remote)
|
||||
.build();
|
||||
|
||||
newCall(request);
|
||||
}
|
||||
|
||||
public void create(UserInfo userInfo) throws Exceptions.HttpException {
|
||||
RequestBody body = RequestBody.create(JSON, userInfo.toJson());
|
||||
|
||||
Request request = new Request.Builder()
|
||||
.post(body)
|
||||
.url(remote)
|
||||
.build();
|
||||
|
||||
newCall(request);
|
||||
}
|
||||
|
||||
public void update(UserInfo userInfo) throws Exceptions.HttpException {
|
||||
HttpUrl remote = this.remote.newBuilder().addPathSegment(userInfo.getOwner()).addPathSegment("").build();
|
||||
RequestBody body = RequestBody.create(JSON, userInfo.toJson());
|
||||
|
||||
Request request = new Request.Builder()
|
||||
.put(body)
|
||||
.url(remote)
|
||||
.build();
|
||||
|
||||
newCall(request);
|
||||
}
|
||||
|
||||
public static class UserInfo {
|
||||
@Setter
|
||||
@Getter
|
||||
private transient String owner;
|
||||
@Getter
|
||||
private byte version;
|
||||
@Getter
|
||||
private byte[] pubkey;
|
||||
private byte[] content;
|
||||
|
||||
public byte[] getContent(Crypto.CryptoManager crypto) {
|
||||
byte[] content = Arrays.copyOfRange(this.content, HMAC_SIZE, this.content.length);
|
||||
return crypto.decrypt(content);
|
||||
}
|
||||
|
||||
void setContent(Crypto.CryptoManager crypto, byte[] rawContent) {
|
||||
byte[] content = crypto.encrypt(rawContent);
|
||||
this.content = Arrays.concatenate(calculateHmac(crypto, content), content);
|
||||
}
|
||||
|
||||
void verify(Crypto.CryptoManager crypto) throws Exceptions.IntegrityException {
|
||||
if (this.content == null) {
|
||||
// Nothing to verify.
|
||||
return;
|
||||
}
|
||||
|
||||
byte[] hmac = Arrays.copyOfRange(this.content, 0, HMAC_SIZE);
|
||||
byte[] content = Arrays.copyOfRange(this.content, HMAC_SIZE, this.content.length);
|
||||
|
||||
byte[] correctHash = calculateHmac(crypto, content);
|
||||
if (!Arrays.areEqual(hmac, correctHash)) {
|
||||
throw new Exceptions.IntegrityException("Bad HMAC. " + toHex(hmac) + " != " + toHex(correctHash));
|
||||
}
|
||||
}
|
||||
|
||||
private byte[] calculateHmac(Crypto.CryptoManager crypto, byte[] content) {
|
||||
return crypto.hmac(Arrays.concatenate(content, pubkey));
|
||||
}
|
||||
|
||||
private UserInfo() {
|
||||
}
|
||||
|
||||
public UserInfo(Crypto.CryptoManager crypto, String owner, byte[] pubkey, byte[] content) {
|
||||
this.owner = owner;
|
||||
this.pubkey = pubkey;
|
||||
version = crypto.getVersion();
|
||||
setContent(crypto, content);
|
||||
}
|
||||
|
||||
public static UserInfo generate(Crypto.CryptoManager cryptoManager, String owner) throws IOException {
|
||||
Crypto.AsymmetricKeyPair keyPair = Crypto.generateKeyPair();
|
||||
return new UserInfo(cryptoManager, owner, keyPair.getPublicKey(), keyPair.getPrivateKey());
|
||||
}
|
||||
|
||||
String toJson() {
|
||||
return GsonHelper.gson.toJson(this, getClass());
|
||||
}
|
||||
}
|
||||
}
|
@ -12,6 +12,7 @@ import com.etesync.syncadapter.App;
|
||||
import com.etesync.syncadapter.HttpClient;
|
||||
import com.etesync.syncadapter.model.CollectionInfo;
|
||||
|
||||
import org.apache.commons.codec.Charsets;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@ -30,7 +31,9 @@ import okio.BufferedSink;
|
||||
|
||||
import static org.junit.Assert.assertArrayEquals;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNotEquals;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
|
||||
public class ServiceTest {
|
||||
private OkHttpClient httpClient;
|
||||
@ -196,4 +199,37 @@ public class ServiceTest {
|
||||
}
|
||||
assertNotNull(caught);
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testUserInfo() throws IOException, Exceptions.HttpException, Exceptions.GenericCryptoException, Exceptions.IntegrityException {
|
||||
Crypto.CryptoManager cryptoManager = new Crypto.CryptoManager(Constants.CURRENT_VERSION, Helpers.keyBase64, "userInfo");
|
||||
UserInfoManager.UserInfo userInfo, userInfo2;
|
||||
UserInfoManager manager = new UserInfoManager(httpClient, remote);
|
||||
|
||||
// Get when there's nothing
|
||||
userInfo = manager.get(cryptoManager, Helpers.USER);
|
||||
assertNull(userInfo);
|
||||
|
||||
// Create
|
||||
userInfo = UserInfoManager.UserInfo.generate(cryptoManager, Helpers.USER);
|
||||
manager.create(userInfo);
|
||||
|
||||
// Get
|
||||
userInfo2 = manager.get(cryptoManager, Helpers.USER);
|
||||
assertNotNull(userInfo2);
|
||||
assertArrayEquals(userInfo.getContent(cryptoManager), userInfo2.getContent(cryptoManager));
|
||||
|
||||
// Update
|
||||
userInfo.setContent(cryptoManager, "test".getBytes(Charsets.UTF_8));
|
||||
manager.update(userInfo);
|
||||
userInfo2 = manager.get(cryptoManager, Helpers.USER);
|
||||
assertNotNull(userInfo2);
|
||||
assertArrayEquals(userInfo.getContent(cryptoManager), userInfo2.getContent(cryptoManager));
|
||||
|
||||
// Delete
|
||||
manager.delete(userInfo);
|
||||
userInfo = manager.get(cryptoManager, Helpers.USER);
|
||||
assertNull(userInfo);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user