зеркало из https://github.com/mozilla/gecko-dev.git
Bug 929066 - Handle skew in HAWK requests. r=nalexander
This commit is contained in:
Родитель
74c9eb6feb
Коммит
ac232563d5
|
@ -508,6 +508,7 @@ sync_java_files = [
|
||||||
'background/fxa/FxAccountClient20.java',
|
'background/fxa/FxAccountClient20.java',
|
||||||
'background/fxa/FxAccountClientException.java',
|
'background/fxa/FxAccountClientException.java',
|
||||||
'background/fxa/FxAccountUtils.java',
|
'background/fxa/FxAccountUtils.java',
|
||||||
|
'background/fxa/SkewHandler.java',
|
||||||
'background/healthreport/Environment.java',
|
'background/healthreport/Environment.java',
|
||||||
'background/healthreport/EnvironmentBuilder.java',
|
'background/healthreport/EnvironmentBuilder.java',
|
||||||
'background/healthreport/EnvironmentV1.java',
|
'background/healthreport/EnvironmentV1.java',
|
||||||
|
|
|
@ -150,6 +150,7 @@ public class FxAccountClient10 {
|
||||||
protected final byte[] tokenId;
|
protected final byte[] tokenId;
|
||||||
protected final byte[] reqHMACKey;
|
protected final byte[] reqHMACKey;
|
||||||
protected final boolean payload;
|
protected final boolean payload;
|
||||||
|
protected final SkewHandler skewHandler;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a delegate for an un-authenticated resource.
|
* Create a delegate for an un-authenticated resource.
|
||||||
|
@ -167,12 +168,13 @@ public class FxAccountClient10 {
|
||||||
this.reqHMACKey = reqHMACKey;
|
this.reqHMACKey = reqHMACKey;
|
||||||
this.tokenId = tokenId;
|
this.tokenId = tokenId;
|
||||||
this.payload = authenticatePayload;
|
this.payload = authenticatePayload;
|
||||||
|
this.skewHandler = SkewHandler.getSkewHandlerForResource(resource);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public AuthHeaderProvider getAuthHeaderProvider() {
|
public AuthHeaderProvider getAuthHeaderProvider() {
|
||||||
if (tokenId != null && reqHMACKey != null) {
|
if (tokenId != null && reqHMACKey != null) {
|
||||||
return new HawkAuthHeaderProvider(Utils.byte2Hex(tokenId), reqHMACKey, payload);
|
return new HawkAuthHeaderProvider(Utils.byte2Hex(tokenId), reqHMACKey, payload, skewHandler.getSkewInSeconds());
|
||||||
}
|
}
|
||||||
return super.getAuthHeaderProvider();
|
return super.getAuthHeaderProvider();
|
||||||
}
|
}
|
||||||
|
@ -182,9 +184,14 @@ public class FxAccountClient10 {
|
||||||
final int status = response.getStatusLine().getStatusCode();
|
final int status = response.getStatusLine().getStatusCode();
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case 200:
|
case 200:
|
||||||
|
skewHandler.updateSkew(response, now());
|
||||||
invokeHandleSuccess(status, response);
|
invokeHandleSuccess(status, response);
|
||||||
return;
|
return;
|
||||||
default:
|
default:
|
||||||
|
if (!skewHandler.updateSkew(response, now())) {
|
||||||
|
// If we couldn't update skew, but we got a failure, let's try clearing the skew.
|
||||||
|
skewHandler.resetSkew();
|
||||||
|
}
|
||||||
invokeHandleFailure(status, response);
|
invokeHandleFailure(status, response);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -242,6 +249,11 @@ public class FxAccountClient10 {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("static-method")
|
||||||
|
public long now() {
|
||||||
|
return System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
|
||||||
public void createAccount(final String email, final byte[] stretchedPWBytes,
|
public void createAccount(final String email, final byte[] stretchedPWBytes,
|
||||||
final String srpSalt, final String mainSalt,
|
final String srpSalt, final String mainSalt,
|
||||||
final RequestDelegate<String> delegate) {
|
final RequestDelegate<String> delegate) {
|
||||||
|
|
|
@ -0,0 +1,111 @@
|
||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
package org.mozilla.gecko.background.fxa;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
import org.mozilla.gecko.background.common.log.Logger;
|
||||||
|
import org.mozilla.gecko.sync.net.Resource;
|
||||||
|
|
||||||
|
import ch.boye.httpclientandroidlib.Header;
|
||||||
|
import ch.boye.httpclientandroidlib.HttpHeaders;
|
||||||
|
import ch.boye.httpclientandroidlib.HttpResponse;
|
||||||
|
import ch.boye.httpclientandroidlib.impl.cookie.DateParseException;
|
||||||
|
import ch.boye.httpclientandroidlib.impl.cookie.DateUtils;
|
||||||
|
|
||||||
|
public class SkewHandler {
|
||||||
|
private static final String LOG_TAG = "SkewHandler";
|
||||||
|
protected volatile long skewMillis = 0L;
|
||||||
|
protected final String hostname;
|
||||||
|
|
||||||
|
private static final HashMap<String, SkewHandler> skewHandlers = new HashMap<String, SkewHandler>();
|
||||||
|
|
||||||
|
public static SkewHandler getSkewHandlerForResource(final Resource resource) {
|
||||||
|
return getSkewHandlerForHostname(resource.getHostname());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SkewHandler getSkewHandlerFromEndpointString(final String url) throws URISyntaxException {
|
||||||
|
if (url == null) {
|
||||||
|
throw new IllegalArgumentException("url must not be null.");
|
||||||
|
}
|
||||||
|
URI u = new URI(url);
|
||||||
|
return getSkewHandlerForHostname(u.getHost());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static synchronized SkewHandler getSkewHandlerForHostname(final String hostname) {
|
||||||
|
SkewHandler handler = skewHandlers.get(hostname);
|
||||||
|
if (handler == null) {
|
||||||
|
handler = new SkewHandler(hostname);
|
||||||
|
skewHandlers.put(hostname, handler);
|
||||||
|
}
|
||||||
|
return handler;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static synchronized void clearSkewHandlers() {
|
||||||
|
skewHandlers.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public SkewHandler(final String hostname) {
|
||||||
|
this.hostname = hostname;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean updateSkewFromServerMillis(long millis, long now) {
|
||||||
|
skewMillis = millis - now;
|
||||||
|
Logger.debug(LOG_TAG, "Updated skew: " + skewMillis + "ms for hostname " + this.hostname);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean updateSkewFromHTTPDateString(String date, long now) {
|
||||||
|
try {
|
||||||
|
final long millis = DateUtils.parseDate(date).getTime();
|
||||||
|
return updateSkewFromServerMillis(millis, now);
|
||||||
|
} catch (DateParseException e) {
|
||||||
|
Logger.warn(LOG_TAG, "Unexpected: invalid Date header from " + this.hostname);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean updateSkewFromDateHeader(Header header, long now) {
|
||||||
|
String date = header.getValue();
|
||||||
|
if (null == date) {
|
||||||
|
Logger.warn(LOG_TAG, "Unexpected: null Date header from " + this.hostname);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return updateSkewFromHTTPDateString(date, now);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Update our tracked skew value to account for the local clock differing from
|
||||||
|
* the server's.
|
||||||
|
*
|
||||||
|
* @param response
|
||||||
|
* the received HTTP response.
|
||||||
|
* @param now
|
||||||
|
* the current time in milliseconds.
|
||||||
|
* @return true if the skew value was updated, false otherwise.
|
||||||
|
*/
|
||||||
|
public boolean updateSkew(HttpResponse response, long now) {
|
||||||
|
Header header = response.getFirstHeader(HttpHeaders.DATE);
|
||||||
|
if (null == header) {
|
||||||
|
Logger.warn(LOG_TAG, "Unexpected: missing Date header from " + this.hostname);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return updateSkewFromDateHeader(header, now);
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getSkewInMillis() {
|
||||||
|
return skewMillis;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getSkewInSeconds() {
|
||||||
|
return skewMillis / 1000;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void resetSkew() {
|
||||||
|
skewMillis = 0L;
|
||||||
|
}
|
||||||
|
}
|
|
@ -15,6 +15,7 @@ import org.mozilla.gecko.background.fxa.FxAccountClient10.StatusResponse;
|
||||||
import org.mozilla.gecko.background.fxa.FxAccountClient10.TwoKeys;
|
import org.mozilla.gecko.background.fxa.FxAccountClient10.TwoKeys;
|
||||||
import org.mozilla.gecko.background.fxa.FxAccountClient20;
|
import org.mozilla.gecko.background.fxa.FxAccountClient20;
|
||||||
import org.mozilla.gecko.background.fxa.FxAccountClient20.LoginResponse;
|
import org.mozilla.gecko.background.fxa.FxAccountClient20.LoginResponse;
|
||||||
|
import org.mozilla.gecko.background.fxa.SkewHandler;
|
||||||
import org.mozilla.gecko.browserid.BrowserIDKeyPair;
|
import org.mozilla.gecko.browserid.BrowserIDKeyPair;
|
||||||
import org.mozilla.gecko.browserid.JSONWebTokenUtils;
|
import org.mozilla.gecko.browserid.JSONWebTokenUtils;
|
||||||
import org.mozilla.gecko.browserid.VerifyingPublicKey;
|
import org.mozilla.gecko.browserid.VerifyingPublicKey;
|
||||||
|
@ -57,6 +58,8 @@ public class FxAccountLoginPolicy {
|
||||||
return new FxAccountClient20(serverURI, executor);
|
return new FxAccountClient20(serverURI, executor);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private SkewHandler skewHandler;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Check if this certificate is not worth generating an assertion from: for
|
* Check if this certificate is not worth generating an assertion from: for
|
||||||
* example, because it is not well-formed, or it is already expired.
|
* example, because it is not well-formed, or it is already expired.
|
||||||
|
@ -83,6 +86,10 @@ public class FxAccountLoginPolicy {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected long now() {
|
||||||
|
return System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
|
||||||
public enum AccountState {
|
public enum AccountState {
|
||||||
Invalid,
|
Invalid,
|
||||||
NeedsSessionToken,
|
NeedsSessionToken,
|
||||||
|
@ -167,6 +174,11 @@ public class FxAccountLoginPolicy {
|
||||||
return stages;
|
return stages;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void login(final String audience, final FxAccountLoginDelegate delegate, final SkewHandler skewHandler) {
|
||||||
|
this.skewHandler = skewHandler;
|
||||||
|
this.login(audience, delegate);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Do as much of a Firefox Account login dance as possible.
|
* Do as much of a Firefox Account login dance as possible.
|
||||||
* <p>
|
* <p>
|
||||||
|
@ -275,6 +287,10 @@ public class FxAccountLoginPolicy {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleFailure(int status, HttpResponse response) {
|
public void handleFailure(int status, HttpResponse response) {
|
||||||
|
if (skewHandler != null) {
|
||||||
|
skewHandler.updateSkew(response, now());
|
||||||
|
}
|
||||||
|
|
||||||
if (status != 401) {
|
if (status != 401) {
|
||||||
delegate.handleError(new FxAccountLoginException(new HTTPFailureException(new SyncStorageResponse(response))));
|
delegate.handleError(new FxAccountLoginException(new HTTPFailureException(new SyncStorageResponse(response))));
|
||||||
return;
|
return;
|
||||||
|
@ -320,6 +336,10 @@ public class FxAccountLoginPolicy {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleFailure(int status, HttpResponse response) {
|
public void handleFailure(int status, HttpResponse response) {
|
||||||
|
if (skewHandler != null) {
|
||||||
|
skewHandler.updateSkew(response, now());
|
||||||
|
}
|
||||||
|
|
||||||
if (status != 401) {
|
if (status != 401) {
|
||||||
delegate.handleError(new FxAccountLoginException(new HTTPFailureException(new SyncStorageResponse(response))));
|
delegate.handleError(new FxAccountLoginException(new HTTPFailureException(new SyncStorageResponse(response))));
|
||||||
return;
|
return;
|
||||||
|
@ -379,6 +399,10 @@ public class FxAccountLoginPolicy {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleFailure(int status, HttpResponse response) {
|
public void handleFailure(int status, HttpResponse response) {
|
||||||
|
if (skewHandler != null) {
|
||||||
|
skewHandler.updateSkew(response, now());
|
||||||
|
}
|
||||||
|
|
||||||
if (status != 401) {
|
if (status != 401) {
|
||||||
delegate.handleError(new FxAccountLoginException(new HTTPFailureException(new SyncStorageResponse(response))));
|
delegate.handleError(new FxAccountLoginException(new HTTPFailureException(new SyncStorageResponse(response))));
|
||||||
return;
|
return;
|
||||||
|
@ -426,6 +450,10 @@ public class FxAccountLoginPolicy {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleFailure(int status, HttpResponse response) {
|
public void handleFailure(int status, HttpResponse response) {
|
||||||
|
if (skewHandler != null) {
|
||||||
|
skewHandler.updateSkew(response, now());
|
||||||
|
}
|
||||||
|
|
||||||
if (status != 401) {
|
if (status != 401) {
|
||||||
delegate.handleError(new FxAccountLoginException(new HTTPFailureException(new SyncStorageResponse(response))));
|
delegate.handleError(new FxAccountLoginException(new HTTPFailureException(new SyncStorageResponse(response))));
|
||||||
return;
|
return;
|
||||||
|
@ -473,6 +501,10 @@ public class FxAccountLoginPolicy {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleFailure(int status, HttpResponse response) {
|
public void handleFailure(int status, HttpResponse response) {
|
||||||
|
if (skewHandler != null) {
|
||||||
|
skewHandler.updateSkew(response, now());
|
||||||
|
}
|
||||||
|
|
||||||
if (status != 401) {
|
if (status != 401) {
|
||||||
delegate.handleError(new FxAccountLoginException(new HTTPFailureException(new SyncStorageResponse(response))));
|
delegate.handleError(new FxAccountLoginException(new HTTPFailureException(new SyncStorageResponse(response))));
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -11,6 +11,7 @@ import java.util.concurrent.Executors;
|
||||||
|
|
||||||
import org.mozilla.gecko.background.common.log.Logger;
|
import org.mozilla.gecko.background.common.log.Logger;
|
||||||
import org.mozilla.gecko.background.fxa.FxAccountUtils;
|
import org.mozilla.gecko.background.fxa.FxAccountUtils;
|
||||||
|
import org.mozilla.gecko.background.fxa.SkewHandler;
|
||||||
import org.mozilla.gecko.browserid.verifier.BrowserIDRemoteVerifierClient;
|
import org.mozilla.gecko.browserid.verifier.BrowserIDRemoteVerifierClient;
|
||||||
import org.mozilla.gecko.browserid.verifier.BrowserIDVerifierDelegate;
|
import org.mozilla.gecko.browserid.verifier.BrowserIDVerifierDelegate;
|
||||||
import org.mozilla.gecko.fxa.FxAccountConstants;
|
import org.mozilla.gecko.fxa.FxAccountConstants;
|
||||||
|
@ -188,8 +189,18 @@ public class FxAccountSyncAdapter extends AbstractThreadedSyncAdapter {
|
||||||
FxAccountGlobalSession globalSession = null;
|
FxAccountGlobalSession globalSession = null;
|
||||||
try {
|
try {
|
||||||
ClientsDataDelegate clientsDataDelegate = new SharedPreferencesClientsDataDelegate(sharedPrefs);
|
ClientsDataDelegate clientsDataDelegate = new SharedPreferencesClientsDataDelegate(sharedPrefs);
|
||||||
final KeyBundle syncKeyBundle = FxAccountUtils.generateSyncKeyBundle(fxAccount.getKb()); // TODO Document this choice for deriving from kB.
|
|
||||||
AuthHeaderProvider authHeaderProvider = new HawkAuthHeaderProvider(token.id, token.key.getBytes("UTF-8"), false);
|
// TODO Document this choice for deriving from kB.
|
||||||
|
final KeyBundle syncKeyBundle = FxAccountUtils.generateSyncKeyBundle(fxAccount.getKb());
|
||||||
|
|
||||||
|
// We compute skew over time using SkewHandler. This yields an unchanging
|
||||||
|
// skew adjustment that the HawkAuthHeaderProvider uses to adjust its
|
||||||
|
// timestamps. Eventually we might want this to adapt within the scope of a
|
||||||
|
// global session.
|
||||||
|
final SkewHandler tokenServerSkewHandler = SkewHandler.getSkewHandlerFromEndpointString(token.endpoint);
|
||||||
|
final long tokenServerSkew = tokenServerSkewHandler.getSkewInSeconds();
|
||||||
|
AuthHeaderProvider authHeaderProvider = new HawkAuthHeaderProvider(token.id, token.key.getBytes("UTF-8"), false, tokenServerSkew);
|
||||||
|
|
||||||
globalSession = new FxAccountGlobalSession(token.endpoint, token.uid, authHeaderProvider, FxAccountConstants.PREFS_PATH, syncKeyBundle, callback, getContext(), extras, clientsDataDelegate);
|
globalSession = new FxAccountGlobalSession(token.endpoint, token.uid, authHeaderProvider, FxAccountConstants.PREFS_PATH, syncKeyBundle, callback, getContext(), extras, clientsDataDelegate);
|
||||||
globalSession.start();
|
globalSession.start();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
|
|
@ -69,7 +69,7 @@ public class BaseResource implements Resource {
|
||||||
|
|
||||||
private static final String LOG_TAG = "BaseResource";
|
private static final String LOG_TAG = "BaseResource";
|
||||||
|
|
||||||
protected URI uri;
|
protected final URI uri;
|
||||||
protected BasicHttpContext context;
|
protected BasicHttpContext context;
|
||||||
protected DefaultHttpClient client;
|
protected DefaultHttpClient client;
|
||||||
public ResourceDelegate delegate;
|
public ResourceDelegate delegate;
|
||||||
|
@ -101,6 +101,7 @@ public class BaseResource implements Resource {
|
||||||
this.uri = new URI(uri.getScheme(), uri.getUserInfo(), ANDROID_LOOPBACK_IP, uri.getPort(), uri.getPath(), uri.getQuery(), uri.getFragment());
|
this.uri = new URI(uri.getScheme(), uri.getUserInfo(), ANDROID_LOOPBACK_IP, uri.getPort(), uri.getPath(), uri.getQuery(), uri.getFragment());
|
||||||
} catch (URISyntaxException e) {
|
} catch (URISyntaxException e) {
|
||||||
Logger.error(LOG_TAG, "Got error rewriting URI for Android emulator.", e);
|
Logger.error(LOG_TAG, "Got error rewriting URI for Android emulator.", e);
|
||||||
|
throw new IllegalArgumentException("Invalid URI", e);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
this.uri = uri;
|
this.uri = uri;
|
||||||
|
@ -121,10 +122,21 @@ public class BaseResource implements Resource {
|
||||||
httpResponseObserver = new WeakReference<HttpResponseObserver>(newHttpResponseObserver);
|
httpResponseObserver = new WeakReference<HttpResponseObserver>(newHttpResponseObserver);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
public URI getURI() {
|
public URI getURI() {
|
||||||
return this.uri;
|
return this.uri;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getURIString() {
|
||||||
|
return this.uri.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getHostname() {
|
||||||
|
return this.getURI().getHost();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This shuts up HttpClient, which will otherwise debug log about there
|
* This shuts up HttpClient, which will otherwise debug log about there
|
||||||
* being no auth cache in the context.
|
* being no auth cache in the context.
|
||||||
|
|
|
@ -12,7 +12,7 @@ import ch.boye.httpclientandroidlib.protocol.BasicHttpContext;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An <code>AuthHeaderProvider</code> that returns an Authorization header for
|
* An <code>AuthHeaderProvider</code> that returns an Authorization header for
|
||||||
* Browser-ID assertions in the format expected by a Mozilla Services Token
|
* BrowserID assertions in the format expected by a Mozilla Services Token
|
||||||
* Server.
|
* Server.
|
||||||
* <p>
|
* <p>
|
||||||
* See <a href="http://docs.services.mozilla.com/token/apis.html">http://docs.services.mozilla.com/token/apis.html</a>.
|
* See <a href="http://docs.services.mozilla.com/token/apis.html">http://docs.services.mozilla.com/token/apis.html</a>.
|
||||||
|
|
|
@ -48,6 +48,7 @@ public class HawkAuthHeaderProvider implements AuthHeaderProvider {
|
||||||
protected final String id;
|
protected final String id;
|
||||||
protected final byte[] key;
|
protected final byte[] key;
|
||||||
protected final boolean includePayloadHash;
|
protected final boolean includePayloadHash;
|
||||||
|
protected final long skewSeconds;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a Hawk Authorization header provider.
|
* Create a Hawk Authorization header provider.
|
||||||
|
@ -63,8 +64,12 @@ public class HawkAuthHeaderProvider implements AuthHeaderProvider {
|
||||||
* @param includePayloadHash
|
* @param includePayloadHash
|
||||||
* true if message integrity hash should be included in signed
|
* true if message integrity hash should be included in signed
|
||||||
* request header. See <a href="https://github.com/hueniverse/hawk#payload-validation">https://github.com/hueniverse/hawk#payload-validation</a>.
|
* request header. See <a href="https://github.com/hueniverse/hawk#payload-validation">https://github.com/hueniverse/hawk#payload-validation</a>.
|
||||||
|
*
|
||||||
|
* @param skewSeconds
|
||||||
|
* a number of seconds by which to skew the current time when
|
||||||
|
* computing a header.
|
||||||
*/
|
*/
|
||||||
public HawkAuthHeaderProvider(String id, byte[] key, boolean includePayloadHash) {
|
public HawkAuthHeaderProvider(String id, byte[] key, boolean includePayloadHash, long skewSeconds) {
|
||||||
if (id == null) {
|
if (id == null) {
|
||||||
throw new IllegalArgumentException("id must not be null");
|
throw new IllegalArgumentException("id must not be null");
|
||||||
}
|
}
|
||||||
|
@ -74,11 +79,33 @@ public class HawkAuthHeaderProvider implements AuthHeaderProvider {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.key = key;
|
this.key = key;
|
||||||
this.includePayloadHash = includePayloadHash;
|
this.includePayloadHash = includePayloadHash;
|
||||||
|
this.skewSeconds = skewSeconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
public HawkAuthHeaderProvider(String id, byte[] key, boolean includePayloadHash) {
|
||||||
|
this(id, key, includePayloadHash, 0L);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the current time in milliseconds.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("static-method")
|
||||||
|
protected long now() {
|
||||||
|
return System.currentTimeMillis();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the current time in seconds, adjusted for skew. This should
|
||||||
|
* approximate the server's timestamp.
|
||||||
|
*/
|
||||||
|
protected long getTimestampSeconds() {
|
||||||
|
return (now() / 1000) + skewSeconds;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Header getAuthHeader(HttpRequestBase request, BasicHttpContext context, DefaultHttpClient client) throws GeneralSecurityException {
|
public Header getAuthHeader(HttpRequestBase request, BasicHttpContext context, DefaultHttpClient client) throws GeneralSecurityException {
|
||||||
long timestamp = System.currentTimeMillis() / 1000;
|
long timestamp = getTimestampSeconds();
|
||||||
String nonce = Base64.encodeBase64String(Utils.generateRandomBytes(NONCE_LENGTH_IN_BYTES));
|
String nonce = Base64.encodeBase64String(Utils.generateRandomBytes(NONCE_LENGTH_IN_BYTES));
|
||||||
String extra = "";
|
String extra = "";
|
||||||
|
|
||||||
|
|
|
@ -4,9 +4,14 @@
|
||||||
|
|
||||||
package org.mozilla.gecko.sync.net;
|
package org.mozilla.gecko.sync.net;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
|
||||||
import ch.boye.httpclientandroidlib.HttpEntity;
|
import ch.boye.httpclientandroidlib.HttpEntity;
|
||||||
|
|
||||||
public interface Resource {
|
public interface Resource {
|
||||||
|
public abstract URI getURI();
|
||||||
|
public abstract String getURIString();
|
||||||
|
public abstract String getHostname();
|
||||||
public abstract void get();
|
public abstract void get();
|
||||||
public abstract void delete();
|
public abstract void delete();
|
||||||
public abstract void post(HttpEntity body);
|
public abstract void post(HttpEntity body);
|
||||||
|
|
|
@ -78,6 +78,21 @@ public class SyncStorageRequest implements Resource {
|
||||||
this.resource.delegate = this.resourceDelegate;
|
this.resource.delegate = this.resourceDelegate;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public URI getURI() {
|
||||||
|
return this.resource.getURI();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getURIString() {
|
||||||
|
return this.resource.getURIString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getHostname() {
|
||||||
|
return this.resource.getHostname();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A ResourceDelegate that mediates between Resource-level notifications and the SyncStorageRequest.
|
* A ResourceDelegate that mediates between Resource-level notifications and the SyncStorageRequest.
|
||||||
*/
|
*/
|
||||||
|
|
Загрузка…
Ссылка в новой задаче