def login(user): client = Client("https://api.accounts.firefox.com") session = client.login(user['email'], user['password'], keys=True) keyA,keyB = session.fetch_keys() info = b"identity.mozilla.com/picl/v1/oldsync" namespace = b"oldsync" keys = derive_key(secret=keyB, namespace=namespace, size=64) encryption_key = keys[0:32] hmac_key = keys[32:64] # TODO: Store this or a derived longer-lived token # Causes a login event which causes an email # TODO: Should move to use OAuth which solves the long-term cred storage # issue fxab = FxABrowserIDAuth(user['email'], user['password'], with_client_state=True) raw_resp = requests.get('https://token.services.mozilla.com/1.0/sync/1.5', auth=fxab) raw_resp.raise_for_status() hawk_resp = raw_resp.json() return { "hawk_resp": hawk_resp, "hawk_uid": hawk_resp['uid'], "hawk_hashalg": hawk_resp['hashalg'], "hawk_api_endpoint": hawk_resp['api_endpoint'], "hawk_duration": hawk_resp['duration'], "hawk_key": hawk_resp['key'], "hawk_hashed_fxa_uid": hawk_resp['hashed_fxa_uid'], "hawk_id": hawk_resp['id'], 'encryption_key': encryption_key.hex(), 'hmac_key': hmac_key.hex(), }
class FxATestAccount: """A base test class that can be extended by other tests to include utility methods.""" password = ''.join([random.choice(string.ascii_letters) for i in range(8)]) def __init__(self, url=DEV_URL): """ Creates an FxATestAccount object, which includes a verified account. :param url: The url for the api host. Defaults to DEV_URL. """ self.url = url random_string = ''.join( random.choice(string.ascii_lowercase) for _ in range(12)) email_pattern = random_string + '@{hostname}' self.account = TestEmailAccount(email=email_pattern) self.client = Client(self.url) # Create and verify the Firefox account self.session = self.client.create_account(self.account.email, self.password) print('fxapom created an account for email: %s at %s on %s' % (self.account.email, self.url, datetime.now())) m = self.account.wait_for_email( lambda m: "x-verify-code" in m["headers"]) if not m: raise RuntimeError("Verification email was not received") self.session.verify_email_code(m["headers"]["x-verify-code"]) def __del__(self): """ Deletes the Firefox Account that was created during __init__. """ try: self.account.clear() self.client.destroy_account(self.email, self.password) print('fxapom deleted the account for email: %s at %s on %s' % (self.account.email, self.url, datetime.now())) except ClientError as err: # 'Unknown Account' error is ok - account already deleted # https://github.com/mozilla/fxa-auth-server/blob/master/docs/api.md#response-format if err.errno == 102: return raise def login(self): try: session = self.client.login(self.email, self.password) return session except ClientError as err: # 'Unknown Account' error is the only one we care about and will # cause us to throw a custom exception # https://github.com/mozilla/fxa-auth-server/blob/master/docs/api.md#response-format if err.errno == 102: raise AccountNotFoundException('FxA Account Not Found') raise @property def email(self): return self.account.email @property def is_verified(self): return self.session.get_email_status()['verified']
def __init__(self, server=_FXA_SERVER, oauth=_FXA_OAUTH, password=_PWD): self.server = server self.oauth = oauth self.session = self.token = None self.password = password self.acct = TestEmailAccount() self.client = Client(_FXA_SERVER)
class FxATestAccount: """A base test class that can be extended by other tests to include utility methods.""" password = ''.join([random.choice(string.letters) for i in range(8)]) def __init__(self, url=DEV_URL): """ Creates an FxATestAccount object, which includes a verified account. :param url: The url for the api host. Defaults to DEV_URL. """ self.url = url random_string = ''.join(random.choice(string.ascii_lowercase) for _ in range(12)) email_pattern = random_string + '@{hostname}' self.account = TestEmailAccount(email=email_pattern) self.client = Client(self.url) # Create and verify the Firefox account self.session = self.client.create_account(self.account.email, self.password) print 'fxapom created an account for email: %s at %s on %s' % ( self.account.email, self.url, datetime.now()) m = self.account.wait_for_email(lambda m: "x-verify-code" in m["headers"]) if not m: raise RuntimeError("Verification email was not received") self.session.verify_email_code(m["headers"]["x-verify-code"]) def __del__(self): """ Deletes the Firefox Account that was created during __init__. """ try: self.account.clear() self.client.destroy_account(self.email, self.password) print 'fxapom deleted the account for email: %s at %s on %s' % ( self.account.email, self.url, datetime.now()) except ClientError as err: # 'Unknown Account' error is ok - account already deleted # https://github.com/mozilla/fxa-auth-server/blob/master/docs/api.md#response-format if err.errno == 102: return raise def login(self): try: session = self.client.login(self.email, self.password) return session except ClientError as err: # 'Unknown Account' error is the only one we care about and will # cause us to throw a custom exception # https://github.com/mozilla/fxa-auth-server/blob/master/docs/api.md#response-format if err.errno == 102: raise AccountNotFoundException('FxA Account Not Found') raise @property def email(self): return self.account.email @property def is_verified(self): return self.session.get_email_status()['verified']
def get_browserid_assertion(login, password, fxa_server_url=FXA_SERVER_URL, tokenserver_url=TOKENSERVER_URL): """Trade a user and password for a BrowserID assertion and the client state. """ client = FxAClient(server_url=fxa_server_url) session = client.login(login, password, keys=True) bid_assertion = session.get_identity_assertion(tokenserver_url) _, keyB = session.fetch_keys() return bid_assertion, hexlify(sha256(keyB).digest()[0:16])
def get_browserid_assertion(login, password, fxa_server_url=FXA_SERVER_URL, tokenserver_url=TOKENSERVER_URL): """Trade a user and password for a BrowserID assertion and the client state. """ client = FxAClient(server_url=fxa_server_url) session = client.login(login, password, keys=True) bid_assertion = session.get_identity_assertion(tokenserver_url) _, keyB = session.fetch_keys() if isinstance(keyB, six.text_type): # pragma: no cover keyB = keyB.encode('utf-8') return bid_assertion, hexlify(sha256(keyB).digest()[0:16])
def get_oauth_token(client_id=CLIENT_ID, oauth_server=OAUTH_SERVER, auth_server=AUTH_SERVER, email=EMAIL, password=PASSWORD): if password is None: raise Exception('You must set FXA_PASSWORD') print('Getting an oauth token from FxA') oauth_client = OAuthClient(client_id, server_url=oauth_server) session = Client(server_url=auth_server).login(email, password=password) assertion = session.get_identity_assertion(oauth_server) return oauth_client.authorize_token(assertion, scope="profile")
class LoadTest(TestCase): server_url = 'https://api-accounts.stage.mozaws.net' def setUp(self): super(LoadTest, self).setUp() self.client = Client(APIClient(self.server_url, session=self.session)) def test_auth_server(self): # Authenticate as a new or existing user. if random.randint(0, 100) < ACCOUNT_CREATE_PERCENT: session = self._authenticate_as_new_user() else: session = self._authenticate_as_existing_user() # Fetch keys and make some number of signing requests. session.fetch_keys() session.check_session_status() session.get_random_bytes() num_sign_reqs = random.randint(SIGN_REQS_MIN, SIGN_REQS_MAX) for i in xrange(num_sign_reqs): session.sign_certificate(DUMMY_PUBLIC_KEY) # Teardown the session, and maybe the whole account. session.destroy_session() if random.randint(0, 100) < ACCOUNT_DELETE_PERCENT: self.client.destroy_account( email=session.email, stretchpwd=self._get_stretchpwd(session.email), ) def _get_stretchpwd(self, email): return hashlib.sha256(email).hexdigest() def _authenticate_as_new_user(self): # Authenticate as a brand-new user account. # Assume it doesn't exist, try to create the account. # But it's not big deal if it happens to already exist. email = "*****@*****.**" % (uniq(),) kwds = { "email": email, "stretchpwd": self._get_stretchpwd(email), "keys": True, "preVerified": True, } try: return self.client.create_account(**kwds) except fxa.errors.ClientError, e: if e.errno != ERROR_ACCOUNT_EXISTS: raise kwds.pop("preVerified") return self.client.login(**kwds)
def create(ctx): """Create a Firefox Account.""" account = TestEmailAccount() client = Client(ctx.obj["URL"]) password = "".join([random.choice(string.ascii_letters) for i in range(8)]) session = client.create_account(account.email, password) add(ctx.obj["URL"], account.email, password) click.echo("Account {}!\n{}".format( crayons.yellow("created"), render(ctx.obj["URL"], account.email, password))) message = account.wait_for_email(lambda m: "x-verify-code" in m["headers"]) session.verify_email_code(message["headers"]["x-verify-code"]) click.echo("Account {}! 🎉".format(crayons.green("verified"))) account.clear()
def create_account(self): random_string = ''.join(random.choice(string.ascii_lowercase) for _ in range(12)) email_pattern = random_string + '@{hostname}' self.account = TestEmailAccount(email=email_pattern) client = Client(self.fxa_url) print 'fxapom created an account for email: %s at %s on %s' % ( self.account.email, self.fxa_url, datetime.now()) # Create and verify the Firefox account self.session = client.create_account(self.account.email, self.password) m = self.account.wait_for_email(lambda m: "x-verify-code" in m["headers"]) if not m: raise RuntimeError("Verification email was not received") self.session.verify_email_code(m["headers"]["x-verify-code"]) return self
def test_add_content(env, conf): # Grab a bearer token that we can use to talk to the webextensions endpoint acct = TestEmailAccount() email = acct.email passwd = str(uuid.uuid4()) fxaclient = FxaClient("https://api.accounts.firefox.com") session = fxaclient.create_account(email, passwd) m = acct.wait_for_email(lambda m: "x-verify-code" in m["headers"]) if m is None: raise RuntimeErrors("Verification email did not arrive") session.verify_email_code(m["headers"]["x-verify-code"]) auth = FxABearerTokenAuth( email, passwd, scopes=['sync:addon_storage'], client_id=DEFAULT_CLIENT_ID, account_server_url=conf.get(env, 'account_server_url'), oauth_server_url=conf.get(env, 'oauth_server_url'), ) client = Client(server_url=conf.get(env, 'we_server_url'), auth=auth) # Add a record to our QA collection and make sure we have N+1 records existing_records = client.get_records(collection=conf.get( env, 'qa_collection'), bucket='default') assert len(existing_records) == 0 data = {"payload": {"encrypted": "SmluZ28gdGVzdA=="}} resp = client.create_record(data=data, collection=conf.get(env, 'qa_collection'), bucket='default') new_record_id = resp['data']['id'] updated_records = client.get_records(collection=conf.get( env, 'qa_collection'), bucket='default') assert len(updated_records) == len(existing_records) + 1 client.delete_record(id=new_record_id, collection=conf.get(env, 'qa_collection')) updated_records = client.get_records(collection=conf.get( env, 'qa_collection'), bucket='default') assert len(updated_records) == len(existing_records) # Clean up the account that we created for the test acct.clear() fxaclient.destroy_account(email, passwd)
def destroy(ctx, _all, email, password): """Destroy a Firefox Account.""" accounts = [] stored = load() try: if email and password: account = { "url": ctx.obj["URL"], "email": email, "password": password } accounts.append(account) stored.remove(account) elif email: click.echo("You must specify a {}! 🔑".format( crayons.magenta("--password"))) exit(1) else: if _all and stored: accounts.extend(stored) stored = [] else: accounts.append(stored.pop()) except ValueError: pass # account unknown to .accounts except IndexError: click.echo("No account to destroy! 🎻") exit(1) for account in accounts: client = Client(account["url"]) try: client.destroy_account(account["email"], account["password"]) click.echo("Account {}! 💥\n{}".format( crayons.red("destroyed"), render(account["url"], account["email"], account["password"]), )) except ClientError as e: if e.errno == 102: click.echo("Account {}! 🔍\n{}".format( crayons.cyan("unknown"), render(account["url"], account["email"], account["password"]), )) else: raise save(stored)
def _create_fxa_user(self): """Create fxa user for logging in.""" fxa_client = Client(ENVIRONMENT_URLS['stable']['authentication']) account = TestEmailAccount(email=self.fxa_email) password = self.fxa_password FxAccount = collections.namedtuple('FxAccount', 'email password') fxa_account = FxAccount(email=account.email, password=password) session = fxa_client.create_account(fxa_account.email, fxa_account.password) account.fetch() message = account.wait_for_email(lambda m: 'x-verify-code' in m[ 'headers'] and session.uid == m['headers']['x-uid']) session.verify_email_code(message['headers']['x-verify-code']) log.info('fxa account created: {}'.format(fxa_account)) return session.uid
def _create_fxa_user(self): """Create fxa user for logging in.""" fxa_client = Client(ENVIRONMENT_URLS['stable']['authentication']) account = TestEmailAccount(email=self.fxa_email) password = self.fxa_password FxAccount = collections.namedtuple('FxAccount', 'email password') fxa_account = FxAccount(email=account.email, password=password) session = fxa_client.create_account(fxa_account.email, fxa_account.password) account.fetch() message = account.wait_for_email( lambda m: 'x-verify-code' in m['headers'] and session.uid == m['headers']['x-uid'] ) session.verify_email_code(message['headers']['x-verify-code']) log.debug('fxa account created: {}'.format(fxa_account)) return session.uid
def init(self, account_server, token_server, username, password): logging.debug("init()") self.account_server = account_server self.token_server = token_server self.username = username self.password = password self.client = Client(account_server)
def __init__(self, url=DEV_URL): """ Creates an FxATestAccount object, which includes a verified account. :param url: The url for the api host. Defaults to DEV_URL. """ self.url = url random_string = ''.join(random.choice(string.ascii_lowercase) for _ in range(12)) email_pattern = random_string + '@{hostname}' self.account = TestEmailAccount(email=email_pattern) self.client = Client(self.url) # Create and verify the Firefox account self.session = self.client.create_account(self.account.email, self.password) print('fxapom created an account for email: %s at %s on %s' % ( self.account.email, self.url, datetime.now())) m = self.account.wait_for_email(lambda m: "x-verify-code" in m["headers"]) if not m: raise RuntimeError("Verification email was not received") self.session.verify_email_code(m["headers"]["x-verify-code"])
def create_account_and_token(args): acct = TestEmailAccount() client = Client("https://api.accounts.firefox.com") session = client.create_account(acct.email, 'MySecretPassword') m = acct.wait_for_email(lambda m: "x-verify-code" in m["headers"]) if m is None: raise RuntimeError("verification email did not arrive") session.verify_email_code(m["headers"]["x-verify-code"]) _FXA['token'] = get_bearer_token( acct.email, 'MySecretPassword', account_server_url="https://api.accounts.firefox.com/v1", oauth_server_url="https://oauth.accounts.firefox.com/v1", scopes=['sync:addon_storage'], client_id=DEFAULT_CLIENT_ID) _FXA['acct'] = acct _FXA['client'] = client
def get_auth(self): acct = TestEmailAccount(self.email) client = Client(self.server) session = client.create_account(self.email, password=self.password) def is_verify_email(m): return "x-verify-code" in m["headers"] message = acct.wait_for_email(is_verify_email) session.verify_email_code(message["headers"]["x-verify-code"]) url = urlparse(SERVER_URL) audience = "%s://%s" % (url.scheme, url.hostname) return FxABrowserIDAuth( self.email, password=self.password, audience=audience, server_url=self.server)
def test_email_code_verification(self): self.client = Client(self.server_url) # Create a fresh testing account. self.acct = TestEmailAccount() self.client.create_account( email=self.acct.email, stretchpwd=DUMMY_STRETCHED_PASSWORD, ) def wait_for_email(m): return "x-uid" in m["headers"] and "x-verify-code" in m["headers"] m = self.acct.wait_for_email(wait_for_email) if not m: raise RuntimeError("Verification email was not received") # If everything went well, verify_email_code should return an empty json object response = self.client.verify_email_code(m["headers"]["x-uid"], m["headers"]["x-verify-code"]) self.assertEquals(response, {})
def login(user): """ Logs a user into their Firefox account and returns tempoary credentials for use by AuthRequest. """ # TODO: pull out the urls to be part of the config. client = Client("https://api.accounts.firefox.com") session = client.login(user['email'], user['password'], keys=True) keyA, keyB = session.fetch_keys() # Magic strings from the docs # https://moz-services-docs.readthedocs.io/en/latest/sync/storageformat5.html info = b"identity.mozilla.com/picl/v1/oldsync" namespace = b"oldsync" keys = derive_key(secret=keyB, namespace=namespace, size=64) encryption_key = keys[0:32] hmac_key = keys[32:64] # TODO: Store this or a derived longer-lived token # Causes a login event which causes an email # TODO: Should move to use OAuth which solves the long-term cred storage # issue fxab = FxABrowserIDAuth(user['email'], user['password'], with_client_state=True) raw_resp = requests.get('https://token.services.mozilla.com/1.0/sync/1.5', auth=fxab) raw_resp.raise_for_status() hawk_resp = raw_resp.json() return { "hawk_resp": hawk_resp, "hawk_uid": hawk_resp['uid'], "hawk_hashalg": hawk_resp['hashalg'], "hawk_api_endpoint": hawk_resp['api_endpoint'], "hawk_duration": hawk_resp['duration'], "hawk_key": hawk_resp['key'], "hawk_hashed_fxa_uid": hawk_resp['hashed_fxa_uid'], "hawk_id": hawk_resp['id'], 'encryption_key': encryption_key.hex(), 'hmac_key': hmac_key.hex(), }
def get_auth(self): self.account_server_url = os.getenv("FXA_URL", DEFAULT_FXA_URL) random_user = uuid.uuid4().hex self.user_email = "*****@*****.**" % random_user acct = TestEmailAccount(self.user_email) client = Client(self.account_server_url) fxa_session = client.create_account(self.user_email, password=random_user) def is_verify_email(m): return "x-verify-code" in m["headers"] message = acct.wait_for_email(is_verify_email) fxa_session.verify_email_code(message["headers"]["x-verify-code"]) url = urlparse(self.base_url) audience = "%s://%s" % (url.scheme, url.hostname) return FxABrowserIDAuth( self.user_email, password=random_user, audience=audience, server_url=self.account_server_url)
def __init__(self, server_url=None): if server_url is None: server_url = self.server_url self.session = requests.Session() apiclient = APIClient(self.server_url, session=self.session) self.client = Client(apiclient) # setup to capture req/res timings self._patch_send() self._patch_getresponse() self.timing_data = {} self.current_uri = () self.current_start = 0
def setUp(self): self.client = Client(self.server_url) # Create a fresh testing account. self.acct = TestEmailAccount() self.stretchpwd = quick_stretch_password( self.acct.email, DUMMY_PASSWORD, ) self.session = self.client.create_account( email=self.acct.email, stretchpwd=self.stretchpwd, keys=True, ) # Verify the account so that we can actually use the session. m = self.acct.wait_for_email(lambda m: "x-verify-code" in m["headers"]) if not m: raise RuntimeError("Verification email was not received") self.session.verify_email_code(m["headers"]["x-verify-code"]) # Fetch the keys. self.session.fetch_keys() self.assertEqual(len(self.session.keys), 2) self.assertEqual(len(self.session.keys[0]), 32) self.assertEqual(len(self.session.keys[1]), 32)
class FXATestAccount(object): def __init__(self, server=_FXA_SERVER, oauth=_FXA_OAUTH, password=_PWD): self.server = server self.oauth = oauth self.session = self.token = None self.password = password self.acct = TestEmailAccount() self.client = Client(_FXA_SERVER) def _verify(self, session, response): code = response["headers"].get('x-verify-code') if code is None: return False session.verify_email_code(code) return True def create(self): session = self.client.create_account(self.acct.email, self.password) m = self.acct.wait_for_email(functools.partial(self._verify, session)) if m is None: raise RuntimeError("verification email did not arrive") self.token = get_bearer_token(self.acct.email, self.password, account_server_url=self.server + "/v1", oauth_server_url=self.oauth, scopes=['sync:addon_storage'], client_id=DEFAULT_CLIENT_ID) def cleanup(self): self.acct.clear() self.client.destroy_account(self.acct.email, self.password) def authorization(self): if self.token is None: raise RuntimeError("You need to call create() first") return "Bearer %s" % self.token
class WeaveAccountFxA(object): """Encapsulates the cryptographic context for the OnePw account and token server.""" account_server = None token_server = None username = None password = None synckey = None client = None session = None session_keys = False def init(self, account_server, token_server, username, password): logging.debug("init()") self.account_server = account_server self.token_server = token_server self.username = username self.password = password self.client = Client(account_server) def get_session(self, keys=False): if (self.session == None or (keys and not session_keys)): if (self.session != None): session.destroy_session() self.session = self.client.login(self.username, self.password, keys=keys) self.session_keys = keys return self.session def get_auth_token(self, audience=None, keypair=None, certificate=None, synckey=None): if synckey == None: synckey = self.get_synckey() #get_session() must come after get_synckey() to make sure we have the same session object session = self.get_session() return getSyncAuthToken(session, self.token_server, synckey, audience=audience, keypair=keypair, certificate=certificate) def get_synckey(self): if self.synckey == None: self.synckey = self.get_session(True).fetch_keys()[1] return self.synckey
def __init__(self, url=DEV_URL): """ Creates an FxATestAccount object, which includes a verified account. :param url: The url for the api host. Defaults to DEV_URL. """ self.url = url random_string = ''.join(random.choice(string.ascii_lowercase) for _ in range(12)) email_pattern = random_string + '@{hostname}' self.account = TestEmailAccount(email=email_pattern) self.client = Client(self.url) # Create and verify the Firefox account self.session = self.client.create_account(self.account.email, self.password) print 'fxapom created an account for email: %s at %s on %s' % ( self.account.email, self.url, datetime.now()) m = self.account.wait_for_email(lambda m: "x-verify-code" in m["headers"]) if not m: raise RuntimeError("Verification email was not received") self.session.verify_email_code(m["headers"]["x-verify-code"])
def setUpClass(cls): # Create an ephemeral email account to use to create an FxA account cls.acct = TestEmailAccount() cls.client = Client(FXA_ACCOUNT_STAGE_HOST) cls.oauth_client = OAuthClient(CLIENT_ID, None, server_url=FXA_OAUTH_STAGE_HOST) cls.fxa_password = cls._generate_password() # Create an FxA account for these end-to-end tests cls.session = cls.client.create_account(cls.acct.email, password=cls.fxa_password) # Loop until we receive the verification email from FxA while not cls.acct.messages: time.sleep(0.5) cls.acct.fetch() # Find the message containing the verification code and verify the # code for m in cls.acct.messages: if 'x-verify-code' in m['headers']: cls.session.verify_email_code(m['headers']['x-verify-code']) # Create an OAuth token to be used for the end-to-end tests cls.oauth_token = cls.oauth_client.authorize_token(cls.session, SCOPE) cls.browserid_assertion = \ cls.session.get_identity_assertion(BROWSERID_AUDIENCE)
import hashlib import hmac config_file_name = 'config.test.ini' config = ConfigParser() config.read(config_file_name) hawk_resp = None encryption_key = None hmac_key = None if 'hawk' not in config or 'fxa' not in config: user = config['user'] client = Client("https://api.accounts.firefox.com") session = client.login(user['email'], user['password'], keys=True) keyA, keyB = session.fetch_keys() info = b"identity.mozilla.com/picl/v1/oldsync" namespace = b"oldsync" keys = derive_key(secret=keyB, namespace=namespace, size=64) encryption_key = keys[0:32] hmac_key = keys[32:64] # TODO: Store this or a derived longer-lived token # Causes a login event which causes an email fxab = FxABrowserIDAuth(user['email'], user['password'], with_client_state=True)
class FxATiming(object): server_url = 'https://api-accounts.stage.mozaws.net/v1' def __init__(self, server_url=None): if server_url is None: server_url = self.server_url self.session = requests.Session() apiclient = APIClient(self.server_url, session=self.session) self.client = Client(apiclient) # setup to capture req/res timings self._patch_send() self._patch_getresponse() self.timing_data = {} self.current_uri = () self.current_start = 0 # hook into http request for timing def _patch_send(self): old_send = httplib.HTTPConnection.send def new_send(context, data): self.current_start = time.time() lines = data.split('\r\n') [method, path, proto] = re.split('\s+', lines.pop(0)) path = urlparse(path).path headers = {} for line in lines: if len(line) == 0: break [key, val] = line.split(': ') headers[key.lower()] = val request = (headers['host'], method, path) self.current_uri = request if self.current_uri not in self.timing_data: self.timing_data[self.current_uri] = { 'times': [], 'codes': [] } log_request(data) req = old_send(context, data) return req httplib.HTTPConnection.send = new_send # hook into http response for timing def _patch_getresponse(self): old_getresponse = httplib.HTTPConnection.getresponse def new_getresponse(context): res = old_getresponse(context) elapsed = time.time() - self.current_start log_response(res) self.timing_data[self.current_uri]['times'].append(elapsed) self.timing_data[self.current_uri]['codes'].append(res.status) self.current_uri = () self.current_start = 0 return res httplib.HTTPConnection.getresponse = new_getresponse def _get_stretchpwd(self, email): return hashlib.sha256(email).hexdigest() def _get_user_email(self): uid = uniq() self.user_email = "fxa-timing-{}@restmail.net".format(uid) return self.user_email def _get_existing_user_email(self): uid = random.randint(1, 999) return "loads-fxa-{}[email protected]".format(uid) def dump_requests(self): for k, v in self.timing_data.items(): if k[0] == 'restmail.net': continue args = (k[0], k[1], k[2], v['codes'][0], v['times'][0]) print "%s %-4s %-32s %3d %.4f" % args def run(self): self.login_session_flow() # self.password_reset_flow() self.dump_requests() def login_session_flow(self): """Do a full login-flow with cert signing etc.""" # Login as a new user. session = self._authenticate_as_new_user() session.get_email_status() session.fetch_keys() session.check_session_status() session.get_random_bytes() session.sign_certificate(DUMMY_PUBLIC_KEY) base_url = self.server_url[:-3] self.session.get(base_url + "/.well-known/browserid") stretchpwd = self._get_stretchpwd(session.email) self.client.change_password( session.email, oldstretchpwd=stretchpwd, newstretchpwd=stretchpwd, ) kwds = { "email": session.email, "stretchpwd": stretchpwd, "keys": True } session = self.client.login(**kwds) pftok = self.client.send_reset_code(session.email) pftok.get_status() # verify the password forgot code. acct = Restmail(email=session.email) mail = acct.wait_for_email(lambda m: "x-recovery-code" in m["headers"]) if not mail: raise RuntimeError("Password reset email was not received") acct.clear() code = mail["headers"]["x-recovery-code"] # Now verify with the actual code, and reset the account. artok = pftok.verify_code(code) self.client.reset_account( email=session.email, token=artok, stretchpwd=stretchpwd ) session = self.client.login(**kwds) session.destroy_session() self.client.destroy_account(email=session.email, stretchpwd=stretchpwd) def _authenticate_as_new_user(self): email = self._get_user_email() stretchpwd = self._get_stretchpwd(email) kwds = { "email": email, "stretchpwd": stretchpwd, "keys": True } session = self.client.create_account(**kwds) # resend the confirmation email. session.resend_email_code() # verify the confirmation code. acct = Restmail(email=email) mail = acct.wait_for_email(lambda m: "x-verify-code" in m["headers"]) if not mail: raise RuntimeError("Verification email was not received") acct.clear() session.verify_email_code(mail["headers"]["x-verify-code"]) return self.client.login(**kwds)
class TestCoreClientSession(unittest.TestCase): server_url = TEST_SERVER_URL def setUp(self): self.client = Client(self.server_url) # Create a fresh testing account. self.acct = TestEmailAccount() self.stretchpwd = quick_stretch_password( self.acct.email, DUMMY_PASSWORD, ) self.session = self.client.create_account( email=self.acct.email, stretchpwd=self.stretchpwd, keys=True, ) # Verify the account so that we can actually use the session. m = self.acct.wait_for_email(lambda m: "x-verify-code" in m["headers"]) if not m: raise RuntimeError("Verification email was not received") self.session.verify_email_code(m["headers"]["x-verify-code"]) # Fetch the keys. self.session.fetch_keys() self.assertEqual(len(self.session.keys), 2) self.assertEqual(len(self.session.keys[0]), 32) self.assertEqual(len(self.session.keys[1]), 32) def tearDown(self): # Clean up the session and account. # This might fail if the test already cleaned it up. try: self.session.destroy_session() except fxa.errors.ClientError: pass try: self.client.destroy_account( email=self.acct.email, stretchpwd=self.stretchpwd, ) except fxa.errors.ClientError: pass self.acct.clear() def test_session_status(self): self.session.check_session_status() self.session.destroy_session() with self.assertRaises(fxa.errors.ClientError): self.session.check_session_status() def test_email_status(self): status = self.session.get_email_status() self.assertTrue(status["verified"]) def test_get_random_bytes(self): b1 = self.session.get_random_bytes() b2 = self.session.get_random_bytes() self.assertTrue(isinstance(b1, six.binary_type)) self.assertNotEqual(b1, b2) def test_sign_certificate(self): email = self.acct.email pubkey = browserid.tests.support.get_keypair(email)[0] cert = self.session.sign_certificate(pubkey) issuer = browserid.utils.decode_json_bytes(cert.split(".")[1])["iss"] expected_issuer = urlparse.urlparse(self.client.server_url).hostname self.assertEqual(issuer, expected_issuer) def test_change_password(self): # Change the password. newpwd = mutate_one_byte(DUMMY_PASSWORD) self.stretchpwd = quick_stretch_password(self.acct.email, newpwd) self.session.change_password(DUMMY_PASSWORD, newpwd) # Check that we can use the new password. session2 = self.client.login(self.acct.email, newpwd, keys=True) # Check that encryption keys have been preserved. session2.fetch_keys() self.assertEquals(self.session.keys, session2.keys)
def setUp(self): super(LoadTest, self).setUp() self.client = Client(APIClient(self.server_url, session=self.session))
def __init__(self): """ Init client """ from fxa.core import Client as FxAClient self.__fxa_client = FxAClient()
def get_fxa_client(): fxa_env = os.getenv('FXA_ENV', 'stable') return Client(ENVIRONMENT_URLS[fxa_env]['authentication'])
def fxa_client(fxa_urls): return Client(fxa_urls['authentication'])
class MozillaSync(object): """ Sync client """ def __init__(self): """ Init client """ from fxa.core import Client as FxAClient self.__fxa_client = FxAClient() def login(self, login, password): """ Login to FxA and get the keys. @param login as str @param password as str @return fxaSession """ fxaSession = self.__fxa_client.login(login, password, keys=True) fxaSession.fetch_keys() return fxaSession def connect(self, bid_assertion, key): """ Connect to sync using FxA browserid assertion @param session as fxaSession @return bundle keys as KeyBundle """ state = None if key is not None: from binascii import hexlify state = hexlify(sha256(key).digest()[0:16]) self.__client = SyncClient(bid_assertion, state) sync_keys = KeyBundle.fromMasterKey( key, "identity.mozilla.com/picl/v1/oldsync") # Fetch the sync bundle keys out of storage. # They're encrypted with the account-level key. keys = self.__decrypt_payload(self.__client.get_record("crypto", "keys"), sync_keys) # There's some provision for using separate # key bundles for separate collections # but I haven't bothered digging through # to see what that's about because # it doesn't seem to be in use, at least on my account. if keys["collections"]: print("no support for per-collection key bundles yet sorry :-(") return None # Now use those keys to decrypt the records of interest. from base64 import b64decode bulk_keys = KeyBundle(b64decode(keys["default"][0]), b64decode(keys["default"][1])) return bulk_keys def get_records(self, collection, bulk_keys): """ Return records payload @param collection as str @param bulk keys as KeyBundle @return [{}] """ records = [] for record in self.__client.get_records(collection): record["payload"] = self.__decrypt_payload(record, bulk_keys) records.append(record) return records def add(self, item, collection, bulk_keys): """ Add bookmark @param bookmark as {} @param collection as str @param bulk_keys as KeyBundle """ payload = self.__encrypt_payload(item, bulk_keys) record = {} record["modified"] = round(time(), 2) record["payload"] = payload record["id"] = item["id"] self.__client.put_record(collection, record) def get_browserid_assertion(self, session, tokenserver_url=TOKENSERVER_URL): """ Get browser id assertion and state @param session as fxaSession @return (bid_assertion, state) as (str, str) """ bid_assertion = session.get_identity_assertion(tokenserver_url) return bid_assertion, session.keys[1] @property def client(self): """ Get client """ return self.__client @property def fxa_client(self): """ Get fxa client """ return self.__fxa_client ####################### # PRIVATE # ####################### def __encrypt_payload(self, record, key_bundle): """ Encrypt payload @param record as {} @param key bundle as KeyBundle @return encrypted record payload """ from Crypto.Cipher import AES from Crypto import Random from hmac import new from base64 import b64encode plaintext = json.dumps(record).encode("utf-8") # Input strings must be a multiple of 16 in length length = 16 - (len(plaintext) % 16) plaintext += bytes([length]) * length iv = Random.new().read(16) aes = AES.new(key_bundle.encryption_key, AES.MODE_CBC, iv) ciphertext = b64encode(aes.encrypt(plaintext)) _hmac = new(key_bundle.hmac_key, ciphertext, sha256).hexdigest() payload = {"ciphertext": ciphertext.decode("utf-8"), "IV": b64encode(iv).decode("utf-8"), "hmac": _hmac} return json.dumps(payload) def __decrypt_payload(self, record, key_bundle): """ Descrypt payload @param record as str (json) @param key bundle as KeyBundle @return uncrypted record payload """ from Crypto.Cipher import AES from hmac import new from base64 import b64decode j = json.loads(record["payload"]) # Always check the hmac before decrypting anything. expected_hmac = new(key_bundle.hmac_key, j['ciphertext'].encode("utf-8"), sha256).hexdigest() if j['hmac'] != expected_hmac: raise ValueError("HMAC mismatch: %s != %s" % (j['hmac'], expected_hmac)) ciphertext = b64decode(j['ciphertext']) iv = b64decode(j['IV']) aes = AES.new(key_bundle.encryption_key, AES.MODE_CBC, iv) plaintext = aes.decrypt(ciphertext).strip().decode("utf-8") # Remove any CBC block padding, # assuming it's a well-formed JSON payload. plaintext = plaintext[:plaintext.rfind("}") + 1] return json.loads(plaintext)
class TestCoreClientSession(unittest.TestCase): server_url = TEST_SERVER_URL def setUp(self): self.client = Client(self.server_url) # Create a fresh testing account. self.acct = TestEmailAccount() self.stretchpwd = quick_stretch_password( self.acct.email, DUMMY_PASSWORD, ) self.session = self.client.create_account( email=self.acct.email, stretchpwd=self.stretchpwd, keys=True, ) # Verify the account so that we can actually use the session. m = self.acct.wait_for_email(lambda m: "x-verify-code" in m["headers"]) if not m: raise RuntimeError("Verification email was not received") self.acct.clear() self.session.verify_email_code(m["headers"]["x-verify-code"]) # Fetch the keys. self.session.fetch_keys() self.assertEqual(len(self.session.keys), 2) self.assertEqual(len(self.session.keys[0]), 32) self.assertEqual(len(self.session.keys[1]), 32) def tearDown(self): # Clean up the session and account. # This might fail if the test already cleaned it up. try: self.session.destroy_session() except fxa.errors.ClientError: pass try: self.client.destroy_account( email=self.acct.email, stretchpwd=self.stretchpwd, ) except fxa.errors.ClientError: pass self.acct.clear() def test_session_status(self): self.session.check_session_status() self.session.destroy_session() with self.assertRaises(fxa.errors.ClientError): self.session.check_session_status() def test_email_status(self): status = self.session.get_email_status() self.assertTrue(status["verified"]) def test_get_random_bytes(self): b1 = self.session.get_random_bytes() b2 = self.session.get_random_bytes() self.assertTrue(isinstance(b1, binary_type)) self.assertNotEqual(b1, b2) def test_sign_certificate(self): email = self.acct.email pubkey = browserid.tests.support.get_keypair(email)[0] cert = self.session.sign_certificate(pubkey) issuer = browserid.utils.decode_json_bytes(cert.split(".")[1])["iss"] expected_issuer = urlparse(self.client.server_url).hostname self.assertEqual(issuer, expected_issuer) def test_sign_certificate_handles_duration(self): email = self.acct.email pubkey = browserid.tests.support.get_keypair(email)[0] millis = int(round(time.time() * 1000)) cert = self.session.sign_certificate(pubkey, duration=4000) cert_exp = browserid.utils.decode_json_bytes(cert.split(".")[1])["exp"] ttl = round(float(cert_exp - millis) / 1000) self.assertGreaterEqual(ttl, 2) self.assertLessEqual(ttl, 30) def test_change_password(self): # Change the password. newpwd = mutate_one_byte(DUMMY_PASSWORD) self.stretchpwd = quick_stretch_password(self.acct.email, newpwd) self.session.change_password(DUMMY_PASSWORD, newpwd) # Check that we can use the new password. session2 = self.client.login(self.acct.email, newpwd, keys=True) if not session2.get_email_status().get("verified"): def has_verify_code(m): return "x-verify-code" in m["headers"] m = self.acct.wait_for_email(has_verify_code) if not m: raise RuntimeError("Verification email was not received") self.acct.clear() session2.verify_email_code(m["headers"]["x-verify-code"]) # Check that encryption keys have been preserved. session2.fetch_keys() self.assertEquals(self.session.keys, session2.keys) def test_get_identity_assertion(self): assertion = self.session.get_identity_assertion("http://example.com") data = browserid.verify(assertion, audience="http://example.com") self.assertEquals(data["status"], "okay") expected_issuer = urlparse(self.session.server_url).hostname self.assertEquals(data["issuer"], expected_issuer) expected_email = "{0}@{1}".format(self.session.uid, expected_issuer) self.assertEquals(data["email"], expected_email) def test_get_identity_assertion_handles_duration(self): millis = int(round(time.time() * 1000)) bid_assertion = self.session.get_identity_assertion( "http://example.com", 1234) cert, assertion = browserid.utils.unbundle_certs_and_assertion( bid_assertion) cert = jwt.parse(cert[0]).payload assertion = jwt.parse(assertion).payload # Validate cert expiry ttl = round(float(cert['exp'] - millis) / 1000) self.assertGreaterEqual(ttl, 1230) self.assertLessEqual(ttl, 1260) # Validate assertion expiry ttl = round(float(assertion['exp'] - millis) / 1000) self.assertGreaterEqual(ttl, 1230) self.assertLessEqual(ttl, 1260) def test_get_identity_assertion_accepts_service(self): # We can't observe any side-effects of sending the service query param, # but we can test that it doesn't error out. assertion = self.session.get_identity_assertion("http://example.com", service="test-me") data = browserid.verify(assertion, audience="http://example.com") self.assertEquals(data["status"], "okay") def test_totp(self): resp = self.session.totp_create() # Double create causes a client error with self.assertRaises(fxa.errors.ClientError): self.session.totp_create() # Created but not verified returns false (and deletes the token) self.assertFalse(self.session.totp_exists()) # Creating again should work this time resp = self.session.totp_create() # Verify the code code = pyotp.TOTP(resp["secret"]).now() self.assertTrue(self.session.totp_verify(code)) self.assertTrue(self.session.verified) # Should exist now self.assertTrue(self.session.totp_exists()) # Remove the code resp = self.session.totp_delete() # And now should not exist self.assertFalse(self.session.totp_exists())
def setUp(self): self.client = Client(self.server_url) self._accounts_to_delete = []
def fxa_client(fxa_urls): return Client(fxa_urls["authentication"])
class TestCoreClient(unittest.TestCase): server_url = TEST_SERVER_URL def setUp(self): self.client = Client(self.server_url) self._accounts_to_delete = [] def tearDown(self): for acct in self._accounts_to_delete: acct.clear() try: stretchpwd = acct.stretchpwd except AttributeError: try: password = acct.password stretchpwd = quick_stretch_password(acct.email, password) except AttributeError: stretchpwd = DUMMY_STRETCHED_PASSWORD self.client.destroy_account(acct.email, stretchpwd=stretchpwd) def test_account_creation(self): acct = TestEmailAccount() acct.password = DUMMY_PASSWORD session = self.client.create_account(acct.email, DUMMY_PASSWORD) self._accounts_to_delete.append(acct) self.assertEqual(session.email, acct.email) self.assertFalse(session.verified) self.assertEqual(session.keys, None) self.assertEqual(session._key_fetch_token, None) with self.assertRaises(Exception): session.fetch_keys() def test_account_creation_with_key_fetch(self): acct = TestEmailAccount() session = self.client.create_account( email=acct.email, stretchpwd=DUMMY_STRETCHED_PASSWORD, keys=True, ) self._accounts_to_delete.append(acct) self.assertEqual(session.email, acct.email) self.assertFalse(session.verified) self.assertEqual(session.keys, None) self.assertNotEqual(session._key_fetch_token, None) def test_account_login(self): acct = TestEmailAccount() session1 = self.client.create_account( email=acct.email, stretchpwd=DUMMY_STRETCHED_PASSWORD, ) self._accounts_to_delete.append(acct) session2 = self.client.login( email=acct.email, stretchpwd=DUMMY_STRETCHED_PASSWORD, ) self.assertEqual(session1.email, session2.email) self.assertNotEqual(session1.token, session2.token) def test_get_random_bytes(self): b1 = self.client.get_random_bytes() b2 = self.client.get_random_bytes() self.assertTrue(isinstance(b1, binary_type)) self.assertNotEqual(b1, b2) def test_resend_verify_code(self): acct = TestEmailAccount() session = self.client.create_account( email=acct.email, stretchpwd=DUMMY_STRETCHED_PASSWORD, ) self._accounts_to_delete.append(acct) def is_verify_email(m): return "x-verify-code" in m["headers"] m1 = acct.wait_for_email(is_verify_email) code1 = m1["headers"]["x-verify-code"] # NOQA acct.clear() session.resend_email_code() # XXX TODO: this won't work against a live server because we # refuse to send duplicate emails within a short timespan. # m2 = acct.wait_for_email(is_verify_email) # code2 = m2["headers"]["x-verify-code"] # self.assertNotEqual(m1, m2) # self.assertEqual(code1, code2) def test_forgot_password_flow(self): acct = TestEmailAccount() self.client.create_account( email=acct.email, stretchpwd=DUMMY_STRETCHED_PASSWORD, ) self._accounts_to_delete.append(acct) # Initiate the password reset flow, and grab the verification code. pftok = self.client.send_reset_code(acct.email, service="foobar") m = acct.wait_for_email(lambda m: "x-recovery-code" in m["headers"]) if not m: raise RuntimeError("Password reset email was not received") acct.clear() code = m["headers"]["x-recovery-code"] # Try with an invalid code to test error handling. tries = pftok.tries_remaining self.assertTrue(tries > 1) with self.assertRaises(Exception): pftok.verify_code(mutate_one_byte(code)) pftok.get_status() self.assertEqual(pftok.tries_remaining, tries - 1) # Re-send the code, as if we've lost the email. pftok.resend_code() m = acct.wait_for_email(lambda m: "x-recovery-code" in m["headers"]) if not m: raise RuntimeError("Password reset email was not received") self.assertEqual(m["headers"]["x-recovery-code"], code) # Now verify with the actual code, and reset the account. artok = pftok.verify_code(code) self.client.reset_account( email=acct.email, token=artok, stretchpwd=DUMMY_STRETCHED_PASSWORD ) def test_email_code_verification(self): self.client = Client(self.server_url) # Create a fresh testing account. self.acct = TestEmailAccount() self.client.create_account( email=self.acct.email, stretchpwd=DUMMY_STRETCHED_PASSWORD, ) def wait_for_email(m): return "x-uid" in m["headers"] and "x-verify-code" in m["headers"] m = self.acct.wait_for_email(wait_for_email) if not m: raise RuntimeError("Verification email was not received") # If everything went well, verify_email_code should return an empty json object response = self.client.verify_email_code(m["headers"]["x-uid"], m["headers"]["x-verify-code"]) self.assertEquals(response, {}) def test_send_unblock_code(self): acct = TestEmailAccount(email="block-{uniq}@{hostname}") self.client.create_account( email=acct.email, stretchpwd=DUMMY_STRETCHED_PASSWORD, ) self._accounts_to_delete.append(acct) # Initiate sending unblock code response = self.client.send_unblock_code(acct.email) self.assertEquals(response, {}) m = acct.wait_for_email(lambda m: "x-unblock-code" in m["headers"]) if not m: raise RuntimeError("Unblock code email was not received") code = m["headers"]["x-unblock-code"] self.assertTrue(len(code) > 0) self.client.login( email=acct.email, stretchpwd=DUMMY_STRETCHED_PASSWORD, unblock_code=code )
class LoadTest(TestCase): server_url = 'https://api-accounts.stage.mozaws.net' def setUp(self): super(LoadTest, self).setUp() self.client = Client(APIClient(self.server_url, session=self.session)) def _pick(self, *choices): """Pick one from a list of (item, weighting) options.""" sum_weights = sum(choice[1] for choice in choices) remainder = random.randint(0, sum_weights - 1) for choice, weight in choices: remainder -= weight if remainder < 0: return choice assert False, "somehow failed to pick from {}".format(choices) def _perc(self, percent): """Decide whether to do something, given desired percentage of runs.""" return random.randint(0, 99) < percent def test_auth_server(self): """Top-level method to run a randomly-secleted auth-server test.""" which_test = self._pick( (self.test_login_session_flow, PERCENT_TEST_LOGIN), (self.test_password_reset_flow, PERCENT_TEST_RESET), (self.test_password_change_flow, PERCENT_TEST_CHANGE), (self.test_support_doc_flow, PERCENT_TEST_SUPPORTDOC), ) which_test() def test_login_session_flow(self): """Do a full login-flow with cert signing etc.""" # Login as either a new or existing user. if self._perc(PERCENT_LOGIN_CREATE): session = self._authenticate_as_new_user() can_delete = True else: session = self._authenticate_as_existing_user() can_delete = False try: # Do a whole lot of account status polling. n_poll_reqs = random.randint(LOGIN_POLL_REQS_MIN, LOGIN_POLL_REQS_MAX) for i in xrange(n_poll_reqs): session.get_email_status() # Always fetch the keys. session.fetch_keys() # Sometimes check the session status. if self._perc(PERCENT_LOGIN_STATUS): session.check_session_status() # Sometimes get some random bytes. if self._perc(PERCENT_LOGIN_RANDBYTES): session.get_random_bytes() # Always do some number of signing requests. n_sign_reqs = random.randint(LOGIN_SIGN_REQS_MIN, LOGIN_SIGN_REQS_MAX) for i in xrange(n_sign_reqs): session.sign_certificate(DUMMY_PUBLIC_KEY) # Sometimes tear down the session. if self._perc(PERCENT_LOGIN_TEARDOWN): session.destroy_session() except fxa.errors.ClientError as e: # There's a small chance this could fail due to concurrent # password change destroying the session token. if e.errno != ERROR_INVALID_TOKEN: raise # Sometimes destroy the account. if can_delete: if self._perc(PERCENT_LOGIN_CREATE_DESTROY): self.client.destroy_account( email=session.email, stretchpwd=self._get_stretchpwd(session.email), ) def _get_stretchpwd(self, email): return hashlib.sha256(email).hexdigest() def _get_new_user_email(self): uid = uniq() return "loads-fxa-{}[email protected]".format(uid) def _get_existing_user_email(self): uid = random.randint(1, 999) return "loads-fxa-{}[email protected]".format(uid) def _authenticate_as_new_user(self): # Authenticate as a brand-new user account. # Assume it doesn't exist, and try to create the account. # But it's not big deal if it happens to already exist. email = self._get_new_user_email() kwds = { "email": email, "stretchpwd": self._get_stretchpwd(email), "keys": True, "preVerified": True, } try: session = self.client.create_account(**kwds) except fxa.errors.ClientError as e: if e.errno != ERROR_ACCOUNT_EXISTS: raise kwds.pop("preVerified") session = self.client.login(**kwds) # Sometimes resend the confirmation email. if self._perc(PERCENT_LOGIN_CREATE_RESEND): session.resend_email_code() # Sometimes (pretend to) verify the confirmation code. if self._perc(PERCENT_LOGIN_CREATE_VERIFY): try: session.verify_email_code(uniq(32)) except fxa.errors.ClientError as e: if e.errno != ERROR_INVALID_CODE: raise return session def _authenticate_as_existing_user(self): # Authenticate as an existing user account. # We select from a small pool of known accounts, creating it # if it does not exist. This should mean that all the accounts # are created quickly at the start of the loadtest run. email = self._get_existing_user_email() kwds = { "email": email, "stretchpwd": self._get_stretchpwd(email), "keys": True, } try: return self.client.login(**kwds) except fxa.errors.ClientError as e: if e.errno != ERROR_UNKNOWN_ACCOUNT: raise kwds["preVerified"] = True # Account creation might likewise fail due to a race. try: return self.client.create_account(**kwds) except fxa.errors.ClientError as e: if e.errno != ERROR_ACCOUNT_EXISTS: raise # Assume a normal login will now succeed. kwds.pop("preVerified") return self.client.login(**kwds) def test_password_reset_flow(self): email = self._get_existing_user_email() pft = self.client.send_reset_code(email) # XXX TODO: how to get the reset code? # I don't want to actually poll restmail during a loadtest... pft.get_status() try: pft.verify_code("0" * 32) except fxa.errors.ClientError as e: if e.errno != ERROR_INVALID_CODE: raise pft.get_status() def test_password_change_flow(self): email = self._get_existing_user_email() stretchpwd = self._get_stretchpwd(email) try: self.client.change_password( email, oldstretchpwd=stretchpwd, newstretchpwd=stretchpwd, ) except fxa.errors.ClientError as e: if e.errno != ERROR_UNKNOWN_ACCOUNT: raise # Create the "existing" account if it doens't yet exist. kwds = { "email": email, "stretchpwd": stretchpwd, "preVerified": True, } try: self.client.create_account(**kwds) except fxa.errors.ClientError as e: if e.errno != ERROR_UNKNOWN_ACCOUNT: raise else: self.client.change_password( email, oldstretchpwd=stretchpwd, newstretchpwd=stretchpwd, ) def test_support_doc_flow(self): base_url = self.server_url[:-3] self.session.get(base_url + "/.well-known/browserid")
def __init__(self): """ Init client """ self.__fxa_client = FxAClient()
def getFxASession(server, username, password, fetch_keys=False): client = Client(server) session = client.login(username, password, keys=fetch_keys) return session
class TestCoreClient(unittest.TestCase): server_url = TEST_SERVER_URL def setUp(self): self.client = Client(self.server_url) self._accounts_to_delete = [] def tearDown(self): for acct in self._accounts_to_delete: acct.clear() try: stretchpwd = acct.stretchpwd except AttributeError: try: password = acct.password stretchpwd = quick_stretch_password(acct.email, password) except AttributeError: stretchpwd = DUMMY_STRETCHED_PASSWORD self.client.destroy_account(acct.email, stretchpwd=stretchpwd) def test_account_creation(self): acct = TestEmailAccount() acct.password = DUMMY_PASSWORD session = self.client.create_account(acct.email, DUMMY_PASSWORD) self._accounts_to_delete.append(acct) self.assertEqual(session.email, acct.email) self.assertFalse(session.verified) self.assertEqual(session.keys, None) self.assertEqual(session._key_fetch_token, None) with self.assertRaises(Exception): session.fetch_keys() def test_account_creation_with_key_fetch(self): acct = TestEmailAccount() session = self.client.create_account( email=acct.email, stretchpwd=DUMMY_STRETCHED_PASSWORD, keys=True, ) self._accounts_to_delete.append(acct) self.assertEqual(session.email, acct.email) self.assertFalse(session.verified) self.assertEqual(session.keys, None) self.assertNotEqual(session._key_fetch_token, None) def test_account_login(self): acct = TestEmailAccount() session1 = self.client.create_account( email=acct.email, stretchpwd=DUMMY_STRETCHED_PASSWORD, ) self._accounts_to_delete.append(acct) session2 = self.client.login( email=acct.email, stretchpwd=DUMMY_STRETCHED_PASSWORD, ) self.assertEqual(session1.email, session2.email) self.assertNotEqual(session1.token, session2.token) def test_get_random_bytes(self): b1 = self.client.get_random_bytes() b2 = self.client.get_random_bytes() self.assertTrue(isinstance(b1, six.binary_type)) self.assertNotEqual(b1, b2) def test_resend_verify_code(self): acct = TestEmailAccount() session = self.client.create_account( email=acct.email, stretchpwd=DUMMY_STRETCHED_PASSWORD, ) self._accounts_to_delete.append(acct) is_verify_email = lambda m: "x-verify-code" in m["headers"] m1 = acct.wait_for_email(is_verify_email) code1 = m1["headers"]["x-verify-code"] # NOQA acct.clear() session.resend_email_code() # XXX TODO: this won't work against a live server because we # refuse to send duplicate emails within a short timespan. # m2 = acct.wait_for_email(is_verify_email) # code2 = m2["headers"]["x-verify-code"] # self.assertNotEqual(m1, m2) # self.assertEqual(code1, code2) def test_forgot_password_flow(self): acct = TestEmailAccount() self.client.create_account( email=acct.email, stretchpwd=DUMMY_STRETCHED_PASSWORD, ) self._accounts_to_delete.append(acct) # Initiate the password reset flow, and grab the verification code. pftok = self.client.send_reset_code(acct.email, service="foobar") m = acct.wait_for_email(lambda m: "x-recovery-code" in m["headers"]) if not m: raise RuntimeError("Password reset email was not received") acct.clear() code = m["headers"]["x-recovery-code"] # Try with an invalid code to test error handling. tries = pftok.tries_remaining self.assertTrue(tries > 1) with self.assertRaises(Exception): pftok.verify_code(mutate_one_byte(code)) pftok.get_status() self.assertEqual(pftok.tries_remaining, tries - 1) # Re-send the code, as if we've lost the email. pftok.resend_code() m = acct.wait_for_email(lambda m: "x-recovery-code" in m["headers"]) if not m: raise RuntimeError("Password reset email was not received") self.assertEqual(m["headers"]["x-recovery-code"], code) # Now verify with the actual code, and reset the account. artok = pftok.verify_code(code) self.client.reset_account(email=acct.email, token=artok, stretchpwd=DUMMY_STRETCHED_PASSWORD)