Ejemplo n.º 1
0
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(),
    }
Ejemplo n.º 2
0
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']
Ejemplo n.º 3
0
 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)
Ejemplo n.º 4
0
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']
Ejemplo n.º 5
0
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])
Ejemplo n.º 6
0
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])
Ejemplo n.º 7
0
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")
Ejemplo n.º 8
0
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)
Ejemplo n.º 9
0
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])
Ejemplo n.º 10
0
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()
Ejemplo n.º 11
0
 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
Ejemplo n.º 12
0
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)
Ejemplo n.º 13
0
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)
Ejemplo n.º 14
0
 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
Ejemplo n.º 15
0
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")
Ejemplo n.º 16
0
 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)
Ejemplo n.º 18
0
    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"])
Ejemplo n.º 19
0
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
Ejemplo n.º 20
0
    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)
Ejemplo n.º 21
0
    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, {})
Ejemplo n.º 22
0
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(),
    }
Ejemplo n.º 23
0
    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)
Ejemplo n.º 24
0
    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
Ejemplo n.º 25
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)
Ejemplo n.º 26
0
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
Ejemplo n.º 28
0
    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"])
Ejemplo n.º 29
0
    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, {})
Ejemplo n.º 30
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)
Ejemplo n.º 31
0
 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)
Ejemplo n.º 32
0
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)
Ejemplo n.º 33
0
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)
Ejemplo n.º 34
0
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)
Ejemplo n.º 35
0
 def setUp(self):
     super(LoadTest, self).setUp()
     self.client = Client(APIClient(self.server_url, session=self.session))
Ejemplo n.º 36
0
 def __init__(self):
     """
         Init client
     """
     from fxa.core import Client as FxAClient
     self.__fxa_client = FxAClient()
Ejemplo n.º 37
0
def get_fxa_client():
    fxa_env = os.getenv('FXA_ENV', 'stable')
    return Client(ENVIRONMENT_URLS[fxa_env]['authentication'])
Ejemplo n.º 38
0
def fxa_client(fxa_urls):
    return Client(fxa_urls['authentication'])
Ejemplo n.º 39
0
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)
Ejemplo n.º 40
0
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())
Ejemplo n.º 41
0
 def setUp(self):
     self.client = Client(self.server_url)
     self._accounts_to_delete = []
Ejemplo n.º 42
0
def fxa_client(fxa_urls):
    return Client(fxa_urls["authentication"])
Ejemplo n.º 43
0
 def setUp(self):
     super(LoadTest, self).setUp()
     self.client = Client(APIClient(self.server_url, session=self.session))
Ejemplo n.º 44
0
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
        )
Ejemplo n.º 45
0
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")
Ejemplo n.º 46
0
 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
Ejemplo n.º 48
0
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)
Ejemplo n.º 49
0
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())
Ejemplo n.º 50
0
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")
Ejemplo n.º 51
0
 def setUp(self):
     self.client = Client(self.server_url)
     self._accounts_to_delete = []
Ejemplo n.º 52
0
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)