def _make_signed_req(self, userid, user): auth_policy = self.config.registry.getUtility(IAuthenticationPolicy) req = Request.blank("http://localhost/") auth_token, auth_secret = auth_policy.encode_hawk_id(req, userid, user) hawkauthlib.sign_request(req, auth_token, auth_secret) req.metrics = {} return req
def main(argv): parser = argparse.ArgumentParser(description="Delete Firefox Sync data") parser.add_argument("email", help="Email of the account for which to delete data") parser.add_argument("--accounts-uri", default=DEFAULT_FXA_URI, help="URI of the Firefox Accounts API server") parser.add_argument("--tokenserver-uri", default=DEFAULT_TOKENSERVER_URI, help="URI of the Firefox Sync tokenserver") args = parser.parse_args(argv) # Sign in to the account. c = fxa.core.Client(args.accounts_uri) password = getpass.getpass("Password for {}: ".format(args.email)) s = c.login(args.email, password, keys=True) try: # Verify the session if necessary. # TODO: this won't work if the user has enabled two-step auth. status = s.get_email_status() if not status["sessionVerified"]: if s.verificationMethod == "totp-2fa": code = raw_input("Enter TOTP code: ") s.totp_verify(code) else: code = raw_input( "Enter verification link or code received via email: ") if "?" in code: # They copy-pasted the full URL. code_url = urlparse(code) code = parse_qs(code_url.query)["code"][0] s.verify_email_code(code) # Prepare authentication details for tokenserver. (_, kB) = s.fetch_keys() xcs = hashlib.sha256(kB).hexdigest()[:32] auth = s.get_identity_assertion(args.tokenserver_uri) # Auth to tokenserver, find sync storage node. token_uri = urljoin(args.tokenserver_uri, "1.0/sync/1.5") r = requests.get(token_uri, headers={ "Authorization": "BrowserID " + auth, "X-Client-State": xcs, }) r.raise_for_status() node = r.json() api_endpoint = node["api_endpoint"] hawk_id = str(node["id"]) hawk_key = str(node["key"]) print("Deleting from", api_endpoint) req = requests.Request("DELETE", api_endpoint).prepare() hawkauthlib.sign_request(req, hawk_id, hawk_key) r = requests.session().send(req) r.raise_for_status() print(r) finally: s.destroy_session()
def sendSigned(node, req): hawk_id = node["id"].encode("ascii") hawk_key = node["key"].encode("ascii") hawkauthlib.sign_request(req, hawk_id, hawk_key) r = requests.session().send(req) r.raise_for_status() return r.json()
def test_authentication_with_far_future_timestamp_fails(self): creds = self._get_credentials(username="******") req = Request.blank("/") ts = str(int(time.time() + 1000)) req.authorization = ("Hawk", {"ts": ts}) hawkauthlib.sign_request(req, **creds) self.app.request(req, status=401)
def test_authentication_with_expired_timestamp_fails(self): req = self._make_request("/auth") creds = self._get_credentials(req, username="******") ts = str(int(time.time() - 1000)) req.authorization = ("Hawk", {"ts": ts}) hawkauthlib.sign_request(req, **creds) self.app.request(req, status=401)
def test_authentication_without_nonce_fails(self): creds = self._get_credentials(username="******") req = Request.blank("/") hawkauthlib.sign_request(req, **creds) authz = req.environ["HTTP_AUTHORIZATION"] authz = authz.replace("nonce", "typonce") req.environ["HTTP_AUTHORIZATION"] = authz self.app.request(req, status=401)
def test_check_signature_fails_with_busted_signature(self): req = Request.blank("/") sign_request(req, "myid", "mykey") signature = parse_authz_header(req)["mac"] authz = req.environ["HTTP_AUTHORIZATION"] authz = authz.replace(signature, "XXX" + signature) req.environ["HTTP_AUTHORIZATION"] = authz self.assertFalse(check_signature(req, "mykey"))
def __call__(self, req): # Requets doesn't seem to include the port in the Host header, # and loads replaces hostnames with IPs. Undo all this rubbish # so that we can calculate the correct signature. req.headers["Host"] = urlparse(self.server_url).netloc params = {"ts": str(int(time.time()) + self.timeskew)} hawkauthlib.sign_request(req, self.id, self.secret, params=params) return req
def __call__(self, req): # Requets doesn't seem to include the port in the Host header, # and loads replaces hostnames with IPs. Undo all this rubbish # so that we can calculate the correct signature. req.headers['Host'] = urlparse(self.server_url).netloc params = {"ts": str(int(time.time()) + self.timeskew)} hawkauthlib.sign_request(req, self.id, self.secret, params=params) return req
def test_authentication_with_busted_signature_fails(self): creds = self._get_credentials(username="******") req = Request.blank("/") hawkauthlib.sign_request(req, **creds) signature = hawkauthlib.utils.parse_authz_header(req)["mac"] authz = req.environ["HTTP_AUTHORIZATION"] authz = authz.replace(signature, "XXX" + signature) req.environ["HTTP_AUTHORIZATION"] = authz self.app.request(req, status=401)
def test_authentication_with_custom_master_secret(self): self.plugin.master_secret = "elvislives" try: creds = self._get_credentials(username="******") req = Request.blank("/") hawkauthlib.sign_request(req, **creds) r = self.app.request(req) self.assertEquals(r.body, "*****@*****.**") finally: self.plugin.master_secret = None
def test_passing_requests_request_as_request_object(self): req = requests.Request( url="http://example.com:8000/resource/1", method="GET", params=[("b", "1"), ("a", "2")], ) req = req.prepare() assert not check_signature(req, TEST_KEY, nonces=False) sign_request(req, TEST_ID, TEST_KEY, params=TEST_PARAMS) assert TEST_SIG in req.headers['Authorization'] assert check_signature(req, TEST_KEY, nonces=False)
def main(argv): parser = argparse.ArgumentParser(description="Delete Firefox Sync data") parser.add_argument("email", help="Email of the account for which to delete data") parser.add_argument("--accounts-uri", default=DEFAULT_FXA_URI, help="URI of the Firefox Accounts API server") parser.add_argument("--tokenserver-uri", default=DEFAULT_TOKENSERVER_URI, help="URI of the Firefox Sync tokenserver") args = parser.parse_args(argv) # Sign in to the account. c = fxa.core.Client(args.accounts_uri) password = getpass.getpass("Password for {}: ".format(args.email)) s = c.login(args.email, password, keys=True) try: # Verify the session if necessary. # TODO: this won't work if the user has enabled two-step auth. status = s.get_email_status() if not status["sessionVerified"]: code = raw_input("Enter verification link or code: ") if "?" in code: # They copy-pasted the full URL. code_url = urlparse.urlparse(code) code = urlparse.parse_qs(code_url.query)["code"][0] s.verify_email_code(code) # Prepare authentication details for tokenserver. (_, kB) = s.fetch_keys() xcs = hashlib.sha256(kB).hexdigest()[:32] auth = s.get_identity_assertion(args.tokenserver_uri) # Auth to tokenserver, find sync storage node. token_uri = urlparse.urljoin(args.tokenserver_uri, "1.0/sync/1.5") r = requests.get(token_uri, headers={ "Authorization": "BrowserID " + auth, "X-Client-State": xcs, }) r.raise_for_status() node = r.json() api_endpoint = node["api_endpoint"] hawk_id = node["id"].encode("ascii") hawk_key = node["key"].encode("ascii") print "Deleting from", api_endpoint req = requests.Request("DELETE", api_endpoint).prepare() hawkauthlib.sign_request(req, hawk_id, hawk_key) r = requests.session().send(req) r.raise_for_status() print r finally: s.destroy_session()
def test_authentication_with_reused_nonce_fails(self): creds = self._get_credentials(username="******") # First request with that nonce should succeed. req = Request.blank("/") req.authorization = ("Hawk", {"nonce": "PEPPER"}) hawkauthlib.sign_request(req, **creds) r = self.app.request(req) self.assertEquals(r.body, "*****@*****.**") # Second request with that nonce should fail. req = Request.blank("/") req.authorization = ("Hawk", {"nonce": "PEPPER"}) hawkauthlib.sign_request(req, **creds) self.app.request(req, status=401)
def __call__(self, req): # Requests doesn't include the port in the Host header by default. # Ensure a fully-correct value so that signatures work properly. req.headers["Host"] = urlparse.urlparse(req.url).netloc params = {} if req.body: payload_str = 'hawk.1.payload\napplication/json\n' payload_str += req.body + '\n' params['hash'] = b64encode(hashlib.sha256(payload_str).digest()) if self.apiclient is not None: params["ts"] = str(int(self.apiclient.server_curtime())) hawkauthlib.sign_request(req, self.id, self.auth_key, params=params) return req
def __call__(self, req): # Requets doesn't seem to include the port in the Host header, # and loads replaces hostnames with IPs. Undo all this rubbish # so that we can calculate the correct signature. req.headers['Host'] = urlparse.urlparse(self.server_url).netloc id = self.tokenid.encode('hex') params = {} if req.body: payloadStr = 'hawk.1.payload\napplication/json\n' payloadStr += req.body + '\n' params['hash'] = base64.b64encode(SRP_HASH(payloadStr)) hawkauthlib.sign_request(req, id, self.authKey, params=params) return req
def test_check_signature_fails_with_reused_nonce(self): # First request with that nonce should succeed. req = Request.blank("/") req.authorization = ("Hawk", {"nonce": "PEPPER"}) sign_request(req, "myid", "mykey") self.assertTrue(check_signature(req, "mykey")) # Second request with that nonce should fail. req = Request.blank("/") req.authorization = ("Hawk", {"nonce": "PEPPER"}) sign_request(req, "myid", "mykey") self.assertFalse(check_signature(req, "mykey")) # But it will succeed if using a different nonce cache. self.assertTrue(check_signature(req, "mykey", nonces=NonceCache()))
def main(): token, key, expires, salt = create_token() path = "http://localhost:5000/storage/1.5/1/storage/col2" req = Request.blank(path) header = hawkauthlib.sign_request(req, token, key) print("Expires: ", expires) print("Salt: ", salt) print("\nPath: ", path) print("Hawk Authorization Header: ", header) path = ("http://localhost:5000/storage/1.5/1/storage/col2" "?batch=MTUzNjE5ODk3NjkyMQ==&commit=true") req = Request.blank(path, POST="") header = hawkauthlib.sign_request(req, token, key) print("\nPath: ", path) print("Hawk Authorization Header: ", header)
def test_checking_of_token_node_assignment(self): # Generate a token for one node req = self.make_request(environ={ "HTTP_HOST": "host1.com", }) tokenid, key = self.policy.encode_hawk_id(req, 42) # It can authenticate for requests to that node. hawkauthlib.sign_request(req, tokenid, key) self.assertEqual(req.authenticated_userid, 42) self.assertEqual(req.user.get("uid"), 42) # But not requests to some other node. req = self.make_request(environ={ "HTTP_HOST": "host2.com", }) hawkauthlib.sign_request(req, tokenid, key) with self.assertRaises(HTTPUnauthorized): req.authenticated_userid
def test_passing_filelike_as_request_object(self): req = BytesIO(TEST_REQ) assert not check_signature(req, TEST_KEY, nonces=False) req = BytesIO(TEST_REQ) authz = sign_request(req, TEST_ID, TEST_KEY, params=TEST_PARAMS) assert TEST_SIG in authz req = BytesIO(TEST_REQ_SIGNED) assert check_signature(req, TEST_KEY, nonces=False)
def test_that_hawkauth_is_used_by_default(self): # Generate signed request. req = self.make_request() tokenid, key = self.policy.encode_hawk_id(req, 42) hawkauthlib.sign_request(req, tokenid, key) # That should be enough to authenticate. self.assertEqual(req.authenticated_userid, 42) self.assertEqual(req.user.get("uid"), 42) # Check that it rejects invalid Hawk ids. req = self.make_request() hawkauthlib.sign_request(req, tokenid, key) authz = req.environ["HTTP_AUTHORIZATION"] req.environ["HTTP_AUTHORIZATION"] = authz.replace(tokenid, "XXXXXX") with self.assertRaises(HTTPUnauthorized): req.authenticated_userid # And that the rejection gets raised when accessing request.user self.assertRaises(HTTPUnauthorized, getattr, req, "user")
def test_checking_of_token_node_assignment(self): # Generate a token for one node req = self.make_request(environ={ "HTTP_HOST": "host1.com", }) tokenid, key = self.policy.encode_hawk_id(req, 42) # It can authenticate for requests to that node. hawkauthlib.sign_request(req, tokenid, key) self.assertEquals(req.authenticated_userid, 42) self.assertEquals(req.user.get("uid"), 42) # But not requests to some other node. req = self.make_request(environ={ "HTTP_HOST": "host2.com", }) hawkauthlib.sign_request(req, tokenid, key) with self.assertRaises(HTTPUnauthorized): req.authenticated_userid
def test_that_hawkauth_is_used_by_default(self): # Generate signed request. req = self.make_request() tokenid, key = self.policy.encode_hawk_id(req, 42) hawkauthlib.sign_request(req, tokenid, key) # That should be enough to authenticate. self.assertEquals(req.authenticated_userid, 42) self.assertEquals(req.user.get("uid"), 42) # Check that it rejects invalid Hawk ids. req = self.make_request() hawkauthlib.sign_request(req, tokenid, key) authz = req.environ["HTTP_AUTHORIZATION"] req.environ["HTTP_AUTHORIZATION"] = authz.replace(tokenid, "XXXXXX") with self.assertRaises(HTTPUnauthorized): req.authenticated_userid # And that the rejection gets raised when accessing request.user self.assertRaises(HTTPUnauthorized, getattr, req, "user")
def __call__(self, req): # Requests doesn't include the port in the Host header by default. # Ensure a fully-correct value so that signatures work properly. req.headers["Host"] = urlparse(req.url).netloc params = {} if req.body: hasher = hashlib.sha256() hasher.update(b"hawk.1.payload\napplication/json\n") hasher.update(req.body.encode("utf8")) hasher.update(b"\n") hash = b64encode(hasher.digest()) if PY3: hash = hash.decode("ascii") params["hash"] = hash if self.apiclient is not None: params["ts"] = str(int(self.apiclient.server_curtime())) hawkauthlib.sign_request(req, self.id, self.auth_key, params=params) return req
def test_access_to_public_urls(self): # Request with no credentials is allowed access. req = Request.blank("/public") resp = self.app.request(req) self.assertEquals(resp.body, "public") # Request with valid credentials is allowed access. creds = self._get_credentials(username="******") req = Request.blank("/public") hawkauthlib.sign_request(req, **creds) resp = self.app.request(req) self.assertEquals(resp.body, "public") # Request with invalid credentials gets a 401. req = Request.blank("/public") hawkauthlib.sign_request(req, **creds) signature = hawkauthlib.utils.parse_authz_header(req)["mac"] authz = req.environ["HTTP_AUTHORIZATION"] authz = authz.replace(signature, "XXX" + signature) req.environ["HTTP_AUTHORIZATION"] = authz resp = self.app.request(req, status=401)
def main(): args = get_args() token, key, expires, salt = create_token(args) path = "{node}{uri}".format(node=args.node, uri=args.uri) req = Request.blank(path) header = hawkauthlib.sign_request(req, token, key) if not args.as_header: print("Expires: ", expires) print("Salt: ", salt) print("\nPath: ", path) print("Hawk Authorization Header: ", header) else: print(header)
def test_passing_environ_dict_as_request_object(self): req = { "wsgi.url_scheme": "http", "REQUEST_METHOD": "GET", "HTTP_HOST": "example.com:8000", "HTTP_CONTENT_LENGTH": "11", "PATH_INFO": "/resource/1", "QUERY_STRING": "b=1&a=2", } assert not check_signature(req, TEST_KEY, nonces=False) authz = sign_request(req, TEST_ID, TEST_KEY, params=TEST_PARAMS) assert TEST_SIG in authz assert check_signature(req, TEST_KEY, nonces=False)
def new_do_request(req, *args, **kwds): hawkauthlib.sign_request(req, auth_token, auth_secret) return orig_do_request(req, *args, **kwds)
def test_authenticated_request_works(self): creds = self._get_credentials(username="******") req = Request.blank("/") hawkauthlib.sign_request(req, **creds) r = self.app.request(req) self.assertEquals(r.body, "*****@*****.**")
def test_check_signature_fails_with_far_future_timestamp(self): req = Request.blank("/") ts = str(int(time.time() + 1000)) req.authorization = ("Hawk", {"ts": ts}) sign_request(req, "myid", "mykey") self.assertFalse(check_signature(req, "mykey"))
def test_check_signature_fails_with_non_mac_scheme(self): req = b"GET / HTTP/1.1\r\nHost: example.com\r\n\r\n" req = Request.from_bytes(req) sign_request(req, "myid", "mykey") req.authorization = ("OAuth", req.authorization[1]) self.assertFalse(check_signature(req, "mykey"))
def test_sign_request_throws_away_other_auth_params(self): req = Request.blank("/") req.authorization = ("Digest", {"response": "helloworld"}) sign_request(req, "id", "key") self.assertEquals(req.authorization[0], "Hawk")
def new_do_request(req, *args, **kwds): hawkauthlib.sign_request(req, self.auth_token, self.auth_secret) return orig_do_request(req, *args, **kwds)
def test_passing_webob_request_as_request_object(self): req = webob.Request.from_bytes(TEST_REQ) assert not check_signature(req, TEST_KEY, nonces=False) authz = sign_request(req, TEST_ID, TEST_KEY, params=TEST_PARAMS) assert TEST_SIG in authz assert check_signature(req, TEST_KEY, nonces=False)
def test_passing_bytestring_as_request_object(self): assert not check_signature(TEST_REQ, TEST_KEY, nonces=False) authz = sign_request(TEST_REQ, TEST_ID, TEST_KEY, params=TEST_PARAMS) assert TEST_SIG in authz assert check_signature(TEST_REQ_SIGNED, TEST_KEY, nonces=False)
def __call__(self, req): hawkauthlib.sign_request(req, self.token, self.secret) return req
def test_authentication_fails_when_hawkid_has_no_userid(self): creds = self._get_credentials(hello="world") req = Request.blank("/") hawkauthlib.sign_request(req, **creds) self.app.request(req, status=401)
def __call__(self, req): sign_request(req, TEST_ID, TEST_KEY, params=TEST_PARAMS) assert check_signature(req, TEST_KEY, nonces=False) assert TEST_SIG in req.headers['Authorization'] raise RuntimeError("aborting the request")