mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1171253 - enable FxA profile image in Sync preferences pane. r=zaach
This commit is contained in:
parent
771a015604
commit
6a05686b86
@ -1829,6 +1829,9 @@ pref("identity.fxaccounts.remote.profile.uri", "https://profile.accounts.firefox
|
||||
// The remote URL of the FxA OAuth Server
|
||||
pref("identity.fxaccounts.remote.oauth.uri", "https://oauth.accounts.firefox.com/v1");
|
||||
|
||||
// Whether we display profile images in the UI or not.
|
||||
pref("identity.fxaccounts.profile_image.enabled", true);
|
||||
|
||||
// Migrate any existing Firefox Account data from the default profile to the
|
||||
// Developer Edition profile.
|
||||
#ifdef MOZ_DEV_EDITION
|
||||
|
@ -111,12 +111,6 @@ AccountState.prototype = {
|
||||
this.signedInUser = null;
|
||||
this.uid = null;
|
||||
this.fxaInternal = null;
|
||||
this.initProfilePromise = null;
|
||||
|
||||
if (this.profile) {
|
||||
this.profile.tearDown();
|
||||
this.profile = null;
|
||||
}
|
||||
},
|
||||
|
||||
// Clobber all cached data and write that empty data to storage.
|
||||
@ -294,41 +288,6 @@ AccountState.prototype = {
|
||||
return d.promise.then(result => this.resolve(result));
|
||||
},
|
||||
|
||||
// Get the account's profile image URL from the profile server
|
||||
getProfile: function () {
|
||||
return this.initProfile()
|
||||
.then(() => this.profile.getProfile());
|
||||
},
|
||||
|
||||
// Instantiate a FxAccountsProfile with a fresh OAuth token if needed
|
||||
initProfile: function () {
|
||||
|
||||
let profileServerUrl = Services.urlFormatter.formatURLPref("identity.fxaccounts.remote.profile.uri");
|
||||
|
||||
let oAuthOptions = {
|
||||
scope: "profile"
|
||||
};
|
||||
|
||||
if (this.initProfilePromise) {
|
||||
return this.initProfilePromise;
|
||||
}
|
||||
|
||||
this.initProfilePromise = this.fxaInternal.getOAuthToken(oAuthOptions)
|
||||
.then(token => {
|
||||
this.profile = new FxAccountsProfile(this, {
|
||||
profileServerUrl: profileServerUrl,
|
||||
token: token
|
||||
});
|
||||
this.initProfilePromise = null;
|
||||
})
|
||||
.then(null, err => {
|
||||
this.initProfilePromise = null;
|
||||
throw err;
|
||||
});
|
||||
|
||||
return this.initProfilePromise;
|
||||
},
|
||||
|
||||
resolve: function(result) {
|
||||
if (!this.isCurrent) {
|
||||
log.info("An accountState promise was resolved, but was actually rejected" +
|
||||
@ -594,6 +553,19 @@ FxAccountsInternal.prototype = {
|
||||
return this._fxAccountsClient;
|
||||
},
|
||||
|
||||
// The profile object used to fetch the actual user profile.
|
||||
_profile: null,
|
||||
get profile() {
|
||||
if (!this._profile) {
|
||||
let profileServerUrl = Services.urlFormatter.formatURLPref("identity.fxaccounts.remote.profile.uri");
|
||||
this._profile = new FxAccountsProfile({
|
||||
fxa: this,
|
||||
profileServerUrl: profileServerUrl,
|
||||
});
|
||||
}
|
||||
return this._profile;
|
||||
},
|
||||
|
||||
/**
|
||||
* Return the current time in milliseconds as an integer. Allows tests to
|
||||
* manipulate the date to simulate certificate expiration.
|
||||
@ -849,6 +821,10 @@ FxAccountsInternal.prototype = {
|
||||
*/
|
||||
_signOutLocal: function signOutLocal() {
|
||||
let currentAccountState = this.currentAccountState;
|
||||
if (this._profile) {
|
||||
this._profile.tearDown();
|
||||
this._profile = null;
|
||||
}
|
||||
return currentAccountState.signOut().then(() => {
|
||||
this.abortExistingFlow(); // this resets this.currentAccountState.
|
||||
});
|
||||
@ -1430,17 +1406,17 @@ FxAccountsInternal.prototype = {
|
||||
* UNKNOWN_ERROR
|
||||
*/
|
||||
getSignedInUserProfile: function () {
|
||||
let accountState = this.currentAccountState;
|
||||
return accountState.getProfile()
|
||||
.then((profileData) => {
|
||||
let currentState = this.currentAccountState;
|
||||
return this.profile.getProfile().then(
|
||||
profileData => {
|
||||
let profile = JSON.parse(JSON.stringify(profileData));
|
||||
return accountState.resolve(profile);
|
||||
return currentState.resolve(profile);
|
||||
},
|
||||
(error) => {
|
||||
error => {
|
||||
log.error("Could not retrieve profile data", error);
|
||||
return accountState.reject(error);
|
||||
})
|
||||
.then(null, err => Promise.reject(this._errorToErrorClass(err)));
|
||||
return currentState.reject(error);
|
||||
}
|
||||
).catch(err => Promise.reject(this._errorToErrorClass(err)));
|
||||
},
|
||||
};
|
||||
|
||||
|
@ -17,9 +17,9 @@ this.EXPORTED_SYMBOLS = ["FxAccountsProfile"];
|
||||
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Log.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/FxAccountsCommon.js");
|
||||
Cu.import("resource://gre/modules/FxAccounts.jsm");
|
||||
|
||||
XPCOMUtils.defineLazyModuleGetter(this, "FxAccountsProfileClient",
|
||||
"resource://gre/modules/FxAccountsProfileClient.jsm");
|
||||
@ -71,11 +71,12 @@ function hasChanged(oldData, newData) {
|
||||
return !deepEqual(oldData, newData);
|
||||
}
|
||||
|
||||
this.FxAccountsProfile = function (accountState, options = {}) {
|
||||
this.currentAccountState = accountState;
|
||||
this.FxAccountsProfile = function (options = {}) {
|
||||
this._cachedProfile = null;
|
||||
this.fxa = options.fxa || fxAccounts;
|
||||
this.client = options.profileClient || new FxAccountsProfileClient({
|
||||
fxa: this.fxa,
|
||||
serverURL: options.profileServerUrl,
|
||||
token: options.token
|
||||
});
|
||||
|
||||
// for testing
|
||||
@ -87,14 +88,15 @@ this.FxAccountsProfile = function (accountState, options = {}) {
|
||||
this.FxAccountsProfile.prototype = {
|
||||
|
||||
tearDown: function () {
|
||||
this.currentAccountState = null;
|
||||
this.fxa = null;
|
||||
this.client = null;
|
||||
this._cachedProfile = null;
|
||||
},
|
||||
|
||||
_getCachedProfile: function () {
|
||||
let currentState = this.currentAccountState;
|
||||
return currentState.getUserAccountData()
|
||||
.then(cachedData => cachedData.profile);
|
||||
// The cached profile will end up back in the generic accountData
|
||||
// once bug 1157529 is fixed.
|
||||
return Promise.resolve(this._cachedProfile);
|
||||
},
|
||||
|
||||
_notifyProfileChange: function (uid) {
|
||||
@ -104,18 +106,16 @@ this.FxAccountsProfile.prototype = {
|
||||
// Cache fetched data if it is different from what's in the cache.
|
||||
// Send out a notification if it has changed so that UI can update.
|
||||
_cacheProfile: function (profileData) {
|
||||
let currentState = this.currentAccountState;
|
||||
if (!currentState) {
|
||||
return;
|
||||
if (!hasChanged(this._cachedProfile, profileData)) {
|
||||
log.debug("fetched profile matches cached copy");
|
||||
return Promise.resolve(null); // indicates no change (but only tests care)
|
||||
}
|
||||
return currentState.getUserAccountData()
|
||||
.then(data => {
|
||||
if (!hasChanged(data.profile, profileData)) {
|
||||
return;
|
||||
}
|
||||
data.profile = profileData;
|
||||
return currentState.setUserAccountData(data)
|
||||
.then(() => this._notifyProfileChange(data.uid));
|
||||
this._cachedProfile = profileData;
|
||||
return this.fxa.getSignedInUser()
|
||||
.then(userData => {
|
||||
log.debug("notifying profile changed for user ${uid}", userData);
|
||||
this._notifyProfileChange(userData.uid);
|
||||
return profileData;
|
||||
});
|
||||
},
|
||||
|
||||
@ -133,7 +133,11 @@ this.FxAccountsProfile.prototype = {
|
||||
return this._getCachedProfile()
|
||||
.then(cachedProfile => {
|
||||
if (cachedProfile) {
|
||||
this._fetchAndCacheProfile();
|
||||
// Note that _fetchAndCacheProfile isn't returned, so continues
|
||||
// in the background.
|
||||
this._fetchAndCacheProfile().catch(err => {
|
||||
log.error("Background refresh of profile failed", err);
|
||||
});
|
||||
return cachedProfile;
|
||||
}
|
||||
return this._fetchAndCacheProfile();
|
||||
|
@ -5,6 +5,7 @@
|
||||
/**
|
||||
* A client to fetch profile information for a Firefox Account.
|
||||
*/
|
||||
"use strict;"
|
||||
|
||||
this.EXPORTED_SYMBOLS = ["FxAccountsProfileClient", "FxAccountsProfileClientError"];
|
||||
|
||||
@ -13,6 +14,8 @@ const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
|
||||
Cu.import("resource://gre/modules/Promise.jsm");
|
||||
Cu.import("resource://gre/modules/Log.jsm");
|
||||
Cu.import("resource://gre/modules/FxAccountsCommon.js");
|
||||
Cu.import("resource://gre/modules/FxAccounts.jsm");
|
||||
Cu.import("resource://gre/modules/Task.jsm");
|
||||
Cu.import("resource://services-common/rest.js");
|
||||
|
||||
Cu.importGlobalProperties(["URL"]);
|
||||
@ -29,16 +32,27 @@ Cu.importGlobalProperties(["URL"]);
|
||||
* @constructor
|
||||
*/
|
||||
this.FxAccountsProfileClient = function(options) {
|
||||
if (!options || !options.serverURL || !options.token) {
|
||||
throw new Error("Missing 'serverURL' or 'token' configuration option");
|
||||
if (!options || !options.serverURL) {
|
||||
throw new Error("Missing 'serverURL' configuration option");
|
||||
}
|
||||
|
||||
this.fxa = options.fxa || fxAccounts;
|
||||
// This is a work-around for loop that manages its own oauth tokens.
|
||||
// * If |token| is in options we use it and don't attempt any token refresh
|
||||
// on 401. This is for loop.
|
||||
// * If |token| doesn't exist we will fetch our own token. This is for the
|
||||
// normal FxAccounts methods for obtaining the profile.
|
||||
// We should nuke all |this.token| support once loop moves closer to FxAccounts.
|
||||
this.token = options.token;
|
||||
|
||||
try {
|
||||
this.serverURL = new URL(options.serverURL);
|
||||
} catch (e) {
|
||||
throw new Error("Invalid 'serverURL'");
|
||||
}
|
||||
this.token = options.token;
|
||||
this.oauthOptions = {
|
||||
scope: "profile",
|
||||
};
|
||||
log.debug("FxAccountsProfileClient: Initialized");
|
||||
};
|
||||
|
||||
@ -49,19 +63,13 @@ this.FxAccountsProfileClient.prototype = {
|
||||
*/
|
||||
serverURL: null,
|
||||
|
||||
/**
|
||||
* {String}
|
||||
* Profile server bearer OAuth token.
|
||||
*/
|
||||
token: null,
|
||||
|
||||
/**
|
||||
* Interface for making remote requests.
|
||||
*/
|
||||
_Request: RESTRequest,
|
||||
|
||||
/**
|
||||
* Remote request helper
|
||||
* Remote request helper which abstracts authentication away.
|
||||
*
|
||||
* @param {String} path
|
||||
* Profile server path, i.e "/profile".
|
||||
@ -72,13 +80,55 @@ this.FxAccountsProfileClient.prototype = {
|
||||
* Rejects: {FxAccountsProfileClientError} Profile client error.
|
||||
* @private
|
||||
*/
|
||||
_createRequest: function(path, method = "GET") {
|
||||
_createRequest: Task.async(function* (path, method = "GET") {
|
||||
let token = this.token;
|
||||
if (!token) {
|
||||
// tokens are cached, so getting them each request is cheap.
|
||||
token = yield this.fxa.getOAuthToken(this.oauthOptions);
|
||||
}
|
||||
try {
|
||||
return (yield this._rawRequest(path, method, token));
|
||||
} catch (ex if ex instanceof FxAccountsProfileClientError && ex.code == 401) {
|
||||
// If this object was instantiated with a token then we don't refresh it.
|
||||
if (this.token) {
|
||||
throw ex;
|
||||
}
|
||||
// it's an auth error - assume our token expired and retry.
|
||||
log.info("Fetching the profile returned a 401 - revoking our token and retrying");
|
||||
yield this.fxa.removeCachedOAuthToken({token});
|
||||
token = yield this.fxa.getOAuthToken(this.oauthOptions);
|
||||
// and try with the new token - if that also fails then we fail after
|
||||
// revoking the token.
|
||||
try {
|
||||
return (yield this._rawRequest(path, method, token));
|
||||
} catch (ex if ex instanceof FxAccountsProfileClientError && ex.code == 401) {
|
||||
log.info("Retry fetching the profile still returned a 401 - revoking our token and failing");
|
||||
yield this.fxa.removeCachedOAuthToken({token});
|
||||
throw ex;
|
||||
}
|
||||
}
|
||||
}),
|
||||
|
||||
/**
|
||||
* Remote "raw" request helper - doesn't handle auth errors and tokens.
|
||||
*
|
||||
* @param {String} path
|
||||
* Profile server path, i.e "/profile".
|
||||
* @param {String} method
|
||||
* Type of request, i.e "GET".
|
||||
* @param {String} token
|
||||
* @return Promise
|
||||
* Resolves: {Object} Successful response from the Profile server.
|
||||
* Rejects: {FxAccountsProfileClientError} Profile client error.
|
||||
* @private
|
||||
*/
|
||||
_rawRequest: function(path, method, token) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let profileDataUrl = this.serverURL + path;
|
||||
let request = new this._Request(profileDataUrl);
|
||||
method = method.toUpperCase();
|
||||
|
||||
request.setHeader("Authorization", "Bearer " + this.token);
|
||||
request.setHeader("Authorization", "Bearer " + token);
|
||||
request.setHeader("Accept", "application/json");
|
||||
|
||||
request.onComplete = function (error) {
|
||||
@ -106,7 +156,12 @@ this.FxAccountsProfileClient.prototype = {
|
||||
if (request.response.success) {
|
||||
return resolve(body);
|
||||
} else {
|
||||
return reject(new FxAccountsProfileClientError(body));
|
||||
return reject(new FxAccountsProfileClientError({
|
||||
error: body.error || ERROR_UNKNOWN,
|
||||
errno: body.errno || ERRNO_UNKNOWN_ERROR,
|
||||
code: request.response.status,
|
||||
message: body.message || body,
|
||||
}));
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -960,29 +960,7 @@ add_test(function test_getOAuthToken_unknown_error() {
|
||||
});
|
||||
});
|
||||
|
||||
add_test(function test_accountState_initProfile() {
|
||||
let fxa = new MockFxAccounts();
|
||||
let alice = getTestUser("alice");
|
||||
alice.verified = true;
|
||||
|
||||
fxa.internal.getOAuthToken = function (opts) {
|
||||
return Promise.resolve("token");
|
||||
};
|
||||
|
||||
fxa.setSignedInUser(alice).then(() => {
|
||||
let accountState = fxa.internal.currentAccountState;
|
||||
|
||||
accountState.initProfile(options)
|
||||
.then(result => {
|
||||
do_check_true(!!accountState.profile);
|
||||
run_next_test();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
add_test(function test_accountState_getProfile() {
|
||||
let fxa = new MockFxAccounts();
|
||||
add_test(function test_getSignedInUserProfile() {
|
||||
let alice = getTestUser("alice");
|
||||
alice.verified = true;
|
||||
|
||||
@ -991,40 +969,18 @@ add_test(function test_accountState_getProfile() {
|
||||
return Promise.resolve({ avatar: "image" });
|
||||
}
|
||||
};
|
||||
let fxa = new FxAccounts({
|
||||
_profile: mockProfile,
|
||||
});
|
||||
|
||||
fxa.setSignedInUser(alice).then(() => {
|
||||
let accountState = fxa.internal.currentAccountState;
|
||||
accountState.profile = mockProfile;
|
||||
accountState.initProfilePromise = new Promise((resolve, reject) => resolve(mockProfile));
|
||||
|
||||
accountState.getProfile()
|
||||
fxa.getSignedInUserProfile()
|
||||
.then(result => {
|
||||
do_check_true(!!result);
|
||||
do_check_eq(result.avatar, "image");
|
||||
run_next_test();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
add_test(function test_getSignedInUserProfile_ok() {
|
||||
let fxa = new MockFxAccounts();
|
||||
let alice = getTestUser("alice");
|
||||
alice.verified = true;
|
||||
|
||||
fxa.setSignedInUser(alice).then(() => {
|
||||
let accountState = fxa.internal.currentAccountState;
|
||||
accountState.getProfile = function () {
|
||||
return Promise.resolve({ avatar: "image" });
|
||||
};
|
||||
|
||||
fxa.getSignedInUserProfile()
|
||||
.then(result => {
|
||||
do_check_eq(result.avatar, "image");
|
||||
run_next_test();
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
add_test(function test_getSignedInUserProfile_error_uses_account_data() {
|
||||
@ -1036,19 +992,26 @@ add_test(function test_getSignedInUserProfile_error_uses_account_data() {
|
||||
return Promise.resolve({ email: "foo@bar.com" });
|
||||
};
|
||||
|
||||
let teardownCalled = false;
|
||||
fxa.setSignedInUser(alice).then(() => {
|
||||
let accountState = fxa.internal.currentAccountState;
|
||||
accountState.getProfile = function () {
|
||||
return Promise.reject("boom");
|
||||
fxa.internal._profile = {
|
||||
getProfile: function () {
|
||||
return Promise.reject("boom");
|
||||
},
|
||||
tearDown: function() {
|
||||
teardownCalled = true;
|
||||
}
|
||||
};
|
||||
|
||||
fxa.getSignedInUserProfile()
|
||||
.catch(error => {
|
||||
do_check_eq(error.message, "UNKNOWN_ERROR");
|
||||
fxa.signOut().then(run_next_test);
|
||||
do_check_eq(error.message, "UNKNOWN_ERROR");
|
||||
fxa.signOut().then(() => {
|
||||
do_check_true(teardownCalled);
|
||||
run_next_test();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
add_test(function test_getSignedInUserProfile_unverified_account() {
|
||||
@ -1056,8 +1019,6 @@ add_test(function test_getSignedInUserProfile_unverified_account() {
|
||||
let alice = getTestUser("alice");
|
||||
|
||||
fxa.setSignedInUser(alice).then(() => {
|
||||
let accountState = fxa.internal.currentAccountState;
|
||||
|
||||
fxa.getSignedInUserProfile()
|
||||
.catch(error => {
|
||||
do_check_eq(error.message, "UNVERIFIED_ACCOUNT");
|
||||
|
@ -11,12 +11,6 @@ Cu.import("resource://gre/modules/FxAccountsProfile.jsm");
|
||||
const URL_STRING = "https://example.com";
|
||||
Services.prefs.setCharPref("identity.fxaccounts.settings.uri", "https://example.com/settings");
|
||||
|
||||
const PROFILE_CLIENT_OPTIONS = {
|
||||
token: "123ABC",
|
||||
serverURL: "http://127.0.0.1:1111/v1",
|
||||
profileServerUrl: "http://127.0.0.1:1111/v1"
|
||||
};
|
||||
|
||||
const STATUS_SUCCESS = 200;
|
||||
|
||||
/**
|
||||
@ -58,35 +52,53 @@ let mockResponseError = function (error) {
|
||||
};
|
||||
};
|
||||
|
||||
let mockClient = function () {
|
||||
let client = new FxAccountsProfileClient(PROFILE_CLIENT_OPTIONS);
|
||||
return client;
|
||||
let mockClient = function (fxa) {
|
||||
let options = {
|
||||
serverURL: "http://127.0.0.1:1111/v1",
|
||||
fxa: fxa,
|
||||
}
|
||||
return new FxAccountsProfileClient(options);
|
||||
};
|
||||
|
||||
const ACCOUNT_DATA = {
|
||||
uid: "abc123"
|
||||
};
|
||||
|
||||
function AccountData () {
|
||||
function FxaMock() {
|
||||
}
|
||||
AccountData.prototype = {
|
||||
getUserAccountData: function () {
|
||||
FxaMock.prototype = {
|
||||
currentAccountState: {
|
||||
profile: null,
|
||||
get isCurrent() true,
|
||||
},
|
||||
|
||||
getSignedInUser: function () {
|
||||
return Promise.resolve(ACCOUNT_DATA);
|
||||
}
|
||||
};
|
||||
|
||||
let mockAccountData = function () {
|
||||
return new AccountData();
|
||||
let mockFxa = function() {
|
||||
return new FxaMock();
|
||||
};
|
||||
|
||||
function CreateFxAccountsProfile(fxa = null, client = null) {
|
||||
if (!fxa) {
|
||||
fxa = mockFxa();
|
||||
}
|
||||
let options = {
|
||||
fxa: fxa,
|
||||
profileServerUrl: "http://127.0.0.1:1111/v1"
|
||||
}
|
||||
if (client) {
|
||||
options.profileClient = client;
|
||||
}
|
||||
return new FxAccountsProfile(options);
|
||||
}
|
||||
|
||||
add_test(function getCachedProfile() {
|
||||
let accountData = mockAccountData();
|
||||
accountData.getUserAccountData = function () {
|
||||
return Promise.resolve({
|
||||
profile: { avatar: "myurl" }
|
||||
});
|
||||
};
|
||||
let profile = new FxAccountsProfile(accountData, PROFILE_CLIENT_OPTIONS);
|
||||
let profile = CreateFxAccountsProfile();
|
||||
// a little pointless until bug 1157529 is fixed...
|
||||
profile._cachedProfile = { avatar: "myurl" };
|
||||
|
||||
return profile._getCachedProfile()
|
||||
.then(function (cached) {
|
||||
@ -96,18 +108,20 @@ add_test(function getCachedProfile() {
|
||||
});
|
||||
|
||||
add_test(function cacheProfile_change() {
|
||||
let accountData = mockAccountData();
|
||||
let fxa = mockFxa();
|
||||
/* Saving profile data disabled - bug 1157529
|
||||
let setUserAccountDataCalled = false;
|
||||
accountData.setUserAccountData = function (data) {
|
||||
fxa.setUserAccountData = function (data) {
|
||||
setUserAccountDataCalled = true;
|
||||
do_check_eq(data.profile.avatar, "myurl");
|
||||
return Promise.resolve();
|
||||
};
|
||||
let profile = new FxAccountsProfile(accountData, PROFILE_CLIENT_OPTIONS);
|
||||
*/
|
||||
let profile = CreateFxAccountsProfile(fxa);
|
||||
|
||||
makeObserver(ON_PROFILE_CHANGE_NOTIFICATION, function (subject, topic, data) {
|
||||
do_check_eq(data, ACCOUNT_DATA.uid);
|
||||
do_check_true(setUserAccountDataCalled);
|
||||
// do_check_true(setUserAccountDataCalled); - bug 1157529
|
||||
run_next_test();
|
||||
});
|
||||
|
||||
@ -115,16 +129,14 @@ add_test(function cacheProfile_change() {
|
||||
});
|
||||
|
||||
add_test(function cacheProfile_no_change() {
|
||||
let accountData = mockAccountData();
|
||||
accountData.getUserAccountData = function () {
|
||||
return Promise.resolve({
|
||||
profile: { avatar: "myurl" }
|
||||
});
|
||||
};
|
||||
accountData.setUserAccountData = function (data) {
|
||||
let fxa = mockFxa();
|
||||
let profile = CreateFxAccountsProfile(fxa)
|
||||
profile._cachedProfile = { avatar: "myurl" };
|
||||
// XXX - saving is disabled (but we can leave that in for now as we are
|
||||
// just checking it is *not* called)
|
||||
fxa.setSignedInUser = function (data) {
|
||||
throw new Error("should not update account data");
|
||||
};
|
||||
let profile = new FxAccountsProfile(accountData, PROFILE_CLIENT_OPTIONS);
|
||||
|
||||
return profile._cacheProfile({ avatar: "myurl" })
|
||||
.then((result) => {
|
||||
@ -134,13 +146,11 @@ add_test(function cacheProfile_no_change() {
|
||||
});
|
||||
|
||||
add_test(function fetchAndCacheProfile_ok() {
|
||||
let client = mockClient();
|
||||
let client = mockClient(mockFxa());
|
||||
client.fetchProfile = function () {
|
||||
return Promise.resolve({ avatar: "myimg"});
|
||||
};
|
||||
let profile = new FxAccountsProfile(mockAccountData(), {
|
||||
profileClient: client
|
||||
});
|
||||
let profile = CreateFxAccountsProfile(null, client);
|
||||
|
||||
profile._cacheProfile = function (toCache) {
|
||||
do_check_eq(toCache.avatar, "myimg");
|
||||
@ -155,13 +165,13 @@ add_test(function fetchAndCacheProfile_ok() {
|
||||
});
|
||||
|
||||
add_test(function tearDown_ok() {
|
||||
let profile = new FxAccountsProfile(mockAccountData(), PROFILE_CLIENT_OPTIONS);
|
||||
let profile = CreateFxAccountsProfile();
|
||||
|
||||
do_check_true(!!profile.client);
|
||||
do_check_true(!!profile.currentAccountState);
|
||||
do_check_true(!!profile.fxa);
|
||||
|
||||
profile.tearDown();
|
||||
do_check_null(profile.currentAccountState);
|
||||
do_check_null(profile.fxa);
|
||||
do_check_null(profile.client);
|
||||
|
||||
run_next_test();
|
||||
@ -169,16 +179,16 @@ add_test(function tearDown_ok() {
|
||||
|
||||
add_test(function getProfile_ok() {
|
||||
let cachedUrl = "myurl";
|
||||
let accountData = mockAccountData();
|
||||
let didFetch = false;
|
||||
|
||||
let profile = new FxAccountsProfile(accountData, PROFILE_CLIENT_OPTIONS);
|
||||
let profile = CreateFxAccountsProfile();
|
||||
profile._getCachedProfile = function () {
|
||||
return Promise.resolve({ avatar: cachedUrl });
|
||||
};
|
||||
|
||||
profile._fetchAndCacheProfile = function () {
|
||||
didFetch = true;
|
||||
return Promise.resolve();
|
||||
};
|
||||
|
||||
return profile.getProfile()
|
||||
@ -191,9 +201,7 @@ add_test(function getProfile_ok() {
|
||||
|
||||
add_test(function getProfile_no_cache() {
|
||||
let fetchedUrl = "newUrl";
|
||||
let accountData = mockAccountData();
|
||||
|
||||
let profile = new FxAccountsProfile(accountData, PROFILE_CLIENT_OPTIONS);
|
||||
let profile = CreateFxAccountsProfile();
|
||||
profile._getCachedProfile = function () {
|
||||
return Promise.resolve();
|
||||
};
|
||||
@ -212,23 +220,23 @@ add_test(function getProfile_no_cache() {
|
||||
add_test(function getProfile_has_cached_fetch_deleted() {
|
||||
let cachedUrl = "myurl";
|
||||
|
||||
let client = mockClient();
|
||||
let fxa = mockFxa();
|
||||
let client = mockClient(fxa);
|
||||
client.fetchProfile = function () {
|
||||
return Promise.resolve({ avatar: null });
|
||||
};
|
||||
|
||||
let accountData = mockAccountData();
|
||||
accountData.getUserAccountData = function () {
|
||||
return Promise.resolve({ profile: { avatar: cachedUrl } });
|
||||
};
|
||||
accountData.setUserAccountData = function (data) {
|
||||
do_check_null(data.profile.avatar);
|
||||
run_next_test();
|
||||
return Promise.resolve();
|
||||
};
|
||||
let profile = CreateFxAccountsProfile(fxa, client);
|
||||
profile._cachedProfile = { avatar: cachedUrl };
|
||||
|
||||
let profile = new FxAccountsProfile(accountData, {
|
||||
profileClient: client
|
||||
// instead of checking this in a mocked "save" function, just check after the
|
||||
// observer
|
||||
makeObserver(ON_PROFILE_CHANGE_NOTIFICATION, function (subject, topic, data) {
|
||||
profile.getProfile()
|
||||
.then(profileData => {
|
||||
do_check_null(profileData.avatar);
|
||||
run_next_test();
|
||||
});
|
||||
});
|
||||
|
||||
return profile.getProfile()
|
||||
|
@ -6,11 +6,6 @@
|
||||
Cu.import("resource://gre/modules/FxAccountsCommon.js");
|
||||
Cu.import("resource://gre/modules/FxAccountsProfileClient.jsm");
|
||||
|
||||
const PROFILE_OPTIONS = {
|
||||
token: "123ABC",
|
||||
serverURL: "http://127.0.0.1:1111/v1",
|
||||
};
|
||||
|
||||
const STATUS_SUCCESS = 200;
|
||||
|
||||
/**
|
||||
@ -35,6 +30,21 @@ let mockResponse = function (response) {
|
||||
return Request;
|
||||
};
|
||||
|
||||
// A simple mock FxA that hands out tokens without checking them and doesn't
|
||||
// expect tokens to be revoked. We have specific token tests further down that
|
||||
// has more checks here.
|
||||
let mockFxa = {
|
||||
getOAuthToken(options) {
|
||||
do_check_eq(options.scope, "profile");
|
||||
return "token";
|
||||
}
|
||||
}
|
||||
|
||||
const PROFILE_OPTIONS = {
|
||||
serverURL: "http://127.0.0.1:1111/v1",
|
||||
fxa: mockFxa,
|
||||
};
|
||||
|
||||
/**
|
||||
* Mock request error responder
|
||||
* @param {Error} error
|
||||
@ -98,8 +108,8 @@ add_test(function parseErrorResponse () {
|
||||
add_test(function serverErrorResponse () {
|
||||
let client = new FxAccountsProfileClient(PROFILE_OPTIONS);
|
||||
let response = {
|
||||
status: 401,
|
||||
body: "{ \"code\": 401, \"errno\": 100, \"error\": \"Bad Request\", \"message\": \"Unauthorized\", \"reason\": \"Bearer token not provided\" }",
|
||||
status: 500,
|
||||
body: "{ \"code\": 500, \"errno\": 100, \"error\": \"Bad Request\", \"message\": \"Something went wrong\", \"reason\": \"Because the internet\" }",
|
||||
};
|
||||
|
||||
client._Request = new mockResponse(response);
|
||||
@ -108,10 +118,149 @@ add_test(function serverErrorResponse () {
|
||||
null,
|
||||
function (e) {
|
||||
do_check_eq(e.name, "FxAccountsProfileClientError");
|
||||
do_check_eq(e.code, 401);
|
||||
do_check_eq(e.code, 500);
|
||||
do_check_eq(e.errno, 100);
|
||||
do_check_eq(e.error, "Bad Request");
|
||||
do_check_eq(e.message, "Unauthorized");
|
||||
do_check_eq(e.message, "Something went wrong");
|
||||
run_next_test();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
// Test that we get a token, then if we get a 401 we revoke it, get a new one
|
||||
// and retry.
|
||||
add_test(function server401ResponseThenSuccess () {
|
||||
// The last token we handed out.
|
||||
let lastToken = -1;
|
||||
// The number of times our removeCachedOAuthToken function was called.
|
||||
let numTokensRemoved = 0;
|
||||
|
||||
let mockFxa = {
|
||||
getOAuthToken(options) {
|
||||
do_check_eq(options.scope, "profile");
|
||||
return "" + ++lastToken; // tokens are strings.
|
||||
},
|
||||
removeCachedOAuthToken(options) {
|
||||
// This test never has more than 1 token alive at once, so the token
|
||||
// being revoked must always be the last token we handed out.
|
||||
do_check_eq(parseInt(options.token), lastToken);
|
||||
++numTokensRemoved;
|
||||
}
|
||||
}
|
||||
let profileOptions = {
|
||||
serverURL: "http://127.0.0.1:1111/v1",
|
||||
fxa: mockFxa,
|
||||
};
|
||||
let client = new FxAccountsProfileClient(profileOptions);
|
||||
|
||||
// 2 responses - first one implying the token has expired, second works.
|
||||
let responses = [
|
||||
{
|
||||
status: 401,
|
||||
body: "{ \"code\": 401, \"errno\": 100, \"error\": \"Token expired\", \"message\": \"That token is too old\", \"reason\": \"Because security\" }",
|
||||
},
|
||||
{
|
||||
success: true,
|
||||
status: STATUS_SUCCESS,
|
||||
body: "{\"avatar\":\"http://example.com/image.jpg\",\"id\":\"0d5c1a89b8c54580b8e3e8adadae864a\"}",
|
||||
},
|
||||
];
|
||||
|
||||
let numRequests = 0;
|
||||
let numAuthHeaders = 0;
|
||||
// Like mockResponse but we want access to headers etc.
|
||||
client._Request = function(requestUri) {
|
||||
return {
|
||||
setHeader: function (name, value) {
|
||||
if (name == "Authorization") {
|
||||
numAuthHeaders++;
|
||||
do_check_eq(value, "Bearer " + lastToken);
|
||||
}
|
||||
},
|
||||
get: function () {
|
||||
this.response = responses[numRequests];
|
||||
++numRequests;
|
||||
this.onComplete();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
client.fetchProfile()
|
||||
.then(result => {
|
||||
do_check_eq(result.avatar, "http://example.com/image.jpg");
|
||||
do_check_eq(result.id, "0d5c1a89b8c54580b8e3e8adadae864a");
|
||||
// should have been exactly 2 requests and exactly 2 auth headers.
|
||||
do_check_eq(numRequests, 2);
|
||||
do_check_eq(numAuthHeaders, 2);
|
||||
// and we should have seen one token revoked.
|
||||
do_check_eq(numTokensRemoved, 1);
|
||||
|
||||
run_next_test();
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
// Test that we get a token, then if we get a 401 we revoke it, get a new one
|
||||
// and retry - but we *still* get a 401 on the retry, so the caller sees that.
|
||||
add_test(function server401ResponsePersists () {
|
||||
// The last token we handed out.
|
||||
let lastToken = -1;
|
||||
// The number of times our removeCachedOAuthToken function was called.
|
||||
let numTokensRemoved = 0;
|
||||
|
||||
let mockFxa = {
|
||||
getOAuthToken(options) {
|
||||
do_check_eq(options.scope, "profile");
|
||||
return "" + ++lastToken; // tokens are strings.
|
||||
},
|
||||
removeCachedOAuthToken(options) {
|
||||
// This test never has more than 1 token alive at once, so the token
|
||||
// being revoked must always be the last token we handed out.
|
||||
do_check_eq(parseInt(options.token), lastToken);
|
||||
++numTokensRemoved;
|
||||
}
|
||||
}
|
||||
let profileOptions = {
|
||||
serverURL: "http://127.0.0.1:1111/v1",
|
||||
fxa: mockFxa,
|
||||
};
|
||||
let client = new FxAccountsProfileClient(profileOptions);
|
||||
|
||||
let response = {
|
||||
status: 401,
|
||||
body: "{ \"code\": 401, \"errno\": 100, \"error\": \"It's not your token, it's you!\", \"message\": \"I don't like you\", \"reason\": \"Because security\" }",
|
||||
};
|
||||
|
||||
let numRequests = 0;
|
||||
let numAuthHeaders = 0;
|
||||
client._Request = function(requestUri) {
|
||||
return {
|
||||
setHeader: function (name, value) {
|
||||
if (name == "Authorization") {
|
||||
numAuthHeaders++;
|
||||
do_check_eq(value, "Bearer " + lastToken);
|
||||
}
|
||||
},
|
||||
get: function () {
|
||||
this.response = response;
|
||||
++numRequests;
|
||||
this.onComplete();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
client.fetchProfile().then(
|
||||
null,
|
||||
function (e) {
|
||||
do_check_eq(e.name, "FxAccountsProfileClientError");
|
||||
do_check_eq(e.code, 401);
|
||||
do_check_eq(e.errno, 100);
|
||||
do_check_eq(e.error, "It's not your token, it's you!");
|
||||
// should have been exactly 2 requests and exactly 2 auth headers.
|
||||
do_check_eq(numRequests, 2);
|
||||
do_check_eq(numAuthHeaders, 2);
|
||||
// and we should have seen both tokens revoked.
|
||||
do_check_eq(numTokensRemoved, 2);
|
||||
run_next_test();
|
||||
}
|
||||
);
|
||||
@ -119,8 +268,8 @@ add_test(function serverErrorResponse () {
|
||||
|
||||
add_test(function networkErrorResponse () {
|
||||
let client = new FxAccountsProfileClient({
|
||||
token: "123ABC",
|
||||
serverURL: "http://"
|
||||
serverURL: "http://",
|
||||
fxa: mockFxa,
|
||||
});
|
||||
client.fetchProfile()
|
||||
.then(
|
||||
@ -191,18 +340,12 @@ add_test(function fetchProfileImage_successfulResponse () {
|
||||
|
||||
add_test(function constructorTests() {
|
||||
validationHelper(undefined,
|
||||
"Error: Missing 'serverURL' or 'token' configuration option");
|
||||
"Error: Missing 'serverURL' configuration option");
|
||||
|
||||
validationHelper({},
|
||||
"Error: Missing 'serverURL' or 'token' configuration option");
|
||||
"Error: Missing 'serverURL' configuration option");
|
||||
|
||||
validationHelper({ serverURL: "http://example.com" },
|
||||
"Error: Missing 'serverURL' or 'token' configuration option");
|
||||
|
||||
validationHelper({ token: "123ABC" },
|
||||
"Error: Missing 'serverURL' or 'token' configuration option");
|
||||
|
||||
validationHelper({ token: "123ABC", serverURL: "badUrl" },
|
||||
validationHelper({ serverURL: "badUrl" },
|
||||
"Error: Invalid 'serverURL'");
|
||||
|
||||
run_next_test();
|
||||
@ -255,6 +398,10 @@ function run_test() {
|
||||
* @returns {*}
|
||||
*/
|
||||
function validationHelper(options, expected) {
|
||||
// add fxa to options - that missing isn't what we are testing here.
|
||||
if (options) {
|
||||
options.fxa = mockFxa;
|
||||
}
|
||||
try {
|
||||
new FxAccountsProfileClient(options);
|
||||
} catch (e) {
|
||||
|
Loading…
Reference in New Issue
Block a user