зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1254643 - Delete FxA device when Fennec Firefox Account is removed. r=Grisha
MozReview-Commit-ID: H4lJlXGYIBg --HG-- extra : rebase_source : f5862b9591def7ee300c6f02ff7750ead043241a
This commit is contained in:
Родитель
d1865bc01a
Коммит
ec0e5f70b8
|
@ -19,6 +19,7 @@ public interface FxAccountClient {
|
||||||
public void keys(byte[] keyFetchToken, RequestDelegate<TwoKeys> requestDelegate);
|
public void keys(byte[] keyFetchToken, RequestDelegate<TwoKeys> requestDelegate);
|
||||||
public void sign(byte[] sessionToken, ExtendedJSONObject publicKey, long certificateDurationInMilliseconds, RequestDelegate<String> requestDelegate);
|
public void sign(byte[] sessionToken, ExtendedJSONObject publicKey, long certificateDurationInMilliseconds, RequestDelegate<String> requestDelegate);
|
||||||
public void registerOrUpdateDevice(byte[] sessionToken, FxAccountDevice device, RequestDelegate<FxAccountDevice> requestDelegate);
|
public void registerOrUpdateDevice(byte[] sessionToken, FxAccountDevice device, RequestDelegate<FxAccountDevice> requestDelegate);
|
||||||
|
public void destroyDevice(byte[] sessionToken, String deviceId, RequestDelegate<ExtendedJSONObject> requestDelegate);
|
||||||
public void deviceList(byte[] sessionToken, RequestDelegate<FxAccountDevice[]> requestDelegate);
|
public void deviceList(byte[] sessionToken, RequestDelegate<FxAccountDevice[]> requestDelegate);
|
||||||
public void notifyDevices(byte[] sessionToken, List<String> deviceIds, ExtendedJSONObject payload, Long TTL, RequestDelegate<ExtendedJSONObject> requestDelegate);
|
public void notifyDevices(byte[] sessionToken, List<String> deviceIds, ExtendedJSONObject payload, Long TTL, RequestDelegate<ExtendedJSONObject> requestDelegate);
|
||||||
}
|
}
|
||||||
|
|
|
@ -822,6 +822,42 @@ public class FxAccountClient20 implements FxAccountClient {
|
||||||
post(resource, body);
|
post(resource, body);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void destroyDevice(byte[] sessionToken, String deviceId, RequestDelegate<ExtendedJSONObject> delegate) {
|
||||||
|
final byte[] tokenId = new byte[32];
|
||||||
|
final byte[] reqHMACKey = new byte[32];
|
||||||
|
final byte[] requestKey = new byte[32];
|
||||||
|
try {
|
||||||
|
HKDF.deriveMany(sessionToken, new byte[0], FxAccountUtils.KW("sessionToken"), tokenId, reqHMACKey, requestKey);
|
||||||
|
} catch (Exception e) {
|
||||||
|
invokeHandleError(delegate, e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final BaseResource resource;
|
||||||
|
final ExtendedJSONObject body = new ExtendedJSONObject();
|
||||||
|
body.put("id", deviceId);
|
||||||
|
try {
|
||||||
|
resource = getBaseResource("account/device/destroy");
|
||||||
|
} catch (URISyntaxException | UnsupportedEncodingException e) {
|
||||||
|
invokeHandleError(delegate, e);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
resource.delegate = new ResourceDelegate<ExtendedJSONObject>(resource, delegate, ResponseType.JSON_OBJECT, tokenId, reqHMACKey) {
|
||||||
|
@Override
|
||||||
|
public void handleSuccess(int status, HttpResponse response, ExtendedJSONObject body) {
|
||||||
|
try {
|
||||||
|
delegate.handleSuccess(body);
|
||||||
|
} catch (Exception e) {
|
||||||
|
delegate.handleError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
post(resource, body);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void deviceList(byte[] sessionToken, RequestDelegate<FxAccountDevice[]> delegate) {
|
public void deviceList(byte[] sessionToken, RequestDelegate<FxAccountDevice[]> delegate) {
|
||||||
final byte[] tokenId = new byte[32];
|
final byte[] tokenId = new byte[32];
|
||||||
|
|
|
@ -52,6 +52,9 @@ public class FxAccountConstants {
|
||||||
public static final String ACCOUNT_DELETED_INTENT_ACCOUNT_PROFILE = "account_deleted_intent_profile";
|
public static final String ACCOUNT_DELETED_INTENT_ACCOUNT_PROFILE = "account_deleted_intent_profile";
|
||||||
public static final String ACCOUNT_OAUTH_SERVICE_ENDPOINT_KEY = "account_oauth_service_endpoint";
|
public static final String ACCOUNT_OAUTH_SERVICE_ENDPOINT_KEY = "account_oauth_service_endpoint";
|
||||||
public static final String ACCOUNT_DELETED_INTENT_ACCOUNT_AUTH_TOKENS = "account_deleted_intent_auth_tokens";
|
public static final String ACCOUNT_DELETED_INTENT_ACCOUNT_AUTH_TOKENS = "account_deleted_intent_auth_tokens";
|
||||||
|
public static final String ACCOUNT_DELETED_INTENT_ACCOUNT_SESSION_TOKEN = "account_deleted_intent_session_token";
|
||||||
|
public static final String ACCOUNT_DELETED_INTENT_ACCOUNT_SERVER_URI = "account_deleted_intent_account_server_uri";
|
||||||
|
public static final String ACCOUNT_DELETED_INTENT_ACCOUNT_DEVICE_ID = "account_deleted_intent_account_device_id";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This action is broadcast when an Android Firefox Account's internal state
|
* This action is broadcast when an Android Firefox Account's internal state
|
||||||
|
|
|
@ -693,6 +693,15 @@ public class AndroidFxAccount {
|
||||||
intent.putExtra(FxAccountConstants.ACCOUNT_OAUTH_SERVICE_ENDPOINT_KEY, getOAuthServerURI());
|
intent.putExtra(FxAccountConstants.ACCOUNT_OAUTH_SERVICE_ENDPOINT_KEY, getOAuthServerURI());
|
||||||
// Deleted broadcasts are package-private, so there's no security risk include the tokens in the extras
|
// Deleted broadcasts are package-private, so there's no security risk include the tokens in the extras
|
||||||
intent.putExtra(FxAccountConstants.ACCOUNT_DELETED_INTENT_ACCOUNT_AUTH_TOKENS, tokens.toArray(new String[tokens.size()]));
|
intent.putExtra(FxAccountConstants.ACCOUNT_DELETED_INTENT_ACCOUNT_AUTH_TOKENS, tokens.toArray(new String[tokens.size()]));
|
||||||
|
|
||||||
|
try {
|
||||||
|
intent.putExtra(FxAccountConstants.ACCOUNT_DELETED_INTENT_ACCOUNT_SESSION_TOKEN, getSessionToken());
|
||||||
|
} catch (InvalidFxAState e) {
|
||||||
|
Logger.warn(LOG_TAG, "Could not get a session token, ignoring.", e);
|
||||||
|
}
|
||||||
|
intent.putExtra(FxAccountConstants.ACCOUNT_DELETED_INTENT_ACCOUNT_SERVER_URI, getAccountServerURI());
|
||||||
|
intent.putExtra(FxAccountConstants.ACCOUNT_DELETED_INTENT_ACCOUNT_DEVICE_ID, getDeviceId());
|
||||||
|
|
||||||
return intent;
|
return intent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -7,8 +7,11 @@ package org.mozilla.gecko.fxa.receivers;
|
||||||
import android.app.IntentService;
|
import android.app.IntentService;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
|
||||||
import org.mozilla.gecko.background.common.log.Logger;
|
import org.mozilla.gecko.background.common.log.Logger;
|
||||||
|
import org.mozilla.gecko.background.fxa.FxAccountClient20;
|
||||||
|
import org.mozilla.gecko.background.fxa.FxAccountClientException;
|
||||||
import org.mozilla.gecko.background.fxa.oauth.FxAccountAbstractClient;
|
import org.mozilla.gecko.background.fxa.oauth.FxAccountAbstractClient;
|
||||||
import org.mozilla.gecko.background.fxa.oauth.FxAccountAbstractClientException.FxAccountAbstractClientRemoteException;
|
import org.mozilla.gecko.background.fxa.oauth.FxAccountAbstractClientException.FxAccountAbstractClientRemoteException;
|
||||||
import org.mozilla.gecko.background.fxa.oauth.FxAccountOAuthClient10;
|
import org.mozilla.gecko.background.fxa.oauth.FxAccountOAuthClient10;
|
||||||
|
@ -16,10 +19,13 @@ import org.mozilla.gecko.fxa.FxAccountConstants;
|
||||||
import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount;
|
import org.mozilla.gecko.fxa.authenticator.AndroidFxAccount;
|
||||||
import org.mozilla.gecko.fxa.sync.FxAccountNotificationManager;
|
import org.mozilla.gecko.fxa.sync.FxAccountNotificationManager;
|
||||||
import org.mozilla.gecko.fxa.sync.FxAccountSyncAdapter;
|
import org.mozilla.gecko.fxa.sync.FxAccountSyncAdapter;
|
||||||
|
import org.mozilla.gecko.sync.ExtendedJSONObject;
|
||||||
import org.mozilla.gecko.sync.repositories.android.ClientsDatabase;
|
import org.mozilla.gecko.sync.repositories.android.ClientsDatabase;
|
||||||
import org.mozilla.gecko.sync.repositories.android.FennecTabsRepository;
|
import org.mozilla.gecko.sync.repositories.android.FennecTabsRepository;
|
||||||
|
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
|
import java.util.concurrent.ExecutorService;
|
||||||
|
import java.util.concurrent.Executors;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A background service to clean up after a Firefox Account is deleted.
|
* A background service to clean up after a Firefox Account is deleted.
|
||||||
|
@ -69,6 +75,8 @@ public class FxAccountDeletedService extends IntentService {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Delete current device the from FxA devices list.
|
||||||
|
deleteFxADevice(intent);
|
||||||
|
|
||||||
// Fire up gecko and unsubscribe push
|
// Fire up gecko and unsubscribe push
|
||||||
final Intent geckoIntent = new Intent();
|
final Intent geckoIntent = new Intent();
|
||||||
|
@ -76,7 +84,6 @@ public class FxAccountDeletedService extends IntentService {
|
||||||
geckoIntent.setClassName(context, "org.mozilla.gecko.GeckoService");
|
geckoIntent.setClassName(context, "org.mozilla.gecko.GeckoService");
|
||||||
geckoIntent.putExtra("category", "android-push-service");
|
geckoIntent.putExtra("category", "android-push-service");
|
||||||
geckoIntent.putExtra("data", "android-fxa-unsubscribe");
|
geckoIntent.putExtra("data", "android-fxa-unsubscribe");
|
||||||
final AndroidFxAccount fxAccount = AndroidFxAccount.fromContext(context);
|
|
||||||
geckoIntent.putExtra("org.mozilla.gecko.intent.PROFILE_NAME",
|
geckoIntent.putExtra("org.mozilla.gecko.intent.PROFILE_NAME",
|
||||||
intent.getStringExtra(FxAccountConstants.ACCOUNT_DELETED_INTENT_ACCOUNT_PROFILE));
|
intent.getStringExtra(FxAccountConstants.ACCOUNT_DELETED_INTENT_ACCOUNT_PROFILE));
|
||||||
context.startService(geckoIntent);
|
context.startService(geckoIntent);
|
||||||
|
@ -151,4 +158,38 @@ public class FxAccountDeletedService extends IntentService {
|
||||||
Logger.error(LOG_TAG, "Cached OAuth server URI is null or cached OAuth tokens are null; ignoring.");
|
Logger.error(LOG_TAG, "Cached OAuth server URI is null or cached OAuth tokens are null; ignoring.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Remove our current device from the FxA device list.
|
||||||
|
private void deleteFxADevice(Intent intent) {
|
||||||
|
final byte[] sessionToken = intent.getByteArrayExtra(FxAccountConstants.ACCOUNT_DELETED_INTENT_ACCOUNT_SESSION_TOKEN);
|
||||||
|
if (sessionToken == null) {
|
||||||
|
Logger.warn(LOG_TAG, "Empty session token, skipping FxA device destruction.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
final String deviceId = intent.getStringExtra(FxAccountConstants.ACCOUNT_DELETED_INTENT_ACCOUNT_DEVICE_ID);
|
||||||
|
if (TextUtils.isEmpty(deviceId)) {
|
||||||
|
Logger.warn(LOG_TAG, "Empty FxA device ID, skipping FxA device destruction.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ExecutorService executor = Executors.newSingleThreadExecutor(); // Not called often, it's okay to spawn another thread
|
||||||
|
final String accountServerURI = intent.getStringExtra(FxAccountConstants.ACCOUNT_DELETED_INTENT_ACCOUNT_SERVER_URI);
|
||||||
|
final FxAccountClient20 fxAccountClient = new FxAccountClient20(accountServerURI, executor);
|
||||||
|
fxAccountClient.destroyDevice(sessionToken, deviceId, new FxAccountClient20.RequestDelegate<ExtendedJSONObject>() {
|
||||||
|
@Override
|
||||||
|
public void handleError(Exception e) {
|
||||||
|
Logger.error(LOG_TAG, "Error while trying to delete the FxA device; ignoring.", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleFailure(FxAccountClientException.FxAccountClientRemoteException e) {
|
||||||
|
Logger.error(LOG_TAG, "Exception while trying to delete the FxA device; ignoring.", e);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void handleSuccess(ExtendedJSONObject result) {
|
||||||
|
Logger.info(LOG_TAG, "Successfully deleted the FxA device.");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -202,6 +202,26 @@ public class MockFxAccountClient implements FxAccountClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void destroyDevice(byte[] sessionToken, String deviceId, RequestDelegate<ExtendedJSONObject> requestDelegate) {
|
||||||
|
String email = sessionTokens.get(Utils.byte2Hex(sessionToken));
|
||||||
|
User user = users.get(email);
|
||||||
|
if (email == null || user == null) {
|
||||||
|
handleFailure(requestDelegate, HttpStatus.SC_UNAUTHORIZED, FxAccountRemoteError.INVALID_AUTHENTICATION_TOKEN, "invalid sessionToken");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!user.verified) {
|
||||||
|
handleFailure(requestDelegate, HttpStatus.SC_BAD_REQUEST, FxAccountRemoteError.ATTEMPT_TO_OPERATE_ON_AN_UNVERIFIED_ACCOUNT, "user is unverified");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(user.devices.containsKey(deviceId)) {
|
||||||
|
user.devices.remove(deviceId);
|
||||||
|
requestDelegate.handleSuccess(new ExtendedJSONObject());
|
||||||
|
} else {
|
||||||
|
handleFailure(requestDelegate, HttpStatus.SC_BAD_REQUEST, FxAccountRemoteError.UNKNOWN_DEVICE, "device is unknown");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void deviceList(byte[] sessionToken, RequestDelegate<FxAccountDevice[]> requestDelegate) {
|
public void deviceList(byte[] sessionToken, RequestDelegate<FxAccountDevice[]> requestDelegate) {
|
||||||
String email = sessionTokens.get(Utils.byte2Hex(sessionToken));
|
String email = sessionTokens.get(Utils.byte2Hex(sessionToken));
|
||||||
|
|
Загрузка…
Ссылка в новой задаче