зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1101444
- [Loop] For each invalid inserted verification code, Device receives a valid verification code. r=spenrose
This commit is contained in:
Родитель
6aaa94fefc
Коммит
f688c6c397
|
@ -37,68 +37,67 @@ MobileIdentityVerificationFlow.prototype = {
|
|||
return Promise.reject(ERROR_INTERNAL_UNEXPECTED);
|
||||
}
|
||||
this.sessionToken = registerResult.msisdnSessionToken;
|
||||
return this._doVerification();
|
||||
// We save the timestamp of the start of the verification timeout to be
|
||||
// able to provide to the UI the remaining time on each retry.
|
||||
if (!this.timer) {
|
||||
log.debug("Creating verification code timer");
|
||||
this.timerCreation = Date.now();
|
||||
this.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
||||
this.timer.initWithCallback(this.onVerificationCodeTimeout.bind(this),
|
||||
VERIFICATIONCODE_TIMEOUT,
|
||||
this.timer.TYPE_ONE_SHOT);
|
||||
}
|
||||
|
||||
if (!this.verifyStrategy) {
|
||||
return Promise.reject(ERROR_INTERNAL_INVALID_VERIFICATION_FLOW);
|
||||
}
|
||||
|
||||
return this.verifyStrategy()
|
||||
.then(() => {
|
||||
return this._doVerification();
|
||||
}, (reason) => {
|
||||
this.verificationCodeDeferred.reject(reason);
|
||||
});
|
||||
}
|
||||
)
|
||||
},
|
||||
|
||||
_doVerification: function() {
|
||||
log.debug("_doVerification");
|
||||
// We save the timestamp of the start of the verification timeout to be
|
||||
// able to provide to the UI the remaining time on each retry.
|
||||
if (!this.timer) {
|
||||
log.debug("Creating verification code timer");
|
||||
this.timerCreation = Date.now();
|
||||
this.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
||||
this.timer.initWithCallback(this.onVerificationCodeTimeout.bind(this),
|
||||
VERIFICATIONCODE_TIMEOUT,
|
||||
this.timer.TYPE_ONE_SHOT);
|
||||
}
|
||||
|
||||
if (!this.verifyStrategy) {
|
||||
return Promise.reject(ERROR_INTERNAL_INVALID_VERIFICATION_FLOW);
|
||||
}
|
||||
|
||||
this.verificationCodeDeferred = Promise.defer();
|
||||
|
||||
this.verifyStrategy()
|
||||
.then(
|
||||
() => {
|
||||
// If the verification flow can be for an external phone number,
|
||||
// we need to ask the user for the verification code.
|
||||
// In that case we don't do a notification about the verification
|
||||
// process being done until the user enters the verification code
|
||||
// in the UI.
|
||||
if (this.verificationOptions.external) {
|
||||
let timeLeft = 0;
|
||||
if (this.timer) {
|
||||
timeLeft = this.timerCreation + VERIFICATIONCODE_TIMEOUT -
|
||||
Date.now();
|
||||
}
|
||||
this.ui.verificationCodePrompt(this.retries,
|
||||
VERIFICATIONCODE_TIMEOUT / 1000,
|
||||
timeLeft / 1000)
|
||||
.then(
|
||||
(verificationCode) => {
|
||||
if (!verificationCode) {
|
||||
return this.verificationCodeDeferred.reject(
|
||||
ERROR_INTERNAL_INVALID_PROMPT_RESULT);
|
||||
}
|
||||
// If the user got the verification code that means that the
|
||||
// introduced phone number didn't belong to any of the inserted
|
||||
// SIMs.
|
||||
this.ui.verify();
|
||||
this.verificationCodeDeferred.resolve(verificationCode);
|
||||
}
|
||||
);
|
||||
} else {
|
||||
this.ui.verify();
|
||||
}
|
||||
},
|
||||
(reason) => {
|
||||
this.verificationCodeDeferred.reject(reason);
|
||||
// If the verification flow can be for an external phone number,
|
||||
// we need to ask the user for the verification code.
|
||||
// In that case we don't do a notification about the verification
|
||||
// process being done until the user enters the verification code
|
||||
// in the UI.
|
||||
if (this.verificationOptions.external) {
|
||||
let timeLeft = 0;
|
||||
if (this.timer) {
|
||||
timeLeft = this.timerCreation + VERIFICATIONCODE_TIMEOUT -
|
||||
Date.now();
|
||||
}
|
||||
);
|
||||
this.ui.verificationCodePrompt(this.retries,
|
||||
VERIFICATIONCODE_TIMEOUT / 1000,
|
||||
timeLeft / 1000)
|
||||
.then(
|
||||
(verificationCode) => {
|
||||
if (!verificationCode) {
|
||||
return this.verificationCodeDeferred.reject(
|
||||
ERROR_INTERNAL_INVALID_PROMPT_RESULT);
|
||||
}
|
||||
// If the user got the verification code that means that the
|
||||
// introduced phone number didn't belong to any of the inserted
|
||||
// SIMs.
|
||||
this.ui.verify();
|
||||
this.verificationCodeDeferred.resolve(verificationCode);
|
||||
}
|
||||
);
|
||||
} else {
|
||||
this.ui.verify();
|
||||
}
|
||||
|
||||
return this.verificationCodeDeferred.promise.then(
|
||||
this.onVerificationCode.bind(this)
|
||||
);
|
||||
|
@ -145,8 +144,11 @@ MobileIdentityVerificationFlow.prototype = {
|
|||
log.error("Retries left " + this.retries);
|
||||
if (!this.retries) {
|
||||
this.ui.error(ERROR_NO_RETRIES_LEFT);
|
||||
this.timer.cancel();
|
||||
this.timer = null;
|
||||
return Promise.reject(ERROR_NO_RETRIES_LEFT);
|
||||
}
|
||||
this.ui.error(ERROR_INVALID_VERIFICATION_CODE);
|
||||
this.verifying = false;
|
||||
if (this.queuedTimeout) {
|
||||
this.onVerificationCodeTimeout();
|
||||
|
|
|
@ -5,6 +5,8 @@ const {classes: Cc, interfaces: Ci, results: Cr, utils: Cu} = Components;
|
|||
|
||||
"use strict";
|
||||
|
||||
const Cm = Components.manager;
|
||||
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
|
@ -17,3 +19,444 @@ Cu.import("resource://gre/modules/Services.jsm");
|
|||
Services.prefs.setCharPref("services.mobileid.server.uri",
|
||||
"https://dummyurl.com");
|
||||
}).call(this);
|
||||
|
||||
const DEBUG = false;
|
||||
|
||||
const GET_ASSERTION_IPC_MSG = "MobileId:GetAssertion";
|
||||
const GET_ASSERTION_RETURN_OK = "MobileId:GetAssertion:Return:OK";
|
||||
const GET_ASSERTION_RETURN_KO = "MobileId:GetAssertion:Return:KO";
|
||||
|
||||
// === Globals ===
|
||||
|
||||
const ORIGIN = "app://afakeorigin";
|
||||
const APP_ID = 1;
|
||||
const PRINCIPAL = {
|
||||
origin: ORIGIN,
|
||||
appId: APP_ID
|
||||
};
|
||||
const PHONE_NUMBER = "+34666555444";
|
||||
const ANOTHER_PHONE_NUMBER = "+44123123123";
|
||||
const VERIFICATION_CODE = "123456";
|
||||
const SESSION_TOKEN = "aSessionToken";
|
||||
const ICC_ID = "aIccId";
|
||||
const ANOTHER_ICC_ID = "anotherIccId";
|
||||
const MNC = "aMnc";
|
||||
const ANOTHER_MNC = "anotherMnc";
|
||||
const MCC = "aMcc";
|
||||
const ANOTHER_MCC = "anotherMcc";
|
||||
const OPERATOR = "aOperator";
|
||||
const ANOTHER_OPERATOR = "anotherOperator";
|
||||
const RADIO_INTERFACE = {
|
||||
rilContext: {
|
||||
iccInfo: {
|
||||
iccid: ICC_ID,
|
||||
mcc: MCC,
|
||||
mnc: MNC,
|
||||
msisdn: PHONE_NUMBER,
|
||||
operator: OPERATOR
|
||||
}
|
||||
},
|
||||
voice: {
|
||||
network: {
|
||||
shortName: OPERATOR
|
||||
},
|
||||
roaming: false
|
||||
},
|
||||
data: {
|
||||
network: {
|
||||
shortName: OPERATOR
|
||||
}
|
||||
}
|
||||
};
|
||||
const ANOTHER_RADIO_INTERFACE = {
|
||||
rilContext: {
|
||||
iccInfo: {
|
||||
iccid: ANOTHER_ICC_ID,
|
||||
mcc: ANOTHER_MCC,
|
||||
mnc: ANOTHER_MNC,
|
||||
msisdn: ANOTHER_PHONE_NUMBER,
|
||||
operator: ANOTHER_OPERATOR
|
||||
}
|
||||
},
|
||||
voice: {
|
||||
network: {
|
||||
shortName: ANOTHER_OPERATOR
|
||||
},
|
||||
roaming: false
|
||||
},
|
||||
data: {
|
||||
network: {
|
||||
shortName: ANOTHER_OPERATOR
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const INVALID_RADIO_INTERFACE = {
|
||||
rilContext: {
|
||||
iccInfo: {
|
||||
iccid: null,
|
||||
mcc: "",
|
||||
mnc: "",
|
||||
msisdn: "",
|
||||
operator: ""
|
||||
}
|
||||
},
|
||||
voice: {
|
||||
network: {
|
||||
shortName: ""
|
||||
},
|
||||
roaming: undefined
|
||||
},
|
||||
data: {
|
||||
network: {
|
||||
shortName: ""
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const CERTIFICATE = "eyJhbGciOiJEUzI1NiJ9.eyJsYXN0QXV0aEF0IjoxNDA0NDY5NzkyODc3LCJ2ZXJpZmllZE1TSVNETiI6IiszMTYxNzgxNTc1OCIsInB1YmxpYy1rZXkiOnsiYWxnb3JpdGhtIjoiRFMiLCJ5IjoiNGE5YzkzNDY3MWZhNzQ3YmM2ZjMyNjE0YTg1MzUyZjY5NDcwMDdhNTRkMDAxMDY4OWU5ZjJjZjc0ZGUwYTEwZTRlYjlmNDk1ZGFmZTA0NGVjZmVlNDlkN2YwOGU4ODQyMDJiOTE5OGRhNWZhZWE5MGUzZjRmNzE1YzZjNGY4Yjc3MGYxZTU4YWZhNDM0NzVhYmFiN2VlZGE1MmUyNjk2YzFmNTljNzMzYjFlYzBhNGNkOTM1YWIxYzkyNzAxYjNiYTA5ZDRhM2E2MzNjNTJmZjE2NGYxMWY3OTg1YzlmZjY3ZThmZDFlYzA2NDU3MTdkMjBiNDE4YmM5M2YzYzVkNCIsInAiOiJmZjYwMDQ4M2RiNmFiZmM1YjQ1ZWFiNzg1OTRiMzUzM2Q1NTBkOWYxYmYyYTk5MmE3YThkYWE2ZGMzNGY4MDQ1YWQ0ZTZlMGM0MjlkMzM0ZWVlYWFlZmQ3ZTIzZDQ4MTBiZTAwZTRjYzE0OTJjYmEzMjViYTgxZmYyZDVhNWIzMDVhOGQxN2ViM2JmNGEwNmEzNDlkMzkyZTAwZDMyOTc0NGE1MTc5MzgwMzQ0ZTgyYTE4YzQ3OTMzNDM4Zjg5MWUyMmFlZWY4MTJkNjljOGY3NWUzMjZjYjcwZWEwMDBjM2Y3NzZkZmRiZDYwNDYzOGMyZWY3MTdmYzI2ZDAyZTE3IiwicSI6ImUyMWUwNGY5MTFkMWVkNzk5MTAwOGVjYWFiM2JmNzc1OTg0MzA5YzMiLCJnIjoiYzUyYTRhMGZmM2I3ZTYxZmRmMTg2N2NlODQxMzgzNjlhNjE1NGY0YWZhOTI5NjZlM2M4MjdlMjVjZmE2Y2Y1MDhiOTBlNWRlNDE5ZTEzMzdlMDdhMmU5ZTJhM2NkNWRlYTcwNGQxNzVmOGViZjZhZjM5N2Q2OWUxMTBiOTZhZmIxN2M3YTAzMjU5MzI5ZTQ4MjliMGQwM2JiYzc4OTZiMTViNGFkZTUzZTEzMDg1OGNjMzRkOTYyNjlhYTg5MDQxZjQwOTEzNmM3MjQyYTM4ODk1YzlkNWJjY2FkNGYzODlhZjFkN2E0YmQxMzk4YmQwNzJkZmZhODk2MjMzMzk3YSJ9LCJwcmluY2lwYWwiOiIwMzgxOTgyYS0xZTgzLTI1NjYtNjgzZS05MDRmNDA0NGM1MGRAbXNpc2RuLWRldi5zdGFnZS5tb3phd3MubmV0IiwiaWF0IjoxNDA0NDY5NzgyODc3LCJleHAiOjE0MDQ0OTEzOTI4NzcsImlzcyI6Im1zaXNkbi1kZXYuc3RhZ2UubW96YXdzLm5ldCJ9."
|
||||
|
||||
// === Helpers ===
|
||||
|
||||
function addPermission(aAction) {
|
||||
let uri = Cc["@mozilla.org/network/io-service;1"]
|
||||
.getService(Ci.nsIIOService)
|
||||
.newURI(ORIGIN, null, null);
|
||||
let _principal = Cc["@mozilla.org/scriptsecuritymanager;1"]
|
||||
.getService(Ci.nsIScriptSecurityManager)
|
||||
.getAppCodebasePrincipal(uri, APP_ID, false);
|
||||
let pm = Cc["@mozilla.org/permissionmanager;1"]
|
||||
.getService(Ci.nsIPermissionManager);
|
||||
pm.addFromPrincipal(_principal, MOBILEID_PERM, aAction);
|
||||
}
|
||||
|
||||
function removePermission() {
|
||||
let uri = Cc["@mozilla.org/network/io-service;1"]
|
||||
.getService(Ci.nsIIOService)
|
||||
.newURI(ORIGIN, null, null);
|
||||
let _principal = Cc["@mozilla.org/scriptsecuritymanager;1"]
|
||||
.getService(Ci.nsIScriptSecurityManager)
|
||||
.getAppCodebasePrincipal(uri, APP_ID, false);
|
||||
let pm = Cc["@mozilla.org/permissionmanager;1"]
|
||||
.getService(Ci.nsIPermissionManager);
|
||||
pm.removeFromPrincipal(_principal, MOBILEID_PERM);
|
||||
}
|
||||
|
||||
// === Mocks ===
|
||||
|
||||
let Mock = function(aOptions) {
|
||||
if (!aOptions) {
|
||||
aOptions = {};
|
||||
}
|
||||
this._options = aOptions;
|
||||
this._spied = {};
|
||||
};
|
||||
|
||||
Mock.prototype = {
|
||||
_: function(aMethod) {
|
||||
DEBUG && do_print("_ " + aMethod + JSON.stringify(this._spied));
|
||||
let self = this;
|
||||
return {
|
||||
callsLength: function(aNumberOfCalls) {
|
||||
if (aNumberOfCalls == 0) {
|
||||
do_check_eq(self._spied[aMethod], undefined);
|
||||
return;
|
||||
}
|
||||
do_check_eq(self._spied[aMethod].length, aNumberOfCalls);
|
||||
},
|
||||
call: function(aCallNumber) {
|
||||
return {
|
||||
arg: function(aArgNumber, aValue) {
|
||||
let _arg = self._spied[aMethod][aCallNumber - 1][aArgNumber - 1];
|
||||
if (Array.isArray(aValue)) {
|
||||
do_check_eq(_arg.length, aValue.length)
|
||||
for (let i = 0; i < _arg.length; i++) {
|
||||
do_check_eq(_arg[i], aValue[i]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof aValue === 'object') {
|
||||
do_check_eq(JSON.stringify(_arg), JSON.stringify(aValue));
|
||||
return;
|
||||
}
|
||||
|
||||
do_check_eq(_arg, aValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_spy: function(aMethod, aArgs) {
|
||||
DEBUG && do_print(aMethod + " - " + JSON.stringify(aArgs));
|
||||
if (!this._spied[aMethod]) {
|
||||
this._spied[aMethod] = [];
|
||||
}
|
||||
this._spied[aMethod].push(aArgs);
|
||||
},
|
||||
|
||||
getSpiedCalls: function(aMethod) {
|
||||
return this._spied[aMethod];
|
||||
}
|
||||
};
|
||||
|
||||
// UI Glue mock up.
|
||||
let MockUi = function(aOptions) {
|
||||
Mock.call(this, aOptions);
|
||||
};
|
||||
|
||||
MockUi.prototype = {
|
||||
__proto__: Mock.prototype,
|
||||
|
||||
_startFlowResult: {
|
||||
phoneNumber: PHONE_NUMBER,
|
||||
mcc: MNC
|
||||
},
|
||||
|
||||
_verifyCodePromptResult: {
|
||||
verificationCode: VERIFICATION_CODE
|
||||
},
|
||||
|
||||
startFlow: function() {
|
||||
this._spy("startFlow", arguments);
|
||||
return Promise.resolve(this._options.startFlowResult ||
|
||||
this._startFlowResult);
|
||||
},
|
||||
|
||||
verificationCodePrompt: function() {
|
||||
this._spy("verifyCodePrompt", arguments);
|
||||
return Promise.resolve(this._options.verificationCodePromptResult ||
|
||||
this._verifyCodePromptResult);
|
||||
},
|
||||
|
||||
verify: function() {
|
||||
this._spy("verify", arguments);
|
||||
},
|
||||
|
||||
error: function() {
|
||||
this._spy("error", arguments);
|
||||
},
|
||||
|
||||
verified: function() {
|
||||
this._spy("verified", arguments);
|
||||
},
|
||||
|
||||
set oncancel(aCallback) {
|
||||
},
|
||||
|
||||
set onresendcode(aCallback) {
|
||||
}
|
||||
};
|
||||
|
||||
// Credentials store mock up.
|
||||
let MockCredStore = function(aOptions) {
|
||||
Mock.call(this, aOptions);
|
||||
};
|
||||
|
||||
MockCredStore.prototype = {
|
||||
__proto__: Mock.prototype,
|
||||
|
||||
_getByOriginResult: null,
|
||||
|
||||
_getByMsisdnResult: null,
|
||||
|
||||
_getByIccIdResult: null,
|
||||
|
||||
getByOrigin: function() {
|
||||
this._spy("getByOrigin", arguments);
|
||||
let result = this._getByOriginResult;
|
||||
if (this._options.getByOriginResult) {
|
||||
if (Array.isArray(this._options.getByOriginResult)) {
|
||||
result = this._options.getByOriginResult.length ?
|
||||
this._options.getByOriginResult.shift() : null;
|
||||
} else {
|
||||
result = this._options.getByOriginResult;
|
||||
}
|
||||
}
|
||||
return Promise.resolve(result);
|
||||
},
|
||||
|
||||
getByMsisdn: function() {
|
||||
this._spy("getByMsisdn", arguments);
|
||||
return Promise.resolve(this._options.getByMsisdnResult ||
|
||||
this._getByMsisdnResult);
|
||||
},
|
||||
|
||||
getByIccId: function() {
|
||||
this._spy("getByIccId", arguments);
|
||||
return Promise.resolve(this._options.getByIccIdResult ||
|
||||
this._getByIccIdResult);
|
||||
},
|
||||
|
||||
add: function() {
|
||||
this._spy("add", arguments);
|
||||
return Promise.resolve();
|
||||
},
|
||||
|
||||
setDeviceIccIds: function() {
|
||||
this._spy("setDeviceIccIds", arguments);
|
||||
return Promise.resolve();
|
||||
},
|
||||
|
||||
removeOrigin: function() {
|
||||
this._spy("removeOrigin", arguments);
|
||||
return Promise.resolve();
|
||||
},
|
||||
|
||||
delete: function() {
|
||||
this._spy("delete", arguments);
|
||||
return Promise.resolve();
|
||||
}
|
||||
};
|
||||
|
||||
// Client mock up.
|
||||
let MockClient = function(aOptions) {
|
||||
Mock.call(this, aOptions);
|
||||
};
|
||||
|
||||
MockClient.prototype = {
|
||||
|
||||
__proto__: Mock.prototype,
|
||||
|
||||
_discoverResult: {
|
||||
verificationMethods: ["sms/mt"],
|
||||
verificationDetails: {
|
||||
"sms/mt": {
|
||||
mtSender: "123",
|
||||
url: "https://msisdn.accounts.firefox.com/v1/msisdn/sms/mt/verify"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_registerResult: {
|
||||
msisdnSessionToken: SESSION_TOKEN
|
||||
},
|
||||
|
||||
_smsMtVerifyResult: {},
|
||||
|
||||
_verifyCodeResult: {
|
||||
msisdn: PHONE_NUMBER
|
||||
},
|
||||
|
||||
_signResult: {
|
||||
cert: CERTIFICATE
|
||||
},
|
||||
|
||||
hawk: {
|
||||
now: function() {
|
||||
return Date.now();
|
||||
}
|
||||
},
|
||||
|
||||
discover: function() {
|
||||
this._spy("discover", arguments);
|
||||
return Promise.resolve(this._options.discoverResult ||
|
||||
this._discoverResult);
|
||||
},
|
||||
|
||||
register: function() {
|
||||
this._spy("register", arguments);
|
||||
return Promise.resolve(this._options.registerResult ||
|
||||
this._registerResult);
|
||||
},
|
||||
|
||||
smsMtVerify: function() {
|
||||
this._spy("smsMtVerify", arguments);
|
||||
return Promise.resolve(this._options.smsMtVerifyResult ||
|
||||
this._smsMtVerifyResult);
|
||||
},
|
||||
|
||||
verifyCode: function() {
|
||||
this._spy("verifyCode", arguments);
|
||||
if (this._options.verifyCodeError) {
|
||||
let error = Array.isArray(this._options.verifyCodeError) ?
|
||||
this._options.verifyCodeError.shift() :
|
||||
this._options.verifyCodeError;
|
||||
if (!this._options.verifyCodeError.length) {
|
||||
this._options.verifyCodeError = null;
|
||||
}
|
||||
return Promise.reject(error);
|
||||
}
|
||||
return Promise.resolve(this._options.verifyCodeResult ||
|
||||
this._verifyCodeResult);
|
||||
},
|
||||
|
||||
sign: function() {
|
||||
this._spy("sign", arguments);
|
||||
if (this._options.signError) {
|
||||
let error = Array.isArray(this._options.signError) ?
|
||||
this._options.signError.shift() :
|
||||
this._options.signError;
|
||||
return Promise.reject(error);
|
||||
}
|
||||
return Promise.resolve(this._options.signResult || this._signResult);
|
||||
}
|
||||
};
|
||||
|
||||
// Override MobileIdentityUIGlue.
|
||||
const kMobileIdentityUIGlueUUID = "{05df0566-ca8a-4ec7-bc76-78626ebfbe9a}";
|
||||
const kMobileIdentityUIGlueContractID =
|
||||
"@mozilla.org/services/mobileid-ui-glue;1";
|
||||
|
||||
// Save original factory.
|
||||
/*const kMobileIdentityUIGlueFactory =
|
||||
Cm.getClassObject(Cc[kMobileIdentityUIGlueContractID], Ci.nsIFactory);*/
|
||||
|
||||
let fakeMobileIdentityUIGlueFactory = {
|
||||
createInstance: function(aOuter, aIid) {
|
||||
return MobileIdentityUIGlue.QueryInterface(aIid);
|
||||
}
|
||||
};
|
||||
|
||||
// MobileIdentityUIGlue fake component.
|
||||
let MobileIdentityUIGlue = {
|
||||
QueryInterface: XPCOMUtils.generateQI([Ci.nsIMobileIdentityUIGlue]),
|
||||
|
||||
};
|
||||
|
||||
(function registerFakeMobileIdentityUIGlue() {
|
||||
Cm.QueryInterface(Ci.nsIComponentRegistrar)
|
||||
.registerFactory(Components.ID(kMobileIdentityUIGlueUUID),
|
||||
"MobileIdentityUIGlue",
|
||||
kMobileIdentityUIGlueContractID,
|
||||
fakeMobileIdentityUIGlueFactory);
|
||||
})();
|
||||
|
||||
// The tests rely on having an app registered. Otherwise, it will throw.
|
||||
// Override XULAppInfo.
|
||||
const XUL_APP_INFO_UUID = Components.ID("{84fdc459-d96d-421c-9bff-a8193233ae75}");
|
||||
const XUL_APP_INFO_CONTRACT_ID = "@mozilla.org/xre/app-info;1";
|
||||
|
||||
let (XULAppInfo = {
|
||||
vendor: "Mozilla",
|
||||
name: "MobileIdTest",
|
||||
ID: "{230de50e-4cd1-11dc-8314-0800200b9a66}",
|
||||
version: "1",
|
||||
appBuildID: "2007010101",
|
||||
platformVersion: "",
|
||||
platformBuildID: "2007010101",
|
||||
inSafeMode: false,
|
||||
logConsoleErrors: true,
|
||||
OS: "XPCShell",
|
||||
XPCOMABI: "noarch-spidermonkey",
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([
|
||||
Ci.nsIXULAppInfo,
|
||||
Ci.nsIXULRuntime,
|
||||
])
|
||||
}) {
|
||||
let XULAppInfoFactory = {
|
||||
createInstance: function (outer, iid) {
|
||||
if (outer != null) {
|
||||
throw Cr.NS_ERROR_NO_AGGREGATION;
|
||||
}
|
||||
return XULAppInfo.QueryInterface(iid);
|
||||
}
|
||||
};
|
||||
Cm.QueryInterface(Ci.nsIComponentRegistrar)
|
||||
.registerFactory(XUL_APP_INFO_UUID,
|
||||
"XULAppInfo",
|
||||
XUL_APP_INFO_CONTRACT_ID,
|
||||
XULAppInfoFactory);
|
||||
}
|
||||
|
|
|
@ -3,424 +3,16 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
const Cm = Components.manager;
|
||||
|
||||
Cu.import("resource://gre/modules/Promise.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/MobileIdentityManager.jsm");
|
||||
Cu.import("resource://gre/modules/MobileIdentityCommon.jsm");
|
||||
|
||||
const DEBUG = false;
|
||||
|
||||
const GET_ASSERTION_IPC_MSG = "MobileId:GetAssertion";
|
||||
const GET_ASSERTION_RETURN_OK = "MobileId:GetAssertion:Return:OK";
|
||||
const GET_ASSERTION_RETURN_KO = "MobileId:GetAssertion:Return:KO";
|
||||
|
||||
// === Globals ===
|
||||
|
||||
const ORIGIN = "app://afakeorigin";
|
||||
const APP_ID = 1;
|
||||
const PRINCIPAL = {
|
||||
origin: ORIGIN,
|
||||
appId: APP_ID
|
||||
};
|
||||
const PHONE_NUMBER = "+34666555444";
|
||||
const ANOTHER_PHONE_NUMBER = "+44123123123";
|
||||
const VERIFICATION_CODE = "123456";
|
||||
const SESSION_TOKEN = "aSessionToken";
|
||||
const ICC_ID = "aIccId";
|
||||
const ANOTHER_ICC_ID = "anotherIccId";
|
||||
const MNC = "aMnc";
|
||||
const ANOTHER_MNC = "anotherMnc";
|
||||
const MCC = "aMcc";
|
||||
const ANOTHER_MCC = "anotherMcc";
|
||||
const OPERATOR = "aOperator";
|
||||
const ANOTHER_OPERATOR = "anotherOperator";
|
||||
const RADIO_INTERFACE = {
|
||||
rilContext: {
|
||||
iccInfo: {
|
||||
iccid: ICC_ID,
|
||||
mcc: MCC,
|
||||
mnc: MNC,
|
||||
msisdn: PHONE_NUMBER,
|
||||
operator: OPERATOR
|
||||
}
|
||||
},
|
||||
voice: {
|
||||
network: {
|
||||
shortName: OPERATOR
|
||||
},
|
||||
roaming: false
|
||||
},
|
||||
data: {
|
||||
network: {
|
||||
shortName: OPERATOR
|
||||
}
|
||||
}
|
||||
};
|
||||
const ANOTHER_RADIO_INTERFACE = {
|
||||
rilContext: {
|
||||
iccInfo: {
|
||||
iccid: ANOTHER_ICC_ID,
|
||||
mcc: ANOTHER_MCC,
|
||||
mnc: ANOTHER_MNC,
|
||||
msisdn: ANOTHER_PHONE_NUMBER,
|
||||
operator: ANOTHER_OPERATOR
|
||||
}
|
||||
},
|
||||
voice: {
|
||||
network: {
|
||||
shortName: ANOTHER_OPERATOR
|
||||
},
|
||||
roaming: false
|
||||
},
|
||||
data: {
|
||||
network: {
|
||||
shortName: ANOTHER_OPERATOR
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const INVALID_RADIO_INTERFACE = {
|
||||
rilContext: {
|
||||
iccInfo: {
|
||||
iccid: null,
|
||||
mcc: "",
|
||||
mnc: "",
|
||||
msisdn: "",
|
||||
operator: ""
|
||||
}
|
||||
},
|
||||
voice: {
|
||||
network: {
|
||||
shortName: ""
|
||||
},
|
||||
roaming: undefined
|
||||
},
|
||||
data: {
|
||||
network: {
|
||||
shortName: ""
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const CERTIFICATE = "eyJhbGciOiJEUzI1NiJ9.eyJsYXN0QXV0aEF0IjoxNDA0NDY5NzkyODc3LCJ2ZXJpZmllZE1TSVNETiI6IiszMTYxNzgxNTc1OCIsInB1YmxpYy1rZXkiOnsiYWxnb3JpdGhtIjoiRFMiLCJ5IjoiNGE5YzkzNDY3MWZhNzQ3YmM2ZjMyNjE0YTg1MzUyZjY5NDcwMDdhNTRkMDAxMDY4OWU5ZjJjZjc0ZGUwYTEwZTRlYjlmNDk1ZGFmZTA0NGVjZmVlNDlkN2YwOGU4ODQyMDJiOTE5OGRhNWZhZWE5MGUzZjRmNzE1YzZjNGY4Yjc3MGYxZTU4YWZhNDM0NzVhYmFiN2VlZGE1MmUyNjk2YzFmNTljNzMzYjFlYzBhNGNkOTM1YWIxYzkyNzAxYjNiYTA5ZDRhM2E2MzNjNTJmZjE2NGYxMWY3OTg1YzlmZjY3ZThmZDFlYzA2NDU3MTdkMjBiNDE4YmM5M2YzYzVkNCIsInAiOiJmZjYwMDQ4M2RiNmFiZmM1YjQ1ZWFiNzg1OTRiMzUzM2Q1NTBkOWYxYmYyYTk5MmE3YThkYWE2ZGMzNGY4MDQ1YWQ0ZTZlMGM0MjlkMzM0ZWVlYWFlZmQ3ZTIzZDQ4MTBiZTAwZTRjYzE0OTJjYmEzMjViYTgxZmYyZDVhNWIzMDVhOGQxN2ViM2JmNGEwNmEzNDlkMzkyZTAwZDMyOTc0NGE1MTc5MzgwMzQ0ZTgyYTE4YzQ3OTMzNDM4Zjg5MWUyMmFlZWY4MTJkNjljOGY3NWUzMjZjYjcwZWEwMDBjM2Y3NzZkZmRiZDYwNDYzOGMyZWY3MTdmYzI2ZDAyZTE3IiwicSI6ImUyMWUwNGY5MTFkMWVkNzk5MTAwOGVjYWFiM2JmNzc1OTg0MzA5YzMiLCJnIjoiYzUyYTRhMGZmM2I3ZTYxZmRmMTg2N2NlODQxMzgzNjlhNjE1NGY0YWZhOTI5NjZlM2M4MjdlMjVjZmE2Y2Y1MDhiOTBlNWRlNDE5ZTEzMzdlMDdhMmU5ZTJhM2NkNWRlYTcwNGQxNzVmOGViZjZhZjM5N2Q2OWUxMTBiOTZhZmIxN2M3YTAzMjU5MzI5ZTQ4MjliMGQwM2JiYzc4OTZiMTViNGFkZTUzZTEzMDg1OGNjMzRkOTYyNjlhYTg5MDQxZjQwOTEzNmM3MjQyYTM4ODk1YzlkNWJjY2FkNGYzODlhZjFkN2E0YmQxMzk4YmQwNzJkZmZhODk2MjMzMzk3YSJ9LCJwcmluY2lwYWwiOiIwMzgxOTgyYS0xZTgzLTI1NjYtNjgzZS05MDRmNDA0NGM1MGRAbXNpc2RuLWRldi5zdGFnZS5tb3phd3MubmV0IiwiaWF0IjoxNDA0NDY5NzgyODc3LCJleHAiOjE0MDQ0OTEzOTI4NzcsImlzcyI6Im1zaXNkbi1kZXYuc3RhZ2UubW96YXdzLm5ldCJ9."
|
||||
|
||||
// === Helpers ===
|
||||
|
||||
function addPermission(aAction) {
|
||||
let uri = Cc["@mozilla.org/network/io-service;1"]
|
||||
.getService(Ci.nsIIOService)
|
||||
.newURI(ORIGIN, null, null);
|
||||
let _principal = Cc["@mozilla.org/scriptsecuritymanager;1"]
|
||||
.getService(Ci.nsIScriptSecurityManager)
|
||||
.getAppCodebasePrincipal(uri, APP_ID, false);
|
||||
let pm = Cc["@mozilla.org/permissionmanager;1"]
|
||||
.getService(Ci.nsIPermissionManager);
|
||||
pm.addFromPrincipal(_principal, MOBILEID_PERM, aAction);
|
||||
}
|
||||
|
||||
function removePermission() {
|
||||
let uri = Cc["@mozilla.org/network/io-service;1"]
|
||||
.getService(Ci.nsIIOService)
|
||||
.newURI(ORIGIN, null, null);
|
||||
let _principal = Cc["@mozilla.org/scriptsecuritymanager;1"]
|
||||
.getService(Ci.nsIScriptSecurityManager)
|
||||
.getAppCodebasePrincipal(uri, APP_ID, false);
|
||||
let pm = Cc["@mozilla.org/permissionmanager;1"]
|
||||
.getService(Ci.nsIPermissionManager);
|
||||
pm.removeFromPrincipal(_principal, MOBILEID_PERM);
|
||||
}
|
||||
|
||||
// === Mocks ===
|
||||
|
||||
let Mock = function(aOptions) {
|
||||
if (!aOptions) {
|
||||
aOptions = {};
|
||||
}
|
||||
this._options = aOptions;
|
||||
this._spied = {};
|
||||
};
|
||||
|
||||
Mock.prototype = {
|
||||
_: function(aMethod) {
|
||||
DEBUG && do_print("_ " + aMethod + JSON.stringify(this._spied));
|
||||
let self = this;
|
||||
return {
|
||||
callsLength: function(aNumberOfCalls) {
|
||||
if (aNumberOfCalls == 0) {
|
||||
do_check_eq(self._spied[aMethod], undefined);
|
||||
return;
|
||||
}
|
||||
do_check_eq(self._spied[aMethod].length, aNumberOfCalls);
|
||||
},
|
||||
call: function(aCallNumber) {
|
||||
return {
|
||||
arg: function(aArgNumber, aValue) {
|
||||
let _arg = self._spied[aMethod][aCallNumber - 1][aArgNumber - 1];
|
||||
if (Array.isArray(aValue)) {
|
||||
do_check_eq(_arg.length, aValue.length)
|
||||
for (let i = 0; i < _arg.length; i++) {
|
||||
do_check_eq(_arg[i], aValue[i]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (typeof aValue === 'object') {
|
||||
do_check_eq(JSON.stringify(_arg), JSON.stringify(aValue));
|
||||
return;
|
||||
}
|
||||
|
||||
do_check_eq(_arg, aValue);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_spy: function(aMethod, aArgs) {
|
||||
DEBUG && do_print(aMethod + " - " + JSON.stringify(aArgs));
|
||||
if (!this._spied[aMethod]) {
|
||||
this._spied[aMethod] = [];
|
||||
}
|
||||
this._spied[aMethod].push(aArgs);
|
||||
},
|
||||
|
||||
getSpiedCalls: function(aMethod) {
|
||||
return this._spied[aMethod];
|
||||
}
|
||||
};
|
||||
|
||||
// UI Glue mock up.
|
||||
let MockUi = function(aOptions) {
|
||||
Mock.call(this, aOptions);
|
||||
};
|
||||
|
||||
MockUi.prototype = {
|
||||
__proto__: Mock.prototype,
|
||||
|
||||
_startFlowResult: {
|
||||
phoneNumber: PHONE_NUMBER,
|
||||
mcc: MNC
|
||||
},
|
||||
|
||||
_verifyCodePromptResult: {
|
||||
verificationCode: VERIFICATION_CODE
|
||||
},
|
||||
|
||||
startFlow: function() {
|
||||
this._spy("startFlow", arguments);
|
||||
return Promise.resolve(this._options.startFlowResult ||
|
||||
this._startFlowResult);
|
||||
},
|
||||
|
||||
verificationCodePrompt: function() {
|
||||
this._spy("verifyCodePrompt", arguments);
|
||||
return Promise.resolve(this._options.verificationCodePromptResult ||
|
||||
this._verifyCodePromptResult);
|
||||
},
|
||||
|
||||
verify: function() {
|
||||
this._spy("verify", arguments);
|
||||
},
|
||||
|
||||
error: function() {
|
||||
this._spy("error", arguments);
|
||||
},
|
||||
|
||||
verified: function() {
|
||||
this._spy("verified", arguments);
|
||||
},
|
||||
|
||||
set oncancel(aCallback) {
|
||||
},
|
||||
|
||||
set onresendcode(aCallback) {
|
||||
}
|
||||
};
|
||||
|
||||
// Save original credential store instance.
|
||||
const kMobileIdentityCredStore = MobileIdentityManager.credStore;
|
||||
|
||||
// Credentials store mock up.
|
||||
let MockCredStore = function(aOptions) {
|
||||
Mock.call(this, aOptions);
|
||||
};
|
||||
|
||||
MockCredStore.prototype = {
|
||||
__proto__: Mock.prototype,
|
||||
|
||||
_getByOriginResult: null,
|
||||
|
||||
_getByMsisdnResult: null,
|
||||
|
||||
_getByIccIdResult: null,
|
||||
|
||||
getByOrigin: function() {
|
||||
this._spy("getByOrigin", arguments);
|
||||
let result = this._getByOriginResult;
|
||||
if (this._options.getByOriginResult) {
|
||||
if (Array.isArray(this._options.getByOriginResult)) {
|
||||
result = this._options.getByOriginResult.length ?
|
||||
this._options.getByOriginResult.shift() : null;
|
||||
} else {
|
||||
result = this._options.getByOriginResult;
|
||||
}
|
||||
}
|
||||
return Promise.resolve(result);
|
||||
},
|
||||
|
||||
getByMsisdn: function() {
|
||||
this._spy("getByMsisdn", arguments);
|
||||
return Promise.resolve(this._options.getByMsisdnResult ||
|
||||
this._getByMsisdnResult);
|
||||
},
|
||||
|
||||
getByIccId: function() {
|
||||
this._spy("getByIccId", arguments);
|
||||
return Promise.resolve(this._options.getByIccIdResult ||
|
||||
this._getByIccIdResult);
|
||||
},
|
||||
|
||||
add: function() {
|
||||
this._spy("add", arguments);
|
||||
return Promise.resolve();
|
||||
},
|
||||
|
||||
setDeviceIccIds: function() {
|
||||
this._spy("setDeviceIccIds", arguments);
|
||||
return Promise.resolve();
|
||||
},
|
||||
|
||||
removeOrigin: function() {
|
||||
this._spy("removeOrigin", arguments);
|
||||
return Promise.resolve();
|
||||
},
|
||||
|
||||
delete: function() {
|
||||
this._spy("delete", arguments);
|
||||
return Promise.resolve();
|
||||
}
|
||||
};
|
||||
|
||||
// Save original client instance.
|
||||
const kMobileIdentityClient = MobileIdentityManager.client;
|
||||
|
||||
// Client mock up.
|
||||
let MockClient = function(aOptions) {
|
||||
Mock.call(this, aOptions);
|
||||
};
|
||||
|
||||
MockClient.prototype = {
|
||||
|
||||
__proto__: Mock.prototype,
|
||||
|
||||
_discoverResult: {
|
||||
verificationMethods: ["sms/mt"],
|
||||
verificationDetails: {
|
||||
"sms/mt": {
|
||||
mtSender: "123",
|
||||
url: "https://msisdn.accounts.firefox.com/v1/msisdn/sms/mt/verify"
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
_registerResult: {
|
||||
msisdnSessionToken: SESSION_TOKEN
|
||||
},
|
||||
|
||||
_smsMtVerifyResult: {},
|
||||
|
||||
_verifyCodeResult: {
|
||||
msisdn: PHONE_NUMBER
|
||||
},
|
||||
|
||||
_signResult: {
|
||||
cert: CERTIFICATE
|
||||
},
|
||||
|
||||
hawk: {
|
||||
now: function() {
|
||||
return Date.now();
|
||||
}
|
||||
},
|
||||
|
||||
discover: function() {
|
||||
this._spy("discover", arguments);
|
||||
return Promise.resolve(this._options.discoverResult ||
|
||||
this._discoverResult);
|
||||
},
|
||||
|
||||
register: function() {
|
||||
this._spy("register", arguments);
|
||||
return Promise.resolve(this._options.registerResult ||
|
||||
this._registerResult);
|
||||
},
|
||||
|
||||
smsMtVerify: function() {
|
||||
this._spy("smsMtVerify", arguments);
|
||||
return Promise.resolve(this._options.smsMtVerifyResult ||
|
||||
this._smsMtVerifyResult);
|
||||
},
|
||||
|
||||
verifyCode: function() {
|
||||
this._spy("verifyCode", arguments);
|
||||
return Promise.resolve(this._options.verifyCodeResult ||
|
||||
this._verifyCodeResult);
|
||||
},
|
||||
|
||||
sign: function() {
|
||||
this._spy("sign", arguments);
|
||||
if (this._options.signError) {
|
||||
let error = Array.isArray(this._options.signError) ?
|
||||
this._options.signError.shift() :
|
||||
this._options.signError;
|
||||
return Promise.reject(error);
|
||||
}
|
||||
return Promise.resolve(this._options.signResult || this._signResult);
|
||||
}
|
||||
};
|
||||
|
||||
// The test rely on having an app registered. Otherwise, it will throw.
|
||||
// Override XULAppInfo.
|
||||
const XUL_APP_INFO_UUID = Components.ID("{84fdc459-d96d-421c-9bff-a8193233ae75}");
|
||||
const XUL_APP_INFO_CONTRACT_ID = "@mozilla.org/xre/app-info;1";
|
||||
|
||||
let (XULAppInfo = {
|
||||
vendor: "Mozilla",
|
||||
name: "MobileIdTest",
|
||||
ID: "{230de50e-4cd1-11dc-8314-0800200b9a66}",
|
||||
version: "1",
|
||||
appBuildID: "2007010101",
|
||||
platformVersion: "",
|
||||
platformBuildID: "2007010101",
|
||||
inSafeMode: false,
|
||||
logConsoleErrors: true,
|
||||
OS: "XPCShell",
|
||||
XPCOMABI: "noarch-spidermonkey",
|
||||
|
||||
QueryInterface: XPCOMUtils.generateQI([
|
||||
Ci.nsIXULAppInfo,
|
||||
Ci.nsIXULRuntime,
|
||||
])
|
||||
}) {
|
||||
let XULAppInfoFactory = {
|
||||
createInstance: function (outer, iid) {
|
||||
if (outer != null) {
|
||||
throw Cr.NS_ERROR_NO_AGGREGATION;
|
||||
}
|
||||
return XULAppInfo.QueryInterface(iid);
|
||||
}
|
||||
};
|
||||
Cm.QueryInterface(Ci.nsIComponentRegistrar)
|
||||
.registerFactory(XUL_APP_INFO_UUID,
|
||||
"XULAppInfo",
|
||||
XUL_APP_INFO_CONTRACT_ID,
|
||||
XULAppInfoFactory);
|
||||
}
|
||||
|
||||
// === Global cleanup ===
|
||||
|
||||
function cleanup() {
|
||||
MobileIdentityManager.credStore = kMobileIdentityCredStore;
|
||||
MobileIdentityManager.client = kMobileIdentityClient;
|
||||
|
@ -431,7 +23,6 @@ function cleanup() {
|
|||
|
||||
// Unregister mocks and restore original code.
|
||||
do_register_cleanup(cleanup);
|
||||
|
||||
// === Tests ===
|
||||
function run_test() {
|
||||
run_next_test();
|
||||
|
|
|
@ -0,0 +1,42 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
"use strict";
|
||||
|
||||
Cu.import("resource://gre/modules/MobileIdentityVerificationFlow.jsm");
|
||||
|
||||
function verifyStrategy() {
|
||||
return Promise.resolve();
|
||||
}
|
||||
|
||||
function cleanupStrategy() {
|
||||
}
|
||||
|
||||
function run_test() {
|
||||
do_print("= Bug 1101444: Invalid verification code shouldn't restart " +
|
||||
"verification flow =");
|
||||
|
||||
let client = new MockClient({
|
||||
// This will emulate two invalid attempts. The third time it will work.
|
||||
verifyCodeError: ["INVALID", "INVALID"]
|
||||
});
|
||||
let ui = new MockUi();
|
||||
|
||||
let verificationFlow = new MobileIdentityVerificationFlow({
|
||||
external: true,
|
||||
sessionToken: SESSION_TOKEN,
|
||||
msisdn: PHONE_NUMBER
|
||||
}, ui, client, verifyStrategy, cleanupStrategy);
|
||||
|
||||
verificationFlow.doVerification().then(() => {
|
||||
// We should only do the registration process once. We only try registering
|
||||
// again when the timeout fires, but not when we enter an invalid
|
||||
// verification code.
|
||||
client._("register").callsLength(1);
|
||||
client._("verifyCode").callsLength(3);
|
||||
// Because we do two invalid attempts, we should show the invalid code error twice.
|
||||
ui._("error").callsLength(2);
|
||||
});
|
||||
|
||||
do_test_finished();
|
||||
};
|
|
@ -1,7 +1,8 @@
|
|||
[DEFAULT]
|
||||
head = head.js
|
||||
tail =
|
||||
skip-if = toolkit == 'gonk'
|
||||
|
||||
[test_mobileid_manager.js]
|
||||
skip-if = 1
|
||||
[test_mobileid_client.js]
|
||||
[test_mobileid_verification_flow.js]
|
||||
|
|
Загрузка…
Ссылка в новой задаче