Skip to content
Snippets Groups Projects
Commit 87f0b507 authored by Frantisek Simon's avatar Frantisek Simon
Browse files

#78 Added security hash to REST server requests

parent b99478b3
Branches
Tags
No related merge requests found
external @ 0687994a
Subproject commit 17f7d92b6b507736f75c8ba45498b08b24e7469c
Subproject commit 0687994a57443ecb3a5598fac9114b1b96fbfcde
......@@ -5,9 +5,16 @@ import com.badlogic.gdx.Net;
import com.badlogic.gdx.net.HttpRequestBuilder;
import com.badlogic.gdx.utils.JsonWriter;
import java.io.UnsupportedEncodingException;
import java.security.NoSuchAlgorithmException;
import java.util.concurrent.atomic.AtomicLong;
import cz.nic.tablexia.Tablexia;
import cz.nic.tablexia.TablexiaBuildConfig;
import cz.nic.tablexia.shared.rest.UserRestPath;
import cz.nic.tablexia.shared.security.SecurityHelper;
import cz.nic.tablexia.shared.security.SecurityRestHeader;
import cz.nic.tablexia.shared.security.SecurityRestPath;
import cz.nic.tablexia.sync.work.SyncWork;
import cz.nic.tablexia.util.Log;
......@@ -25,6 +32,8 @@ public class RestSynchronizationService {
public static final String REST_URL = TablexiaBuildConfig.TABLEXIA_SERVER_ADDRESS != null ? TablexiaBuildConfig.TABLEXIA_SERVER_ADDRESS : LOCALHOST_REST_URL;
public static final String SECRET_KEY = TablexiaBuildConfig.TABLEXIA_SERVER_SECRET;
public static AtomicLong secretTime;
private HttpRequestBuilder builder;
private Net.HttpResponseListener listener;
......@@ -51,12 +60,23 @@ public class RestSynchronizationService {
builder.header(key, value);
}
public void setSecurityHeaders() {
try {
setHeader(SecurityRestHeader.HEADER_AUTH_KEY, SecurityHelper.getSecretHash(SECRET_KEY, secretTime.get()));
setHeader(SecurityRestHeader.HEADER_AUTH_TIME, secretTime.toString());
} catch (NoSuchAlgorithmException e) {
Log.err(RestSynchronizationService.class, "failed to set security headers", e);
} catch (UnsupportedEncodingException e) {
Log.err(RestSynchronizationService.class, "failed to set security headers", e);
}
}
public static void doSyncWork(SyncWork work) {
if (Tablexia.connectionManager.isUsingMobileData()) {
Log.debug(RestSynchronizationService.class, "Using mobile data connection - synchronization disabled");
return;
}
work.send(new RestSynchronizationService(work.getUrl(), work.getHttpMethod(), work));
work.process(new RestSynchronizationService(work.getUrl(), work.getHttpMethod(), work));
}
public static String getCreateUserUrl() {
......@@ -74,4 +94,8 @@ public class RestSynchronizationService {
public static String getDeleteUserUrl(String uuid) {
return REST_URL + UserRestPath.USER_PATH + UserRestPath.USER_DELETE.replace("{" + UserRestPath.USER_UUID + "}", uuid);
}
public static String getSecurityGetUrl() {
return REST_URL + SecurityRestPath.SECURITY_PATH + SecurityRestPath.SECURITY_GET;
}
}
......@@ -2,6 +2,7 @@ package cz.nic.tablexia.sync.work;
import com.badlogic.gdx.Net;
import com.badlogic.gdx.net.HttpRequestHeader;
import com.badlogic.gdx.net.HttpStatus;
import com.badlogic.gdx.utils.Json;
import cz.nic.tablexia.model.UserDAO;
......@@ -33,6 +34,15 @@ public class DownloadUser extends SyncWork {
@Override
protected void onResponse(Net.HttpResponse httpResponse) {
if (httpResponse.getStatus().getStatusCode() != HttpStatus.SC_OK) {
String errorMsg = "Failed to download user. Response code: " + httpResponse.getStatus().getStatusCode();
Log.err(this.getClass(), errorMsg);
if (getListener() != null) {
getListener().onFailure(new Throwable(errorMsg));
}
return;
}
String userJson = httpResponse.getResultAsString();
Json json = new Json();
json.setIgnoreUnknownFields(true);
......
package cz.nic.tablexia.sync.work;
import com.badlogic.gdx.Net;
import com.badlogic.gdx.net.HttpStatus;
import com.badlogic.gdx.utils.Timer;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.atomic.AtomicLong;
import cz.nic.tablexia.shared.model.User;
import cz.nic.tablexia.sync.RestSynchronizationService;
import cz.nic.tablexia.util.Log;
......@@ -20,6 +25,9 @@ public abstract class SyncWork implements Net.HttpResponseListener {
private RestSyncListener listener;
private User user;
// stores request that should be send after getting security time from server
private static List<SyncWork> queuedRequests = new CopyOnWriteArrayList<SyncWork>();
public String getHttpMethod() {
return Net.HttpMethods.POST;
}
......@@ -38,6 +46,16 @@ public abstract class SyncWork implements Net.HttpResponseListener {
@Override
public final void handleHttpResponse(Net.HttpResponse httpResponse) {
// server response with unauthorized status code.
// We need to get new security time, store all request to queue and process them after we have security time from server
if (httpResponse.getStatus().getStatusCode() == HttpStatus.SC_UNAUTHORIZED) {
Log.err(SyncWork.class, "Request not authorized");
RestSynchronizationService.secretTime = null;
queuedRequests.add(this);
sendSecurityRequest();
return;
}
onResponse(httpResponse);
if (listener != null) {
listener.onSuccess(getUser());
......@@ -89,8 +107,68 @@ public abstract class SyncWork implements Net.HttpResponseListener {
return minutes + " min " + seconds + " sec";
}
public final void process(final RestSynchronizationService sync) {
if (RestSynchronizationService.secretTime == null) {
if (queuedRequests.isEmpty()) {
queuedRequests.add(this);
sendSecurityRequest();
} else {
queuedRequests.add(this);
}
} else {
sync.setSecurityHeaders();
send(sync);
processRequestQueue();
}
}
private void sendSecurityRequest() {
RestSynchronizationService securityRequest = new RestSynchronizationService(RestSynchronizationService.getSecurityGetUrl(), Net.HttpMethods.POST, new Net.HttpResponseListener() {
@Override
public void handleHttpResponse(Net.HttpResponse httpResponse) {
String timeString = httpResponse.getResultAsString();
if (timeString == null || timeString.isEmpty()) {
Log.err(SyncWork.class, "failed to receive security time");
return;
}
RestSynchronizationService.secretTime = new AtomicLong(Long.valueOf(timeString));
processRequestQueue();
}
@Override
public void failed(Throwable t) {
Log.err(SyncWork.class, "Security request failed", t);
}
@Override
public void cancelled() {
Log.err(SyncWork.class, "Security request cancelled");
}
});
securityRequest.send();
}
private void processRequestQueue() {
if (queuedRequests.isEmpty()) {
return;
}
Log.debug(this.getClass(), "request queue size: " + queuedRequests.size());
for (int i = 0; i < queuedRequests.size(); i++) {
SyncWork sync = queuedRequests.get(i);
queuedRequests.remove(i);
RestSynchronizationService.doSyncWork(sync);
}
}
protected RestSyncListener getListener() {
return listener;
}
public abstract String getUrl();
public abstract void send(RestSynchronizationService sync);
protected abstract void send(RestSynchronizationService sync);
//////////// Response listener class
......
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment