def test_revoke_token(): sdb = SessionDB() sid = sdb.create_authz_session("sub", AREQ) grant = sdb[sid]["code"] _dict = sdb.upgrade_to_token(grant) token = _dict["access_token"] rtoken = _dict["refresh_token"] assert sdb.is_valid(token) sdb.revoke_token(token) assert sdb.is_valid(token) is False dict2 = sdb.refresh_token(rtoken) token = dict2["access_token"] assert sdb.is_valid(token) sdb.revoke_token(rtoken) assert sdb.is_valid(rtoken) is False raises(ExpiredToken, 'sdb.refresh_token(rtoken)') assert sdb.is_valid(token) # --- new token ---- sdb = SessionDB() sid = sdb.create_authz_session("sub", AREQ) grant = sdb[sid]["code"] sdb.revoke_token(grant) assert sdb.is_valid(grant) is False
def test_upgrade_to_token(): sdb = SessionDB() sid = sdb.create_authz_session("sub", AREQ) grant = sdb[sid]["code"] _dict = sdb.upgrade_to_token(grant) print _dict.keys() assert _eq(_dict.keys(), ['code', 'authzreq', 'token_type', 'local_sub', 'client_id', 'oauth_state', 'refresh_token', 'revoked', 'sub', 'access_token', 'token_expires_at', 'expires_in', 'state', 'redirect_uri', 'code_used', 'scope', 'access_token_scope']) raises(Exception, 'sdb.upgrade_to_token(grant)') raises(Exception, 'sdb.upgrade_to_token(_dict["access_token"]') sdb = SessionDB() sid = sdb.create_authz_session("another_user_id", AREQ) grant = sdb[sid]["code"] _dict = sdb.upgrade_to_token(grant, id_token="id_token", oidreq=OIDR) print _dict.keys() assert _eq(_dict.keys(), ['code', 'authzreq', 'id_token', 'token_type', 'local_sub', 'client_id', 'oauth_state', 'refresh_token', 'revoked', 'sub', 'oidreq', 'access_token', 'token_expires_at', 'expires_in', 'state', 'redirect_uri', 'code_used', 'scope', 'access_token_scope']) assert _dict["id_token"] == "id_token" assert _dict["oidreq"].type() == "OpenIDRequest" _ = _dict["access_token"] raises(Exception, 'sdb.upgrade_to_token(token)')
def test_update_to_token(): sdb = SessionDB() sid = sdb.create_authz_session("user_id", AREQ) grant = sdb[sid]["code"] _dict = sdb.update_to_token(grant) print _dict.keys() assert _eq(_dict.keys(), ['code', 'authzreq', 'token_type', 'local_sub', 'client_id', 'oauth_state', 'refresh_token', 'revoked', 'sub', 'access_token', 'token_expires_at', 'expires_in', 'state', 'redirect_uri', 'code_used', 'scope', 'access_token_scope']) raises(Exception, 'sdb.update_to_token(grant)') raises(Exception, 'sdb.update_to_token(_dict["access_token"]') sdb = SessionDB() sid = sdb.create_authz_session("another_user_id", AREQ) grant = sdb[sid]["code"] _dict = sdb.update_to_token(grant, id_token="id_token", oidreq=OIDR) print _dict.keys() assert _eq(_dict.keys(), ['code', 'authzreq', 'id_token', 'token_type', 'local_sub', 'client_id', 'oauth_state', 'refresh_token', 'revoked', 'sub', 'oidreq', 'access_token', 'token_expires_at', 'expires_in', 'state', 'redirect_uri', 'code_used', 'scope', 'access_token_scope']) assert _dict["id_token"] == "id_token" assert _dict["oidreq"].type() == "OpenIDRequest" _ = _dict["access_token"] raises(Exception, 'sdb.update_to_token(token)')
def test_create_authz_session(): sdb = SessionDB() sid = sdb.create_authz_session("user_id", AREQ) info = sdb[sid] print info assert info["oauth_state"] == "authz" sdb = SessionDB() # Missing nonce property sid = sdb.create_authz_session("user_id", OAUTH2_AREQ) info = sdb[sid] print info assert info["oauth_state"] == "authz" sid2 = sdb.create_authz_session("user_id", AREQN) info = sdb[sid2] print info assert info["nonce"] == "something" sid3 = sdb.create_authz_session("user_id", AREQN, id_token="id_token") info = sdb[sid3] print info assert info["id_token"] == "id_token" sid4 = sdb.create_authz_session("user_id", AREQN, oidreq=OIDR) info = sdb[sid4] print info assert "id_token" not in info assert "oidreq" in info
def test_revoke_token(): sdb = SessionDB() sid = sdb.create_authz_session("user_id", AREQ) grant = sdb[sid]["code"] _dict = sdb.update_to_token(grant) token = _dict["access_token"] rtoken = _dict["refresh_token"] assert sdb.is_valid(token) sdb.revoke_token(token) assert sdb.is_valid(token) is False dict2 = sdb.refresh_token(rtoken) token = dict2["access_token"] assert sdb.is_valid(token) sdb.revoke_token(rtoken) assert sdb.is_valid(rtoken) is False raises(ExpiredToken, 'sdb.refresh_token(rtoken)') assert sdb.is_valid(token) # --- new token ---- sdb = SessionDB() sid = sdb.create_authz_session("user_id", AREQ) grant = sdb[sid]["code"] sdb.revoke_token(grant) assert sdb.is_valid(grant) is False
def test_create_authz_session(): sdb = SessionDB() sid = sdb.create_authz_session("sub", AREQ) info = sdb[sid] print info assert info["oauth_state"] == "authz" sdb = SessionDB() # Missing nonce property sid = sdb.create_authz_session("sub", OAUTH2_AREQ) info = sdb[sid] print info assert info["oauth_state"] == "authz" sid2 = sdb.create_authz_session("sub", AREQN) info = sdb[sid2] print info assert info["nonce"] == "something" sid3 = sdb.create_authz_session("sub", AREQN, id_token="id_token") info = sdb[sid3] print info assert info["id_token"] == "id_token" sid4 = sdb.create_authz_session("sub", AREQN, oidreq=OIDR) info = sdb[sid4] print info assert "id_token" not in info assert "oidreq" in info
def test_create_authz_session_with_sector_id(): sdb = SessionDB(seed="foo") sid5 = sdb.create_authz_session("user_id", AREQN, oidreq=OIDR, sector_id="http://example.com/") info_1 = sdb[sid5] print info_1 assert "id_token" not in info_1 assert "oidreq" in info_1 assert info_1["user_id"] != "user_id" sid6 = sdb.create_authz_session("user_id", AREQN, oidreq=OIDR, sector_id="http://example.org/") info_2 = sdb[sid6] print info_2 assert info_2["user_id"] != "user_id" assert info_2["user_id"] != info_1["user_id"]
def test_is_valid(): sdb = SessionDB(BASE_URL) ae1 = AuthnEvent("sub") sid = sdb.create_authz_session(ae1, AREQ) grant = sdb[sid]["code"] assert sdb.is_valid(grant) _dict = sdb.upgrade_to_token(grant) assert sdb.is_valid(grant) is False token1 = _dict["access_token"] assert sdb.is_valid(token1) rtoken = _dict["refresh_token"] assert sdb.is_valid(rtoken) dict2 = sdb.refresh_token(rtoken) token2 = dict2["access_token"] assert sdb.is_valid(token2) # replace refresh_token dict2["refresh_token"] = token2 assert sdb.is_valid(rtoken) is False # mess with the time-line dict2["token_expires_at"] = utc_time_sans_frac() - 86400 assert sdb.is_valid(token2) is False # replace access_token dict2["access_token"] = token1 assert sdb.is_valid(token2) is False ae = AuthnEvent("another:user") sid = sdb.create_authz_session(ae, AREQ) grant = sdb[sid]["code"] gdict = sdb[grant] gdict["token_expires_at"] = utc_time_sans_frac() - 86400 assert sdb.is_valid(grant) is False
def test_is_valid(): sdb = SessionDB(BASE_URL) ae1 = AuthnEvent("sub") sid = sdb.create_authz_session(ae1, AREQ) grant = sdb[sid]["code"] assert sdb.is_valid(grant) _dict = sdb.upgrade_to_token(grant) assert sdb.is_valid(grant) is False token1 = _dict["access_token"] assert sdb.is_valid(token1) rtoken = _dict["refresh_token"] assert sdb.is_valid(rtoken) dict2 = sdb.refresh_token(rtoken) token2 = dict2["access_token"] assert sdb.is_valid(token2) # replace refresh_token dict2["refresh_token"] = token2 assert sdb.is_valid(rtoken) is False # mess with the time-line dict2["token_expires_at"] = utc_time_sans_frac() - 86400 assert sdb.is_valid(token2) is False # replace access_token dict2["access_token"] = token1 assert sdb.is_valid(token2) is False ae = AuthnEvent("another:user") sid = sdb.create_authz_session(ae, AREQ) grant = sdb[sid]["code"] gdict = sdb[grant] gdict["token_expires_at"] = utc_time_sans_frac() - 86400 assert sdb.is_valid(grant) is False
def test_sub_to_authn_event(): sdb = SessionDB(BASE_URL) ae2 = AuthnEvent("sub") sid = sdb.create_authz_session(ae2, AREQ) sub = sdb.do_sub(sid) # given the sub find out weather the authn event is still valid sids = sdb.sub2sid[sub] ae = sdb[sids[0]]["authn_event"] assert ae.valid()
def test_sub_to_authn_event(): sdb = SessionDB(BASE_URL) ae2 = AuthnEvent("sub", time_stamp=time.time()) sid = sdb.create_authz_session(ae2, AREQ) sub = sdb.do_sub(sid) # given the sub find out weather the authn event is still valid sids = sdb.get_sids_by_sub(sub) ae = sdb[sids[0]]["authn_event"] assert ae.valid()
def test_sub_to_authn_event(): sdb = SessionDB(BASE_URL) ae2 = AuthnEvent("sub", time_stamp=time.time()) sid = sdb.create_authz_session(ae2, AREQ) sub = sdb.do_sub(sid) # given the sub find out weather the authn event is still valid sids = sdb.get_sids_by_sub(sub) ae = sdb[sids[0]]["authn_event"] assert ae.valid()
def test_is_valid(): sdb = SessionDB() sid = sdb.create_authz_session("user_id", AREQ) grant = sdb[sid]["code"] assert sdb.is_valid(grant) _dict = sdb.update_to_token(grant) assert sdb.is_valid(grant) is False token1 = _dict["access_token"] assert sdb.is_valid(token1) rtoken = _dict["refresh_token"] assert sdb.is_valid(rtoken) dict2 = sdb.refresh_token(rtoken) token2 = dict2["access_token"] assert sdb.is_valid(token2) # replace refresh_token dict2["refresh_token"] = token2 assert sdb.is_valid(rtoken) is False # mess with the time-line dict2["expires_at"] = utc_time_sans_frac() - 86400 # like yesterday assert sdb.is_valid(token2) is False # replace access_token dict2["access_token"] = token1 assert sdb.is_valid(token2) is False sid = sdb.create_authz_session("another:user", AREQ) grant = sdb[sid]["code"] gdict = sdb[grant] gdict["expires_at"] = utc_time_sans_frac() - 86400 # like yesterday assert sdb.is_valid(grant) is False
def test_create_authz_session(): sdb = SessionDB(BASE_URL) ae = AuthnEvent("uid") sid = sdb.create_authz_session(ae, AREQ) sdb.do_sub(sid) info = sdb[sid] print info assert info["oauth_state"] == "authz" sdb = SessionDB(BASE_URL) ae = AuthnEvent("sub") # Missing nonce property sid = sdb.create_authz_session(ae, OAUTH2_AREQ) info = sdb[sid] print info assert info["oauth_state"] == "authz" ae = AuthnEvent("sub") sid2 = sdb.create_authz_session(ae, AREQN) info = sdb[sid2] print info assert info["nonce"] == "something" sid3 = sdb.create_authz_session(ae, AREQN, id_token="id_token") info = sdb[sid3] print info assert info["id_token"] == "id_token" sid4 = sdb.create_authz_session(ae, AREQN, oidreq=OIDR) info = sdb[sid4] print info assert "id_token" not in info assert "oidreq" in info
def test_create_authz_session(): sdb = SessionDB(BASE_URL) ae = AuthnEvent("uid") sid = sdb.create_authz_session(ae, AREQ) sdb.do_sub(sid) info = sdb[sid] print info assert info["oauth_state"] == "authz" sdb = SessionDB(BASE_URL) ae = AuthnEvent("sub") # Missing nonce property sid = sdb.create_authz_session(ae, OAUTH2_AREQ) info = sdb[sid] print info assert info["oauth_state"] == "authz" ae = AuthnEvent("sub") sid2 = sdb.create_authz_session(ae, AREQN) info = sdb[sid2] print info assert info["nonce"] == "something" sid3 = sdb.create_authz_session(ae, AREQN, id_token="id_token") info = sdb[sid3] print info assert info["id_token"] == "id_token" sid4 = sdb.create_authz_session(ae, AREQN, oidreq=OIDR) info = sdb[sid4] print info assert "id_token" not in info assert "oidreq" in info
def test_revoke_token(): sdb = SessionDB(BASE_URL) ae1 = AuthnEvent("sub") sid = sdb.create_authz_session(ae1, AREQ) grant = sdb[sid]["code"] _dict = sdb.upgrade_to_token(grant) token = _dict["access_token"] rtoken = _dict["refresh_token"] assert sdb.is_valid(token) sdb.revoke_token(token) assert sdb.is_valid(token) is False dict2 = sdb.refresh_token(rtoken) token = dict2["access_token"] assert sdb.is_valid(token) sdb.revoke_token(rtoken) assert sdb.is_valid(rtoken) is False raises(ExpiredToken, 'sdb.refresh_token(rtoken)') assert sdb.is_valid(token) # --- new token ---- sdb = SessionDB(BASE_URL) ae2 = AuthnEvent("sub") sid = sdb.create_authz_session(ae2, AREQ) grant = sdb[sid]["code"] sdb.revoke_token(grant) assert sdb.is_valid(grant) is False
def test_refresh_token(): sdb = SessionDB() sid = sdb.create_authz_session("user_id", AREQ) grant = sdb[sid]["code"] _dict = sdb.update_to_token(grant) dict1 = _dict.copy() rtoken = _dict["refresh_token"] time.sleep(1) dict2 = sdb.refresh_token(rtoken) print dict2 assert dict1["issued"] != dict2["issued"] assert dict1["access_token"] != dict2["access_token"] raises(Exception, 'sdb.refresh_token(dict2["access_token"])')
def test_refresh_token(): sdb = SessionDB() sid = sdb.create_authz_session("sub", AREQ) grant = sdb[sid]["code"] _dict = sdb.upgrade_to_token(grant) dict1 = _dict.copy() rtoken = _dict["refresh_token"] time.sleep(1) dict2 = sdb.refresh_token(rtoken) print dict2 assert dict1["token_expires_at"] != dict2["token_expires_at"] assert dict1["access_token"] != dict2["access_token"] raises(Exception, 'sdb.refresh_token(dict2["access_token"])')
def test_refresh_token(): sdb = SessionDB(BASE_URL) ae = AuthnEvent("sub") sid = sdb.create_authz_session(ae, AREQ) grant = sdb[sid]["code"] _dict = sdb.upgrade_to_token(grant) dict1 = _dict.copy() rtoken = _dict["refresh_token"] time.sleep(1) dict2 = sdb.refresh_token(rtoken) print dict2 assert dict1["token_expires_at"] != dict2["token_expires_at"] assert dict1["access_token"] != dict2["access_token"] raises(Exception, 'sdb.refresh_token(dict2["access_token"])')
def test_server_authorization_endpoint_id_token(): provider = provider_init bib = { "scope": ["openid"], "state": "id-6da9ca0cc23959f5f33e8becd9b08cae", "redirect_uri": "http://localhost:8087/authz", "response_type": ["code", "id_token"], "client_id": "a1b2c3", "nonce": "Nonce", "prompt": ["none"] } req = AuthorizationRequest(**bib) AREQ = AuthorizationRequest(response_type="code", client_id="client_1", redirect_uri="http://example.com/authz", scope=["openid"], state="state000") sdb = SessionDB() sid = sdb.create_authz_session("userX", AREQ) _info = sdb[sid] _user_info = IdToken(iss="https://foo.example.om", sub="foo", aud=bib["client_id"], exp=epoch_in_a_while(minutes=10), acr="2", nonce=bib["nonce"]) print provider.keyjar.issuer_keys print _user_info.to_dict() idt = provider.id_token_as_signed_jwt(_info, access_token="access_token", user_info=_user_info) req["id_token"] = idt QUERY_STRING = req.to_urlencoded() resp = provider.authorization_endpoint(request=QUERY_STRING) print resp assert "error=login_required" in resp.message
def test_create_authz_session_with_sector_id(): sdb = SessionDB(BASE_URL, seed="foo") ae = AuthnEvent("sub") sid5 = sdb.create_authz_session(ae, AREQN, oidreq=OIDR) sdb.do_sub(sid5, "http://example.com/si.jwt", "pairwise") info_1 = sdb[sid5] print info_1 assert "id_token" not in info_1 assert "oidreq" in info_1 assert info_1["sub"] != "sub" user_id1 = info_1["sub"] sdb.do_sub(sid5, "http://example.net/si.jwt", "pairwise") info_2 = sdb[sid5] print info_2 assert info_2["sub"] != "sub" assert info_2["sub"] != user_id1
def test_create_authz_session_with_sector_id(): sdb = SessionDB(seed="foo") uid = "sub" sid5 = sdb.create_authz_session(uid, AREQN, oidreq=OIDR) sdb.do_userid(sid5, uid, "http://example.com/si.jwt", "pairwise") info_1 = sdb[sid5] print info_1 assert "id_token" not in info_1 assert "oidreq" in info_1 assert info_1["sub"] != "sub" user_id1 = info_1["sub"] sdb.do_userid(sid5, uid, "http://example.net/si.jwt", "pairwise") info_2 = sdb[sid5] print info_2 assert info_2["sub"] != "sub" assert info_2["sub"] != user_id1
def test_create_authz_session_with_sector_id(): sdb = SessionDB(BASE_URL, seed="foo") ae = AuthnEvent("sub") sid5 = sdb.create_authz_session(ae, AREQN, oidreq=OIDR) sdb.do_sub(sid5, "http://example.com/si.jwt", "pairwise") info_1 = sdb[sid5] print info_1 assert "id_token" not in info_1 assert "oidreq" in info_1 assert info_1["sub"] != "sub" user_id1 = info_1["sub"] sdb.do_sub(sid5, "http://example.net/si.jwt", "pairwise") info_2 = sdb[sid5] print info_2 assert info_2["sub"] != "sub" assert info_2["sub"] != user_id1
def test_create_authz_session_with_sector_id(): sdb = SessionDB(seed="foo") uid = "user_id" sid5 = sdb.create_authz_session(uid, AREQN, oidreq=OIDR) sdb.do_userid(sid5, uid, "http://example.com/si.jwt", "pairwise") info_1 = sdb[sid5] print info_1 assert "id_token" not in info_1 assert "oidreq" in info_1 assert info_1["sub"] != "user_id" user_id1 = info_1["sub"] sdb.do_userid(sid5, uid, "http://example.net/si.jwt", "pairwise") info_2 = sdb[sid5] print info_2 assert info_2["sub"] != "user_id" assert info_2["sub"] != user_id1
def test_server_authorization_endpoint_id_token(): provider = provider_init bib = {"scope": ["openid"], "state": "id-6da9ca0cc23959f5f33e8becd9b08cae", "redirect_uri": "http://localhost:8087/authz", "response_type": ["code", "id_token"], "client_id": "a1b2c3", "nonce": "Nonce", "prompt": ["none"]} req = AuthorizationRequest(**bib) AREQ = AuthorizationRequest(response_type="code", client_id="client_1", redirect_uri="http://example.com/authz", scope=["openid"], state="state000") sdb = SessionDB() sid = sdb.create_authz_session("username", AREQ) _info = sdb[sid] _user_info = IdToken(iss="https://foo.example.om", sub="foo", aud=bib["client_id"], exp=epoch_in_a_while(minutes=10), acr="2", nonce=bib["nonce"]) print provider.keyjar.issuer_keys print _user_info.to_dict() idt = provider.id_token_as_signed_jwt(_info, access_token="access_token", user_info=_user_info) req["id_token"] = idt environ = BASE_ENVIRON.copy() environ["QUERY_STRING"] = req.to_urlencoded() resp = provider.authorization_endpoint(environ, start_response) print resp line = resp[0] assert "error=login_required" in line
class TestSessionDB(object): @pytest.fixture(autouse=True) def create_sdb(self): kb = KeyBundle(JWKS["keys"]) kj = KeyJar() kj.issuer_keys[''] = [kb] self.sdb = SessionDB( "https://example.com/", db={}, code_factory=DefaultToken('supersecret', 'verybadpassword', typ='A', lifetime=600), token_factory=JWTToken('T', keyjar=kj, lt_pattern={ 'code': 3600, 'token': 900 }, iss='https://example.com/as', sign_alg='RS256'), refresh_token_factory=JWTToken('R', keyjar=kj, lt_pattern={'': 24 * 3600}, iss='https://example.com/as')) def test_create_authz_session(self): ae = AuthnEvent("uid", "salt") sid = self.sdb.create_authz_session(ae, AREQ) self.sdb.do_sub(sid, "client_salt") info = self.sdb[sid] assert info["oauth_state"] == "authz" def test_create_authz_session_without_nonce(self): ae = AuthnEvent("sub", "salt") sid = self.sdb.create_authz_session(ae, AREQ) info = self.sdb[sid] assert info["oauth_state"] == "authz" def test_create_authz_session_with_nonce(self): ae = AuthnEvent("sub", "salt") sid = self.sdb.create_authz_session(ae, AREQN) info = self.sdb[sid] assert info["nonce"] == "something" def test_create_authz_session_with_id_token(self): ae = AuthnEvent("sub", "salt") sid = self.sdb.create_authz_session(ae, AREQN, id_token="id_token") info = self.sdb[sid] assert info["id_token"] == "id_token" def test_create_authz_session_with_oidreq(self): ae = AuthnEvent("sub", "salt") sid = self.sdb.create_authz_session(ae, AREQN, oidreq=OIDR) info = self.sdb[sid] assert "id_token" not in info assert "oidreq" in info def test_create_authz_session_with_sector_id(self): ae = AuthnEvent("sub", "salt") sid = self.sdb.create_authz_session(ae, AREQN, oidreq=OIDR) self.sdb.do_sub(sid, "client_salt", "http://example.com/si.jwt", "pairwise") info_1 = self.sdb[sid].copy() assert "id_token" not in info_1 assert "oidreq" in info_1 assert info_1["sub"] != "sub" self.sdb.do_sub(sid, "client_salt", "http://example.net/si.jwt", "pairwise") info_2 = self.sdb[sid] assert info_2["sub"] != "sub" assert info_2["sub"] != info_1["sub"] def test_upgrade_to_token(self): ae1 = AuthnEvent("uid", "salt") sid = self.sdb.create_authz_session(ae1, AREQ) self.sdb[sid]['sub'] = 'sub' grant = self.sdb[sid]["code"] _dict = self.sdb.upgrade_to_token(grant) assert _eq(list(_dict.keys()), [ 'authn_event', 'code', 'authzreq', 'revoked', 'access_token', 'token_type', 'state', 'redirect_uri', 'code_used', 'client_id', 'scope', 'oauth_state', 'access_token_scope', 'sub', 'response_type' ]) # can't update again with pytest.raises(AccessCodeUsed): self.sdb.upgrade_to_token(grant) self.sdb.upgrade_to_token(_dict["access_token"]) def test_upgrade_to_token_refresh(self): ae1 = AuthnEvent("sub", "salt") sid = self.sdb.create_authz_session(ae1, AREQO) self.sdb.do_sub(sid, ae1.salt) grant = self.sdb[sid]["code"] _dict = self.sdb.upgrade_to_token(grant, issue_refresh=True) assert _eq(_dict.keys(), [ 'authn_event', 'code', 'authzreq', 'revoked', 'access_token', 'response_type', 'token_type', 'state', 'redirect_uri', 'code_used', 'client_id', 'scope', 'oauth_state', 'access_token_scope', 'refresh_token', 'sub' ]) # can't update again with pytest.raises(AccessCodeUsed): self.sdb.upgrade_to_token(grant) self.sdb.upgrade_to_token(_dict["access_token"]) def test_upgrade_to_token_with_id_token_and_oidreq(self): ae2 = AuthnEvent("another_user_id", "salt") sid = self.sdb.create_authz_session(ae2, AREQ) self.sdb[sid]['sub'] = 'sub' grant = self.sdb[sid]["code"] _dict = self.sdb.upgrade_to_token(grant, id_token="id_token", oidreq=OIDR) assert _eq(list(_dict.keys()), [ 'authn_event', 'code', 'authzreq', 'revoked', 'oidreq', 'access_token', 'id_token', 'response_type', 'token_type', 'state', 'redirect_uri', 'code_used', 'client_id', 'scope', 'oauth_state', 'access_token_scope', 'sub' ]) assert _dict["id_token"] == "id_token" assert isinstance(_dict["oidreq"], OpenIDRequest) def test_refresh_token(self): ae = AuthnEvent("uid", "salt") sid = self.sdb.create_authz_session(ae, AREQ) self.sdb[sid]['sub'] = 'sub' grant = self.sdb[sid]["code"] dict1 = self.sdb.upgrade_to_token(grant, issue_refresh=True).copy() rtoken = dict1["refresh_token"] dict2 = self.sdb.refresh_token(rtoken, AREQ['client_id']) assert dict1["access_token"] != dict2["access_token"] with pytest.raises(ExpiredToken): self.sdb.refresh_token(dict2["access_token"], AREQ['client_id']) def test_refresh_token_cleared_session(self): ae = AuthnEvent('uid', 'salt') sid = self.sdb.create_authz_session(ae, AREQ) self.sdb[sid]['sub'] = 'sub' grant = self.sdb[sid]['code'] dict1 = self.sdb.upgrade_to_token(grant, issue_refresh=True) ac1 = dict1['access_token'] rtoken = dict1['refresh_token'] dict2 = self.sdb.refresh_token(rtoken, AREQ['client_id']) assert ac1 != dict2["access_token"] def test_is_valid(self): ae1 = AuthnEvent("uid", "salt") sid = self.sdb.create_authz_session(ae1, AREQ) self.sdb[sid]['sub'] = 'sub' grant = self.sdb[sid]["code"] assert self.sdb.is_valid(grant) sinfo = self.sdb.upgrade_to_token(grant, issue_refresh=True) assert not self.sdb.is_valid(grant) access_token = sinfo["access_token"] assert self.sdb.access_token.valid(access_token) refresh_token = sinfo["refresh_token"] sinfo = self.sdb.refresh_token(refresh_token, AREQ['client_id']) access_token2 = sinfo["access_token"] assert self.sdb.is_valid(access_token2) # The old access code should be invalid try: self.sdb.is_valid(access_token) except KeyError: pass def test_valid_grant(self): ae = AuthnEvent("another:user", "salt") sid = self.sdb.create_authz_session(ae, AREQ) grant = self.sdb[sid]["code"] assert self.sdb.is_valid(grant) def test_revoke_token(self): ae1 = AuthnEvent("uid", "salt") sid = self.sdb.create_authz_session(ae1, AREQ) self.sdb[sid]['sub'] = 'sub' grant = self.sdb[sid]["code"] tokens = self.sdb.upgrade_to_token(grant, issue_refresh=True) access_token = tokens["access_token"] refresh_token = tokens["refresh_token"] assert self.sdb.is_valid(access_token) self.sdb.revoke_token(access_token) assert not self.sdb.is_valid(access_token) sinfo = self.sdb.refresh_token(refresh_token, AREQ['client_id']) access_token = sinfo["access_token"] assert self.sdb.is_valid(access_token) self.sdb.revoke_refresh_token(refresh_token) assert not self.sdb.is_valid(refresh_token) try: self.sdb.refresh_token(refresh_token, AREQ['client_id']) except ExpiredToken: pass assert self.sdb.is_valid(access_token) ae2 = AuthnEvent("sub", "salt") sid = self.sdb.create_authz_session(ae2, AREQ) grant = self.sdb[sid]["code"] self.sdb.revoke_token(grant) assert not self.sdb.is_valid(grant) def test_sub_to_authn_event(self): ae = AuthnEvent("sub", "salt", time_stamp=time.time()) sid = self.sdb.create_authz_session(ae, AREQ) sub = self.sdb.do_sub(sid, "client_salt") # given the sub find out whether the authn event is still valid sids = self.sdb.get_sids_by_sub(sub) ae = self.sdb[sids[0]]["authn_event"] assert AuthnEvent.from_json(ae).valid() def test_do_sub_deterministic(self): ae = AuthnEvent("tester", "random_value") sid = self.sdb.create_authz_session(ae, AREQ) self.sdb.do_sub(sid, "other_random_value") info = self.sdb[sid] assert info["sub"] == \ '179670cdee6375c48e577317b2abd7d5cd26a5cdb1cfb7ef84af3d703c71d013' self.sdb.do_sub(sid, "other_random_value", sector_id='http://example.com', subject_type="pairwise") info2 = self.sdb[sid] assert info2["sub"] == \ 'aaa50d80f8780cf1c4beb39e8e126556292f5091b9e39596424fefa2b99d9c53' self.sdb.do_sub(sid, "another_random_value", sector_id='http://other.example.com', subject_type="pairwise") info2 = self.sdb[sid] assert info2["sub"] == \ '62fb630e29f0d41b88e049ac0ef49a9c3ac5418c029d6e4f5417df7e9443976b'
class TestSessionDB(object): @pytest.fixture(autouse=True) def create_sdb(self): kb = KeyBundle(JWKS["keys"]) kj = KeyJar() kj.issuer_keys[''] = [kb] self.sdb = SessionDB( "https://example.com/", token_factory=JWTToken('T', keyjar=kj, lt_pattern={'code': 3600, 'token': 900}, iss='https://example.com/as', sign_alg='RS256'), refresh_token_factory=JWTToken( 'R', keyjar=kj, lt_pattern={'': 24 * 3600}, iss='https://example.com/as') ) def test_create_authz_session(self): ae = AuthnEvent("uid", "salt") sid = self.sdb.create_authz_session(ae, AREQ) self.sdb.do_sub(sid, "client_salt") info = self.sdb[sid] assert info["oauth_state"] == "authz" def test_create_authz_session_without_nonce(self): ae = AuthnEvent("sub", "salt") sid = self.sdb.create_authz_session(ae, AREQ) info = self.sdb[sid] assert info["oauth_state"] == "authz" def test_create_authz_session_with_nonce(self): ae = AuthnEvent("sub", "salt") sid = self.sdb.create_authz_session(ae, AREQN) info = self.sdb[sid] assert info["nonce"] == "something" def test_create_authz_session_with_id_token(self): ae = AuthnEvent("sub", "salt") sid = self.sdb.create_authz_session(ae, AREQN, id_token="id_token") info = self.sdb[sid] assert info["id_token"] == "id_token" def test_create_authz_session_with_oidreq(self): ae = AuthnEvent("sub", "salt") sid = self.sdb.create_authz_session(ae, AREQN, oidreq=OIDR) info = self.sdb[sid] assert "id_token" not in info assert "oidreq" in info def test_create_authz_session_with_sector_id(self): ae = AuthnEvent("sub", "salt") sid = self.sdb.create_authz_session(ae, AREQN, oidreq=OIDR) self.sdb.do_sub(sid, "client_salt", "http://example.com/si.jwt", "pairwise") info_1 = self.sdb[sid].copy() assert "id_token" not in info_1 assert "oidreq" in info_1 assert info_1["sub"] != "sub" self.sdb.do_sub(sid, "client_salt", "http://example.net/si.jwt", "pairwise") info_2 = self.sdb[sid] assert info_2["sub"] != "sub" assert info_2["sub"] != info_1["sub"] def test_upgrade_to_token(self): ae1 = AuthnEvent("uid", "salt") sid = self.sdb.create_authz_session(ae1, AREQ) self.sdb[sid]['sub'] = 'sub' grant = self.sdb[sid]["code"] _dict = self.sdb.upgrade_to_token(grant) #print(_dict.keys()) assert _eq(list(_dict.keys()), ['authn_event', 'code', 'authzreq', 'revoked', 'access_token', 'token_type', 'state', 'redirect_uri', 'code_used', 'client_id', 'scope', 'oauth_state', 'access_token_scope', 'sub', 'response_type']) # can't update again with pytest.raises(AccessCodeUsed): self.sdb.upgrade_to_token(grant) self.sdb.upgrade_to_token(_dict["access_token"]) def test_upgrade_to_token_refresh(self): ae1 = AuthnEvent("sub", "salt") sid = self.sdb.create_authz_session(ae1, AREQO) self.sdb.do_sub(sid, ae1.salt) grant = self.sdb[sid]["code"] _dict = self.sdb.upgrade_to_token(grant, issue_refresh=True) #print(_dict.keys()) assert _eq(_dict.keys(), ['authn_event', 'code', 'authzreq', 'revoked', 'access_token', 'response_type', 'token_type', 'state', 'redirect_uri', 'code_used', 'client_id', 'scope', 'oauth_state', 'access_token_scope', 'refresh_token', 'sub']) # can't update again with pytest.raises(AccessCodeUsed): self.sdb.upgrade_to_token(grant) self.sdb.upgrade_to_token(_dict["access_token"]) def test_upgrade_to_token_with_id_token_and_oidreq(self): ae2 = AuthnEvent("another_user_id", "salt") sid = self.sdb.create_authz_session(ae2, AREQ) self.sdb[sid]['sub'] = 'sub' grant = self.sdb[sid]["code"] _dict = self.sdb.upgrade_to_token(grant, id_token="id_token", oidreq=OIDR) #print(_dict.keys()) assert _eq(list(_dict.keys()), ['authn_event', 'code', 'authzreq', 'revoked', 'oidreq', 'access_token', 'id_token', 'response_type', 'token_type', 'state', 'redirect_uri', 'code_used', 'client_id', 'scope', 'oauth_state', 'access_token_scope', 'sub']) assert _dict["id_token"] == "id_token" assert isinstance(_dict["oidreq"], OpenIDRequest) def test_refresh_token(self): ae = AuthnEvent("uid", "salt") sid = self.sdb.create_authz_session(ae, AREQ) self.sdb[sid]['sub'] = 'sub' grant = self.sdb[sid]["code"] # with mock.patch("time.gmtime", side_effect=[ # time.struct_time((1970, 1, 1, 10, 39, 0, 0, 0, 0)), # time.struct_time((1970, 1, 1, 10, 40, 0, 0, 0, 0))]): dict1 = self.sdb.upgrade_to_token(grant, issue_refresh=True).copy() rtoken = dict1["refresh_token"] dict2 = self.sdb.refresh_token(rtoken, AREQ['client_id']) assert dict1["access_token"] != dict2["access_token"] with pytest.raises(ExpiredToken): self.sdb.refresh_token(dict2["access_token"], AREQ['client_id']) def test_refresh_token_cleared_session(self): ae = AuthnEvent('uid', 'salt') sid = self.sdb.create_authz_session(ae, AREQ) self.sdb[sid]['sub'] = 'sub' grant = self.sdb[sid]['code'] dict1 = self.sdb.upgrade_to_token(grant, issue_refresh=True) ac1 = dict1['access_token'] rtoken = dict1['refresh_token'] dict2 = self.sdb.refresh_token(rtoken, AREQ['client_id']) assert ac1 != dict2["access_token"] def test_is_valid(self): ae1 = AuthnEvent("uid", "salt") sid = self.sdb.create_authz_session(ae1, AREQ) self.sdb[sid]['sub'] = 'sub' grant = self.sdb[sid]["code"] assert self.sdb.is_valid(grant) sinfo = self.sdb.upgrade_to_token(grant, issue_refresh=True) assert not self.sdb.is_valid(grant) access_token = sinfo["access_token"] assert self.sdb.access_token.valid(access_token) refresh_token = sinfo["refresh_token"] sinfo = self.sdb.refresh_token(refresh_token, AREQ['client_id']) access_token2 = sinfo["access_token"] assert self.sdb.is_valid(access_token2) # The old access code should be invalid try: self.sdb.is_valid(access_token) except KeyError: pass def test_valid_grant(self): ae = AuthnEvent("another:user", "salt") sid = self.sdb.create_authz_session(ae, AREQ) grant = self.sdb[sid]["code"] assert self.sdb.is_valid(grant) def test_revoke_token(self): ae1 = AuthnEvent("uid", "salt") sid = self.sdb.create_authz_session(ae1, AREQ) self.sdb[sid]['sub'] = 'sub' grant = self.sdb[sid]["code"] tokens = self.sdb.upgrade_to_token(grant, issue_refresh=True) access_token = tokens["access_token"] refresh_token = tokens["refresh_token"] assert self.sdb.is_valid(access_token) self.sdb.revoke_token(access_token) assert not self.sdb.is_valid(access_token) sinfo = self.sdb.refresh_token(refresh_token, AREQ['client_id']) access_token = sinfo["access_token"] assert self.sdb.is_valid(access_token) self.sdb.revoke_refresh_token(refresh_token) assert not self.sdb.is_valid(refresh_token) try: self.sdb.refresh_token(refresh_token, AREQ['client_id']) except ExpiredToken: pass assert self.sdb.is_valid(access_token) ae2 = AuthnEvent("sub", "salt") sid = self.sdb.create_authz_session(ae2, AREQ) grant = self.sdb[sid]["code"] self.sdb.revoke_token(grant) assert not self.sdb.is_valid(grant) def test_sub_to_authn_event(self): ae = AuthnEvent("sub", "salt", time_stamp=time.time()) sid = self.sdb.create_authz_session(ae, AREQ) sub = self.sdb.do_sub(sid, "client_salt") # given the sub find out whether the authn event is still valid sids = self.sdb.get_sids_by_sub(sub) ae = self.sdb[sids[0]]["authn_event"] assert ae.valid() def test_do_sub_deterministic(self): ae = AuthnEvent("tester", "random_value") sid = self.sdb.create_authz_session(ae, AREQ) self.sdb.do_sub(sid, "other_random_value") info = self.sdb[sid] assert info["sub"] == \ '179670cdee6375c48e577317b2abd7d5cd26a5cdb1cfb7ef84af3d703c71d013' self.sdb.do_sub(sid, "other_random_value", sector_id='http://example.com', subject_type="pairwise") info2 = self.sdb[sid] assert info2["sub"] == \ 'aaa50d80f8780cf1c4beb39e8e126556292f5091b9e39596424fefa2b99d9c53' self.sdb.do_sub(sid, "another_random_value", sector_id='http://other.example.com', subject_type="pairwise") info2 = self.sdb[sid] assert info2["sub"] == \ '62fb630e29f0d41b88e049ac0ef49a9c3ac5418c029d6e4f5417df7e9443976b'
class MyFakeOICServer(Server): def __init__(self, name=""): Server.__init__(self) self.sdb = SessionDB() self.name = name self.client = {} self.registration_expires_in = 3600 self.host = "" self.webfinger = WebFinger() #noinspection PyUnusedLocal def http_request(self, path, method="GET", **kwargs): part = urlparse(path) path = part[2] query = part[4] self.host = "%s://%s" % (part.scheme, part.netloc) response = Response response.status_code = 500 response.text = "" if path == ENDPOINT["authorization_endpoint"]: assert method == "GET" response = self.authorization_endpoint(query) elif path == ENDPOINT["token_endpoint"]: assert method == "POST" response = self.token_endpoint(kwargs["data"]) elif path == ENDPOINT["user_info_endpoint"]: assert method == "POST" response = self.userinfo_endpoint(kwargs["data"]) elif path == ENDPOINT["refresh_session_endpoint"]: assert method == "GET" response = self.refresh_session_endpoint(query) elif path == ENDPOINT["check_session_endpoint"]: assert method == "GET" response = self.check_session_endpoint(query) elif path == ENDPOINT["end_session_endpoint"]: assert method == "GET" response = self.end_session_endpoint(query) elif path == ENDPOINT["registration_endpoint"]: if method == "POST": response = self.registration_endpoint(kwargs["data"]) elif path == "/.well-known/webfinger": assert method == "GET" qdict = parse_qs(query) response.status_code = 200 response.text = self.webfinger.response(qdict["resource"][0], "%s/" % self.name) elif path == "/.well-known/openid-configuration": assert method == "GET" response = self.openid_conf() return response def authorization_endpoint(self, query): req = self.parse_authorization_request(query=query) sid = self.sdb.create_authz_session(sub="user", areq=req) _info = self.sdb[sid] _info["sub"] = _info["local_sub"] if "code" in req["response_type"]: if "token" in req["response_type"]: grant = _info["code"] _dict = self.sdb.upgrade_to_token(grant) _dict["oauth_state"] = "authz", _dict = by_schema(AuthorizationResponse(), **_dict) resp = AuthorizationResponse(**_dict) #resp.code = grant else: resp = AuthorizationResponse(state=req["state"], code=_info["code"]) else: # "implicit" in req.response_type: grant = _info["code"] params = AccessTokenResponse.c_param.keys() _dict = dict([(k, v) for k, v in self.sdb.upgrade_to_token(grant).items() if k in params]) try: del _dict["refresh_token"] except KeyError: pass if "id_token" in req["response_type"]: _idt = self.make_id_token(_info, issuer=self.name, access_token=_dict["access_token"]) alg = "RS256" ckey = self.keyjar.get_signing_key(alg2keytype(alg), _info["client_id"]) _dict["id_token"] = _idt.to_jwt(key=ckey, algorithm=alg) resp = AccessTokenResponse(**_dict) location = resp.request(req["redirect_uri"]) response = Response() response.headers = {"location": location} response.status_code = 302 response.text = "" return response def token_endpoint(self, data): if "grant_type=refresh_token" in data: req = self.parse_refresh_token_request(body=data) _info = self.sdb.refresh_token(req["refresh_token"]) elif "grant_type=authorization_code": req = self.parse_token_request(body=data) _info = self.sdb.upgrade_to_token(req["code"]) else: response = TokenErrorResponse(error="unsupported_grant_type") return response, "" resp = AccessTokenResponse(**by_schema(AccessTokenResponse, **_info)) response = Response() response.headers = {"content-type": "application/json"} response.text = resp.to_json() return response def userinfo_endpoint(self, data): _ = self.parse_user_info_request(data) _info = { "sub": "melgar", "name": "Melody Gardot", "nickname": "Mel", "email": "*****@*****.**", "verified": True, } resp = OpenIDSchema(**_info) response = Response() response.headers = {"content-type": "application/json"} response.text = resp.to_json() return response def registration_endpoint(self, data): try: req = self.parse_registration_request(data, "json") except ValueError: req = self.parse_registration_request(data) client_secret = rndstr() expires = utc_time_sans_frac() + self.registration_expires_in kwargs = {} if "client_id" not in req: client_id = rndstr(10) registration_access_token = rndstr(20) _client_info = req.to_dict() kwargs.update(_client_info) _client_info.update({ "client_secret": client_secret, "info": req.to_dict(), "expires": expires, "registration_access_token": registration_access_token, "registration_client_uri": "register_endpoint" }) self.client[client_id] = _client_info kwargs["registration_access_token"] = registration_access_token kwargs["registration_client_uri"] = "register_endpoint" try: del kwargs["operation"] except KeyError: pass else: client_id = req.client_id _cinfo = self.client[req.client_id] _cinfo["info"].update(req.to_dict()) _cinfo["client_secret"] = client_secret _cinfo["expires"] = expires resp = RegistrationResponse(client_id=client_id, client_secret=client_secret, client_secret_expires_at=expires, **kwargs) response = Response() response.headers = {"content-type": "application/json"} response.text = resp.to_json() return response def check_session_endpoint(self, query): try: idtoken = self.parse_check_session_request(query=query) except Exception: raise response = Response() response.text = idtoken.to_json() response.headers = {"content-type": "application/json"} return response #noinspection PyUnusedLocal def refresh_session_endpoint(self, query): try: req = self.parse_refresh_session_request(query=query) except Exception: raise resp = RegistrationResponse(client_id="anonymous", client_secret="hemligt") response = Response() response.headers = {"content-type": "application/json"} response.text = resp.to_json() return response def end_session_endpoint(self, query): try: req = self.parse_end_session_request(query=query) except Exception: raise # redirect back resp = EndSessionResponse(state=req["state"]) url = resp.request(req["redirect_url"]) response = Response() response.headers = {"location": url} response.status_code = 302 # redirect response.text = "" return response #noinspection PyUnusedLocal def add_credentials(self, user, passwd): return def openid_conf(self): endpoint = {} for point, path in ENDPOINT.items(): endpoint[point] = "%s%s" % (self.host, path) signing_algs = jws.SIGNER_ALGS.keys() resp = ProviderConfigurationResponse( issuer=self.name, scopes_supported=["openid", "profile", "email", "address"], identifiers_supported=["public", "PPID"], flows_supported=["code", "token", "code token", "id_token", "code id_token", "token id_token"], subject_types_supported=["pairwise", "public"], response_types_supported=["code", "token", "id_token", "code token", "code id_token", "token id_token", "code token id_token"], jwks_uri="http://example.com/oidc/jwks", id_token_signing_alg_values_supported=signing_algs, grant_types_supported=["authorization_code", "implicit"], **endpoint) response = Response() response.headers = {"content-type": "application/json"} response.text = resp.to_json() return response
class MyFakeOAuth2Server(Server): def __init__(self, jwt_keys=None, name=""): Server.__init__(self, jwt_keys=jwt_keys) self.sdb = SessionDB() self.name = name self.client = {} self.registration_expires_in = 3600 self.host = "" #noinspection PyUnusedLocal def http_request(self, url, method="GET", **kwargs): part = urlparse.urlparse(url) path = part[2] query = part[4] self.host = "%s://%s" % (part.scheme, part.netloc) response = Response response.status_code = 500 response.text = "" if path == ENDPOINT["authorization_endpoint"]: assert method == "GET" response = self.authorization_endpoint(query) elif path == ENDPOINT["token_endpoint"]: assert method == "POST" response = self.token_endpoint(kwargs["data"]) return response def authorization_endpoint(self, query): req = self.parse_authorization_request(query=query) sid = self.sdb.create_authz_session(user_id="user", areq=req) _info = self.sdb[sid] if "code" in req["response_type"]: if "token" in req["response_type"]: grant = _info["code"] _dict = self.sdb.upgrade_to_token(grant) _dict["oauth_state"]="authz", _dict = by_schema(AuthorizationResponse(), **_dict) resp = AuthorizationResponse(**_dict) #resp.code = grant else: resp = AuthorizationResponse(state=req["state"], code=_info["code"]) else: # "implicit" in req.response_type: grant = _info["code"] params = AccessTokenResponse.c_param.keys() _dict = dict([(k,v) for k, v in self.sdb.upgrade_to_token(grant).items() if k in params]) try: del _dict["refresh_token"] except KeyError: pass resp = AccessTokenResponse(**_dict) location = resp.request(req["redirect_uri"]) response= Response() response.headers = {"location":location} response.status_code = 302 response.text = "" return response def token_endpoint(self, data): if "grant_type=refresh_token" in data: req = self.parse_refresh_token_request(body=data) _info = self.sdb.refresh_token(req["refresh_token"]) elif "grant_type=authorization_code": req = self.parse_token_request(body=data) _info = self.sdb.update_to_token(req["code"]) else: response = TokenErrorResponse(error="unsupported_grant_type") return response, "" resp = AccessTokenResponse(**by_schema(AccessTokenResponse, **_info)) response = Response() response.headers = {"content-type":"application/json"} response.text = resp.to_json() return response
class TestSessionDB(object): @pytest.fixture(autouse=True) def create_sdb(self): self.sdb = SessionDB("https://example.com/") def test_create_authz_session(self): ae = AuthnEvent("uid", "salt") sid = self.sdb.create_authz_session(ae, AREQ) self.sdb.do_sub(sid, "client_salt") info = self.sdb[sid] assert info["oauth_state"] == "authz" def test_create_authz_session_without_nonce(self): ae = AuthnEvent("sub", "salt") sid = self.sdb.create_authz_session(ae, AREQ) info = self.sdb[sid] assert info["oauth_state"] == "authz" def test_create_authz_session_with_nonce(self): ae = AuthnEvent("sub", "salt") sid = self.sdb.create_authz_session(ae, AREQN) info = self.sdb[sid] assert info["nonce"] == "something" def test_create_authz_session_with_id_token(self): ae = AuthnEvent("sub", "salt") sid = self.sdb.create_authz_session(ae, AREQN, id_token="id_token") info = self.sdb[sid] assert info["id_token"] == "id_token" def test_create_authz_session_with_oidreq(self): ae = AuthnEvent("sub", "salt") sid = self.sdb.create_authz_session(ae, AREQN, oidreq=OIDR) info = self.sdb[sid] assert "id_token" not in info assert "oidreq" in info def test_create_authz_session_with_sector_id(self): ae = AuthnEvent("sub", "salt") sid = self.sdb.create_authz_session(ae, AREQN, oidreq=OIDR) self.sdb.do_sub(sid, "client_salt", "http://example.com/si.jwt", "pairwise") info_1 = self.sdb[sid].copy() assert "id_token" not in info_1 assert "oidreq" in info_1 assert info_1["sub"] != "sub" self.sdb.do_sub(sid, "client_salt", "http://example.net/si.jwt", "pairwise") info_2 = self.sdb[sid] assert info_2["sub"] != "sub" assert info_2["sub"] != info_1["sub"] def test_upgrade_to_token(self): ae1 = AuthnEvent("uid", "salt") sid = self.sdb.create_authz_session(ae1, AREQ) self.sdb[sid]['sub'] = 'sub' grant = self.sdb[sid]["code"] _dict = self.sdb.upgrade_to_token(grant) print(_dict.keys()) assert _eq(list(_dict.keys()), [ 'authn_event', 'code', 'authzreq', 'revoked', 'access_token', 'token_type', 'state', 'redirect_uri', 'code_used', 'client_id', 'scope', 'oauth_state', 'access_token_scope', 'sub', 'response_type' ]) # can't update again with pytest.raises(AccessCodeUsed): self.sdb.upgrade_to_token(grant) self.sdb.upgrade_to_token(_dict["access_token"]) def test_upgrade_to_token_refresh(self): ae1 = AuthnEvent("sub", "salt") sid = self.sdb.create_authz_session(ae1, AREQO) self.sdb.do_sub(sid, ae1.salt) grant = self.sdb[sid]["code"] _dict = self.sdb.upgrade_to_token(grant, issue_refresh=True) print(_dict.keys()) assert _eq(_dict.keys(), [ 'authn_event', 'code', 'authzreq', 'revoked', 'access_token', 'response_type', 'token_type', 'state', 'redirect_uri', 'code_used', 'client_id', 'scope', 'oauth_state', 'access_token_scope', 'refresh_token', 'sub' ]) # can't update again with pytest.raises(AccessCodeUsed): self.sdb.upgrade_to_token(grant) self.sdb.upgrade_to_token(_dict["access_token"]) def test_upgrade_to_token_with_id_token_and_oidreq(self): ae2 = AuthnEvent("another_user_id", "salt") sid = self.sdb.create_authz_session(ae2, AREQ) self.sdb[sid]['sub'] = 'sub' grant = self.sdb[sid]["code"] _dict = self.sdb.upgrade_to_token(grant, id_token="id_token", oidreq=OIDR) print(_dict.keys()) assert _eq(list(_dict.keys()), [ 'authn_event', 'code', 'authzreq', 'revoked', 'oidreq', 'access_token', 'id_token', 'response_type', 'token_type', 'state', 'redirect_uri', 'code_used', 'client_id', 'scope', 'oauth_state', 'access_token_scope', 'sub' ]) assert _dict["id_token"] == "id_token" assert isinstance(_dict["oidreq"], OpenIDRequest) def test_refresh_token(self): ae = AuthnEvent("uid", "salt") sid = self.sdb.create_authz_session(ae, AREQ) self.sdb[sid]['sub'] = 'sub' grant = self.sdb[sid]["code"] # with mock.patch("time.gmtime", side_effect=[ # time.struct_time((1970, 1, 1, 10, 39, 0, 0, 0, 0)), # time.struct_time((1970, 1, 1, 10, 40, 0, 0, 0, 0))]): dict1 = self.sdb.upgrade_to_token(grant, issue_refresh=True).copy() rtoken = dict1["refresh_token"] dict2 = self.sdb.refresh_token(rtoken, AREQ['client_id']) assert dict1["access_token"] != dict2["access_token"] with pytest.raises(WrongTokenType): self.sdb.refresh_token(dict2["access_token"], AREQ['client_id']) def test_refresh_token_cleared_session(self): ae = AuthnEvent('uid', 'salt') sid = self.sdb.create_authz_session(ae, AREQ) self.sdb[sid]['sub'] = 'sub' grant = self.sdb[sid]['code'] dict1 = self.sdb.upgrade_to_token(grant, issue_refresh=True) ac1 = dict1['access_token'] # Purge the SessionDB self.sdb._db = {} rtoken = dict1['refresh_token'] dict2 = self.sdb.refresh_token(rtoken, AREQ['client_id']) assert ac1 != dict2["access_token"] assert self.sdb.is_valid(dict2['access_token']) def test_is_valid(self): ae1 = AuthnEvent("uid", "salt") sid = self.sdb.create_authz_session(ae1, AREQ) self.sdb[sid]['sub'] = 'sub' grant = self.sdb[sid]["code"] assert self.sdb.is_valid(grant) sinfo = self.sdb.upgrade_to_token(grant, issue_refresh=True) assert not self.sdb.is_valid(grant) access_token = sinfo["access_token"] assert self.sdb.access_token.valid(access_token) refresh_token = sinfo["refresh_token"] sinfo = self.sdb.refresh_token(refresh_token, AREQ['client_id']) access_token2 = sinfo["access_token"] assert self.sdb.is_valid(access_token2) # The old access code should be invalid try: self.sdb.is_valid(access_token) except KeyError: pass def test_valid_grant(self): ae = AuthnEvent("another:user", "salt") sid = self.sdb.create_authz_session(ae, AREQ) grant = self.sdb[sid]["code"] assert self.sdb.is_valid(grant) def test_revoke_token(self): ae1 = AuthnEvent("uid", "salt") sid = self.sdb.create_authz_session(ae1, AREQ) self.sdb[sid]['sub'] = 'sub' grant = self.sdb[sid]["code"] tokens = self.sdb.upgrade_to_token(grant, issue_refresh=True) access_token = tokens["access_token"] refresh_token = tokens["refresh_token"] assert self.sdb.is_valid(access_token) self.sdb.revoke_token(access_token) assert not self.sdb.is_valid(access_token) sinfo = self.sdb.refresh_token(refresh_token, AREQ['client_id']) access_token = sinfo["access_token"] assert self.sdb.is_valid(access_token) self.sdb.revoke_refresh_token(refresh_token) assert not self.sdb.is_valid(refresh_token) try: self.sdb.refresh_token(refresh_token, AREQ['client_id']) except ExpiredToken: pass assert self.sdb.is_valid(access_token) ae2 = AuthnEvent("sub", "salt") sid = self.sdb.create_authz_session(ae2, AREQ) grant = self.sdb[sid]["code"] self.sdb.revoke_token(grant) assert not self.sdb.is_valid(grant) def test_sub_to_authn_event(self): ae = AuthnEvent("sub", "salt", time_stamp=time.time()) sid = self.sdb.create_authz_session(ae, AREQ) sub = self.sdb.do_sub(sid, "client_salt") # given the sub find out whether the authn event is still valid sids = self.sdb.get_sids_by_sub(sub) ae = self.sdb[sids[0]]["authn_event"] assert ae.valid() def test_do_sub_deterministic(self): ae = AuthnEvent("tester", "random_value") sid = self.sdb.create_authz_session(ae, AREQ) self.sdb.do_sub(sid, "other_random_value") info = self.sdb[sid] assert info[ "sub"] == \ '179670cdee6375c48e577317b2abd7d5cd26a5cdb1cfb7ef84af3d703c71d013' self.sdb.do_sub(sid, "other_random_value", sector_id='http://example.com', subject_type="pairwise") info2 = self.sdb[sid] assert info2[ "sub"] == \ 'aaa50d80f8780cf1c4beb39e8e126556292f5091b9e39596424fefa2b99d9c53' self.sdb.do_sub(sid, "another_random_value", sector_id='http://other.example.com', subject_type="pairwise") info2 = self.sdb[sid] assert info2[ "sub"] == '62fb630e29f0d41b88e049ac0ef49a9c3ac5418c029d6e4f5417df7e9443976b'
class MyFakeOAuth2Server(Server): def __init__(self, jwt_keys=None, name=""): Server.__init__(self, jwt_keys=jwt_keys) self.sdb = SessionDB() self.name = name self.client = {} self.registration_expires_in = 3600 self.host = "" #noinspection PyUnusedLocal def http_request(self, url, method="GET", **kwargs): part = urlparse.urlparse(url) path = part[2] query = part[4] self.host = "%s://%s" % (part.scheme, part.netloc) response = Response response.status_code = 500 response.text = "" if path == ENDPOINT["authorization_endpoint"]: assert method == "GET" response = self.authorization_endpoint(query) elif path == ENDPOINT["token_endpoint"]: assert method == "POST" response = self.token_endpoint(kwargs["data"]) return response def authorization_endpoint(self, query): req = self.parse_authorization_request(query=query) sid = self.sdb.create_authz_session(user_id="user", areq=req) _info = self.sdb[sid] if "code" in req["response_type"]: if "token" in req["response_type"]: grant = _info["code"] _dict = self.sdb.update_to_token(grant) _dict["oauth_state"]="authz", _dict = by_schema(AuthorizationResponse(), **_dict) resp = AuthorizationResponse(**_dict) #resp.code = grant else: resp = AuthorizationResponse(state=req["state"], code=_info["code"]) else: # "implicit" in req.response_type: grant = _info["code"] params = AccessTokenResponse.c_param.keys() _dict = dict([(k,v) for k, v in self.sdb.update_to_token(grant).items() if k in params]) try: del _dict["refresh_token"] except KeyError: pass resp = AccessTokenResponse(**_dict) location = resp.request(req["redirect_uri"]) response= Response() response.headers = {"location":location} response.status_code = 302 response.text = "" return response def token_endpoint(self, data): if "grant_type=refresh_token" in data: req = self.parse_refresh_token_request(body=data) _info = self.sdb.refresh_token(req["refresh_token"]) elif "grant_type=authorization_code": req = self.parse_token_request(body=data) _info = self.sdb.update_to_token(req["code"]) else: response = TokenErrorResponse(error="unsupported_grant_type") return response, "" resp = AccessTokenResponse(**by_schema(AccessTokenResponse, **_info)) response = Response() response.headers = {"content-type":"application/json"} response.text = resp.to_json() return response
class TestSessionDB(object): @pytest.fixture(autouse=True) def create_sdb(self): self.sdb = SessionDB("https://example.com/") def test_setitem(self): sid = self.sdb.token.key(areq=AREQ) code = self.sdb.token(sid=sid) self.sdb[sid] = {"indo": "china"} info = self.sdb[sid] assert info == {"indo": "china"} info = self.sdb[code] assert info == {"indo": "china"} def test_getitem_key_error(self): with pytest.raises(KeyError): self.sdb["abcdefghijklmnop"] def test_update(self): sid = self.sdb.token.key(areq=AREQ) code = self.sdb.token(sid=sid) self.sdb[sid] = {"indo": "china"} self.sdb.update(sid, "indo", "nebue") self.sdb.update(code, "indo", "second") def test_update_non_existing(self): sid = self.sdb.token.key(areq=AREQ) code = self.sdb.token(sid=sid) # can't update non-existing with pytest.raises(KeyError): self.sdb.update(sid, "indo", "nebue") self.sdb.update(code, "indo", "nebue") self.sdb.update("abcdefghijklmnop", "indo", "bar") def test_create_authz_session(self): ae = AuthnEvent("uid", "salt") sid = self.sdb.create_authz_session(ae, AREQ) self.sdb.do_sub(sid, "client_salt") info = self.sdb[sid] assert info["oauth_state"] == "authz" def test_create_authz_session_without_nonce(self): ae = AuthnEvent("sub", "salt") sid = self.sdb.create_authz_session(ae, AREQ) info = self.sdb[sid] assert info["oauth_state"] == "authz" def test_create_authz_session_with_nonce(self): ae = AuthnEvent("sub", "salt") sid = self.sdb.create_authz_session(ae, AREQN) info = self.sdb[sid] assert info["nonce"] == "something" def test_create_authz_session_with_id_token(self): ae = AuthnEvent("sub", "salt") sid = self.sdb.create_authz_session(ae, AREQN, id_token="id_token") info = self.sdb[sid] assert info["id_token"] == "id_token" def test_create_authz_session_with_oidreq(self): ae = AuthnEvent("sub", "salt") sid = self.sdb.create_authz_session(ae, AREQN, oidreq=OIDR) info = self.sdb[sid] assert "id_token" not in info assert "oidreq" in info def test_create_authz_session_with_sector_id(self): ae = AuthnEvent("sub", "salt") sid = self.sdb.create_authz_session(ae, AREQN, oidreq=OIDR) self.sdb.do_sub(sid, "client_salt", "http://example.com/si.jwt", "pairwise") info_1 = self.sdb[sid].copy() assert "id_token" not in info_1 assert "oidreq" in info_1 assert info_1["sub"] != "sub" self.sdb.do_sub(sid, "client_salt", "http://example.net/si.jwt", "pairwise") info_2 = self.sdb[sid] assert info_2["sub"] != "sub" assert info_2["sub"] != info_1["sub"] def test_upgrade_to_token(self): ae1 = AuthnEvent("uid", "salt") sid = self.sdb.create_authz_session(ae1, AREQ) self.sdb[sid]['sub'] = 'sub' grant = self.sdb[sid]["code"] _dict = self.sdb.upgrade_to_token(grant) print(_dict.keys()) assert _eq(list(_dict.keys()), ['authn_event', 'code', 'authzreq', 'revoked', 'access_token', 'token_expires_at', 'expires_in', 'token_type', 'state', 'redirect_uri', 'code_used', 'client_id', 'scope', 'oauth_state', 'access_token_scope', 'sub']) # can't update again with pytest.raises(AccessCodeUsed): self.sdb.upgrade_to_token(grant) self.sdb.upgrade_to_token(_dict["access_token"]) def test_upgrade_to_token_refresh(self): ae1 = AuthnEvent("sub", "salt") sid = self.sdb.create_authz_session(ae1, AREQO) self.sdb.do_sub(sid, ae1.salt) grant = self.sdb[sid]["code"] _dict = self.sdb.upgrade_to_token(grant, issue_refresh=True) print(_dict.keys()) assert _eq(_dict.keys(), ['authn_event', 'code', 'authzreq', 'revoked', 'access_token', 'token_expires_at', 'expires_in', 'token_type', 'state', 'redirect_uri', 'code_used', 'client_id', 'scope', 'oauth_state', 'access_token_scope', 'refresh_token', 'sub']) # can't update again with pytest.raises(AccessCodeUsed): self.sdb.upgrade_to_token(grant) self.sdb.upgrade_to_token(_dict["access_token"]) def test_upgrade_to_token_with_id_token_and_oidreq(self): ae2 = AuthnEvent("another_user_id", "salt") sid = self.sdb.create_authz_session(ae2, AREQ) self.sdb[sid]['sub'] = 'sub' grant = self.sdb[sid]["code"] _dict = self.sdb.upgrade_to_token(grant, id_token="id_token", oidreq=OIDR) print(_dict.keys()) assert _eq(list(_dict.keys()), ['authn_event', 'code', 'authzreq', 'revoked', 'oidreq', 'access_token', 'id_token', 'token_expires_at', 'expires_in', 'token_type', 'state', 'redirect_uri', 'code_used', 'client_id', 'scope', 'oauth_state', 'access_token_scope', 'sub']) assert _dict["id_token"] == "id_token" assert isinstance(_dict["oidreq"], OpenIDRequest) def test_refresh_token(self): ae = AuthnEvent("uid", "salt") sid = self.sdb.create_authz_session(ae, AREQ) self.sdb[sid]['sub'] = 'sub' grant = self.sdb[sid]["code"] with mock.patch("time.gmtime", side_effect=[ time.struct_time((1970, 1, 1, 10, 39, 0, 0, 0, 0)), time.struct_time((1970, 1, 1, 10, 40, 0, 0, 0, 0))]): dict1 = self.sdb.upgrade_to_token(grant, issue_refresh=True).copy() rtoken = dict1["refresh_token"] dict2 = self.sdb.refresh_token(rtoken, AREQ['client_id']) assert dict1["token_expires_at"] != dict2["token_expires_at"] assert dict1["access_token"] != dict2["access_token"] with pytest.raises(WrongTokenType): self.sdb.refresh_token(dict2["access_token"], AREQ['client_id']) def test_refresh_token_cleared_session(self): ae = AuthnEvent('uid', 'salt') sid = self.sdb.create_authz_session(ae, AREQ) self.sdb[sid]['sub'] = 'sub' grant = self.sdb[sid]['code'] dict1 = self.sdb.upgrade_to_token(grant, issue_refresh=True) # Purge session DB self.sdb._db = {} # Our refresh token shoudl still work with mock.patch("time.gmtime", side_effect=[ time.struct_time((1970, 1, 1, 10, 39, 0, 0, 0, 0)), time.struct_time((1970, 1, 1, 10, 40, 0, 0, 0, 0))]): rtoken = dict1['refresh_token'] dict2 = self.sdb.refresh_token(rtoken, AREQ['client_id']) assert dict1["token_expires_at"] != dict2["token_expires_at"] assert dict1["access_token"] != dict2["access_token"] def test_is_valid(self): ae1 = AuthnEvent("uid", "salt") sid = self.sdb.create_authz_session(ae1, AREQ) self.sdb[sid]['sub'] = 'sub' grant = self.sdb[sid]["code"] assert self.sdb.is_valid(grant) tokens = self.sdb.upgrade_to_token(grant, issue_refresh=True) assert not self.sdb.is_valid(grant) access_token = tokens["access_token"] assert self.sdb.is_valid(access_token) refresh_token = tokens["refresh_token"] assert self.sdb.is_valid(refresh_token, AREQ['client_id']) refreshed_tokens = self.sdb.refresh_token(refresh_token, AREQ['client_id']) access_token2 = refreshed_tokens["access_token"] assert self.sdb.is_valid(access_token2) # replace refresh_token refreshed_tokens["refresh_token"] = access_token2 assert not self.sdb.is_valid(refresh_token) # mess with the time-line refreshed_tokens["token_expires_at"] = utc_time_sans_frac() - 86400 assert not self.sdb.is_valid(access_token2) # replace access_token refreshed_tokens["access_token"] = access_token assert not self.sdb.is_valid(access_token2) ae = AuthnEvent("another:user", "salt") sid = self.sdb.create_authz_session(ae, AREQ) grant = self.sdb[sid]["code"] self.sdb.update(grant, "token_expires_at", utc_time_sans_frac() - 86400) assert not self.sdb.is_valid(grant) def test_revoke_token(self): ae1 = AuthnEvent("uid", "salt") sid = self.sdb.create_authz_session(ae1, AREQ) self.sdb[sid]['sub'] = 'sub' grant = self.sdb[sid]["code"] tokens = self.sdb.upgrade_to_token(grant, issue_refresh=True) access_token = tokens["access_token"] refresh_token = tokens["refresh_token"] assert self.sdb.is_valid(access_token) self.sdb.revoke_token(access_token) assert not self.sdb.is_valid(access_token) refreshed_tokens = self.sdb.refresh_token(refresh_token, AREQ['client_id']) access_token = refreshed_tokens["access_token"] assert self.sdb.is_valid(access_token) self.sdb.revoke_refresh_token(refresh_token) assert not self.sdb.is_valid(refresh_token) with pytest.raises(ExpiredToken): self.sdb.refresh_token(refresh_token, AREQ['client_id']) assert self.sdb.is_valid(access_token) ae2 = AuthnEvent("sub", "salt") sid = self.sdb.create_authz_session(ae2, AREQ) grant = self.sdb[sid]["code"] self.sdb.revoke_token(grant) assert not self.sdb.is_valid(grant) def test_sub_to_authn_event(self): ae = AuthnEvent("sub", "salt", time_stamp=time.time()) sid = self.sdb.create_authz_session(ae, AREQ) sub = self.sdb.do_sub(sid, "client_salt") # given the sub find out whether the authn event is still valid sids = self.sdb.get_sids_by_sub(sub) ae = self.sdb[sids[0]]["authn_event"] assert ae.valid() def test_do_sub_deterministic(self): ae = AuthnEvent("tester", "random_value") sid = self.sdb.create_authz_session(ae, AREQ) self.sdb.do_sub(sid, "other_random_value") info = self.sdb[sid] assert info[ "sub"] == \ '179670cdee6375c48e577317b2abd7d5cd26a5cdb1cfb7ef84af3d703c71d013' self.sdb.do_sub(sid, "other_random_value", sector_id='http://example.com', subject_type="pairwise") info2 = self.sdb[sid] assert info2[ "sub"] == \ 'aaa50d80f8780cf1c4beb39e8e126556292f5091b9e39596424fefa2b99d9c53' self.sdb.do_sub(sid, "another_random_value", sector_id='http://other.example.com', subject_type="pairwise") info2 = self.sdb[sid] assert info2[ "sub"] == \ '62fb630e29f0d41b88e049ac0ef49a9c3ac5418c029d6e4f5417df7e9443976b'
class MyFakeOICServer(Server): def __init__(self, jwt_keys=None, name=""): Server.__init__(self, jwt_keys=jwt_keys) self.sdb = SessionDB() self.name = name self.client = {} self.registration_expires_in = 3600 self.host = "" #noinspection PyUnusedLocal def http_request(self, path, method="GET", **kwargs): part = urlparse.urlparse(path) path = part[2] query = part[4] self.host = "%s://%s" % (part.scheme, part.netloc) response = Response response.status_code = 500 response.text = "" if path == ENDPOINT["authorization_endpoint"]: assert method == "GET" response = self.authorization_endpoint(query) elif path == ENDPOINT["token_endpoint"]: assert method == "POST" response = self.token_endpoint(kwargs["data"]) elif path == ENDPOINT["user_info_endpoint"]: assert method == "POST" response = self.userinfo_endpoint(kwargs["data"]) elif path == ENDPOINT["refresh_session_endpoint"]: assert method == "GET" response = self.refresh_session_endpoint(query) elif path == ENDPOINT["check_session_endpoint"]: assert method == "GET" response = self.check_session_endpoint(query) elif path == ENDPOINT["end_session_endpoint"]: assert method == "GET" response = self.end_session_endpoint(query) elif path == ENDPOINT["registration_endpoint"]: if method == "POST": response = self.registration_endpoint(kwargs["data"]) elif path == "/.well-known/simple-web-discovery": assert method == "GET" response = self.issuer(query) elif path == "/swd_server": assert method == "GET" response = self.swd_server(query) elif path == "/.well-known/openid-configuration"\ or path == "/providerconf/.well-known/openid-configuration": assert method == "GET" response = self.openid_conf() return response def authorization_endpoint(self, query): req = self.parse_authorization_request(query=query) sid = self.sdb.create_authz_session(user_id="user", areq=req) _info = self.sdb[sid] if "code" in req["response_type"]: if "token" in req["response_type"]: grant = _info["code"] _dict = self.sdb.update_to_token(grant) _dict["oauth_state"]="authz", _dict = by_schema(AuthorizationResponse(), **_dict) resp = AuthorizationResponse(**_dict) #resp.code = grant else: resp = AuthorizationResponse(state=req["state"], code=_info["code"]) else: # "implicit" in req.response_type: grant = _info["code"] params = AccessTokenResponse.c_param.keys() _dict = dict([(k,v) for k, v in self.sdb.update_to_token(grant).items() if k in params]) try: del _dict["refresh_token"] except KeyError: pass if "id_token" in req["response_type"]: _dict["id_token"] = self.make_id_token(_info, issuer=self.name, access_token=_dict["access_token"]) resp = AccessTokenResponse(**_dict) location = resp.request(req["redirect_uri"]) response= Response() response.headers = {"location":location} response.status_code = 302 response.text = "" return response def token_endpoint(self, data): if "grant_type=refresh_token" in data: req = self.parse_refresh_token_request(body=data) _info = self.sdb.refresh_token(req["refresh_token"]) elif "grant_type=authorization_code": req = self.parse_token_request(body=data) _info = self.sdb.update_to_token(req["code"]) else: response = TokenErrorResponse(error="unsupported_grant_type") return response, "" resp = AccessTokenResponse(**by_schema(AccessTokenResponse, **_info)) response = Response() response.headers = {"content-type":"application/json"} response.text = resp.to_json() return response def userinfo_endpoint(self, data): _ = self.parse_user_info_request(data) _info = { "name": "Melody Gardot", "nickname": "Mel", "email": "*****@*****.**", "verified": True, } resp = OpenIDSchema(**_info) response = Response() response.headers = {"content-type":"application/json"} response.text = resp.to_json() return response def registration_endpoint(self, data): req = self.parse_registration_request(data) client_secret = rndstr() expires = utc_time_sans_frac() + self.registration_expires_in if req["type"] == "client_associate": client_id = rndstr(10) self.client[client_id] = { "client_secret": client_secret, "info": req.to_dict(), "expires": expires } else: client_id = req.client_id _cinfo = self.client[req.client_id] _cinfo["info"].update(req.to_dict()) _cinfo["client_secret"] = client_secret _cinfo["expires"] = expires resp = RegistrationResponseCARS(client_id=client_id, client_secret=client_secret, expires_at=expires) response = Response() response.headers = {"content-type":"application/json"} response.text = resp.to_json() return response def check_session_endpoint(self, query): try: idtoken = self.parse_check_session_request(query=query) except Exception: raise response = Response() response.text = idtoken.to_json() response.headers = {"content-type":"application/json"} return response #noinspection PyUnusedLocal def refresh_session_endpoint(self, query): try: req = self.parse_refresh_session_request(query=query) except Exception: raise resp = RegistrationResponseCARS(client_id="anonymous", client_secret="hemligt") response = Response() response.headers = {"content-type":"application/json"} response.text = resp.to_json() return response def end_session_endpoint(self, query): try: req = self.parse_end_session_request(query=query) except Exception: raise # redirect back resp = EndSessionResponse(state=req["state"]) url = resp.request(req["redirect_url"]) response = Response() response.headers= {"location":url} response.status_code = 302 # redirect response.text = "" return response #noinspection PyUnusedLocal def add_credentials(self, user, passwd): return def issuer(self, query): request = self.parse_issuer_request(query) if request["principal"] == "*****@*****.**": resp = IssuerResponse(locations="http://example.com/") elif request["principal"] == "*****@*****.**": swd = SWDServiceRedirect(location="https://example.net/swd_server") resp = IssuerResponse(SWD_service_redirect=swd, expires=time_sans_frac() + 600) else: resp = None if resp is None: response = Response() response.status = 401 return response, "" else: response = Response() response.headers = {"content-type":"application/json"} response.text = resp.to_json() return response def swd_server(self, query): request = self.parse_issuer_request(query) if request["principal"] == "*****@*****.**": resp = IssuerResponse(locations="http://example.net/providerconf") else: resp = None if resp is None: response = Response() response.status_code = 401 response.text = "" return response else: response = Response() response.headers = {"content-type":"application/json"} response.text = resp.to_json() return response def openid_conf(self): endpoint = {} for point, path in ENDPOINT.items(): endpoint[point] = "%s%s" % (self.host, path) resp = ProviderConfigurationResponse( issuer=self.name, scopes_supported=["openid", "profile", "email", "address"], identifiers_supported=["public", "PPID"], flows_supported=["code", "token", "code token", "id_token", "code id_token", "token id_token"], **endpoint) response = Response() response.headers = {"content-type":"application/json"} response.text = resp.to_json() return response
class TestSessionDB(object): @pytest.fixture(autouse=True) def create_sdb(self): self.sdb = SessionDB("https://example.com/") def test_setitem(self): sid = self.sdb.token.key(areq=AREQ) code = self.sdb.token(sid=sid) self.sdb[sid] = {"indo": "china"} info = self.sdb[sid] assert info == {"indo": "china"} info = self.sdb[code] assert info == {"indo": "china"} def test_getitem_key_error(self): with pytest.raises(KeyError): self.sdb["abcdefghijklmnop"] def test_update(self): sid = self.sdb.token.key(areq=AREQ) code = self.sdb.token(sid=sid) self.sdb[sid] = {"indo": "china"} self.sdb.update(sid, "indo", "nebue") self.sdb.update(code, "indo", "second") def test_update_non_existing(self): sid = self.sdb.token.key(areq=AREQ) code = self.sdb.token(sid=sid) # can't update non-existing with pytest.raises(KeyError): self.sdb.update(sid, "indo", "nebue") self.sdb.update(code, "indo", "nebue") self.sdb.update("abcdefghijklmnop", "indo", "bar") def test_create_authz_session(self): ae = AuthnEvent("uid", "salt") sid = self.sdb.create_authz_session(ae, AREQ) self.sdb.do_sub(sid, "client_salt") info = self.sdb[sid] assert info["oauth_state"] == "authz" def test_create_authz_session_without_nonce(self): ae = AuthnEvent("sub", "salt") sid = self.sdb.create_authz_session(ae, AREQ) info = self.sdb[sid] assert info["oauth_state"] == "authz" def test_create_authz_session_with_nonce(self): ae = AuthnEvent("sub", "salt") sid = self.sdb.create_authz_session(ae, AREQN) info = self.sdb[sid] assert info["nonce"] == "something" def test_create_authz_session_with_id_token(self): ae = AuthnEvent("sub", "salt") sid = self.sdb.create_authz_session(ae, AREQN, id_token="id_token") info = self.sdb[sid] assert info["id_token"] == "id_token" def test_create_authz_session_with_oidreq(self): ae = AuthnEvent("sub", "salt") sid = self.sdb.create_authz_session(ae, AREQN, oidreq=OIDR) info = self.sdb[sid] assert "id_token" not in info assert "oidreq" in info def test_create_authz_session_with_sector_id(self): ae = AuthnEvent("sub", "salt") sid = self.sdb.create_authz_session(ae, AREQN, oidreq=OIDR) self.sdb.do_sub(sid, "client_salt", "http://example.com/si.jwt", "pairwise") info_1 = self.sdb[sid].copy() assert "id_token" not in info_1 assert "oidreq" in info_1 assert info_1["sub"] != "sub" self.sdb.do_sub(sid, "client_salt", "http://example.net/si.jwt", "pairwise") info_2 = self.sdb[sid] assert info_2["sub"] != "sub" assert info_2["sub"] != info_1["sub"] def test_upgrade_to_token(self): ae1 = AuthnEvent("sub", "salt") sid = self.sdb.create_authz_session(ae1, AREQ) grant = self.sdb[sid]["code"] _dict = self.sdb.upgrade_to_token(grant) assert _eq(_dict.keys(), [ 'authn_event', 'code', 'authzreq', 'revoked', 'access_token', 'token_expires_at', 'expires_in', 'token_type', 'state', 'redirect_uri', 'code_used', 'client_id', 'scope', 'oauth_state', 'refresh_token', 'access_token_scope' ]) # can't update again with pytest.raises(AccessCodeUsed): self.sdb.upgrade_to_token(grant) self.sdb.upgrade_to_token(_dict["access_token"]) def test_upgrade_to_token_with_id_token_and_oidreq(self): ae2 = AuthnEvent("another_user_id", "salt") sid = self.sdb.create_authz_session(ae2, AREQ) grant = self.sdb[sid]["code"] _dict = self.sdb.upgrade_to_token(grant, id_token="id_token", oidreq=OIDR) assert _eq(list(_dict.keys()), [ 'authn_event', 'code', 'authzreq', 'revoked', 'oidreq', 'access_token', 'id_token', 'token_expires_at', 'expires_in', 'token_type', 'state', 'redirect_uri', 'code_used', 'client_id', 'scope', 'oauth_state', 'refresh_token', 'access_token_scope' ]) assert _dict["id_token"] == "id_token" assert isinstance(_dict["oidreq"], OpenIDRequest) def test_refresh_token(self): ae = AuthnEvent("sub", "salt") sid = self.sdb.create_authz_session(ae, AREQ) grant = self.sdb[sid]["code"] with mock.patch("time.gmtime", side_effect=[ time.struct_time((1970, 1, 1, 10, 39, 0, 0, 0, 0)), time.struct_time((1970, 1, 1, 10, 40, 0, 0, 0, 0)) ]): dict1 = self.sdb.upgrade_to_token(grant).copy() rtoken = dict1["refresh_token"] dict2 = self.sdb.refresh_token(rtoken) assert dict1["token_expires_at"] != dict2["token_expires_at"] assert dict1["access_token"] != dict2["access_token"] with pytest.raises(WrongTokenType): self.sdb.refresh_token(dict2["access_token"]) def test_is_valid(self): ae1 = AuthnEvent("sub", "salt") sid = self.sdb.create_authz_session(ae1, AREQ) grant = self.sdb[sid]["code"] assert self.sdb.is_valid(grant) tokens = self.sdb.upgrade_to_token(grant) assert not self.sdb.is_valid(grant) access_token = tokens["access_token"] assert self.sdb.is_valid(access_token) refresh_token = tokens["refresh_token"] assert self.sdb.is_valid(refresh_token) refreshed_tokens = self.sdb.refresh_token(refresh_token) access_token2 = refreshed_tokens["access_token"] assert self.sdb.is_valid(access_token2) # replace refresh_token refreshed_tokens["refresh_token"] = access_token2 assert not self.sdb.is_valid(refresh_token) # mess with the time-line refreshed_tokens["token_expires_at"] = utc_time_sans_frac() - 86400 assert not self.sdb.is_valid(access_token2) # replace access_token refreshed_tokens["access_token"] = access_token assert not self.sdb.is_valid(access_token2) ae = AuthnEvent("another:user", "salt") sid = self.sdb.create_authz_session(ae, AREQ) grant = self.sdb[sid]["code"] self.sdb.update(grant, "token_expires_at", utc_time_sans_frac() - 86400) assert not self.sdb.is_valid(grant) def test_revoke_token(self): ae1 = AuthnEvent("sub", "salt") sid = self.sdb.create_authz_session(ae1, AREQ) grant = self.sdb[sid]["code"] tokens = self.sdb.upgrade_to_token(grant) access_token = tokens["access_token"] refresh_token = tokens["refresh_token"] assert self.sdb.is_valid(access_token) self.sdb.revoke_token(access_token) assert not self.sdb.is_valid(access_token) refreshed_tokens = self.sdb.refresh_token(refresh_token) access_token = refreshed_tokens["access_token"] assert self.sdb.is_valid(access_token) self.sdb.revoke_token(refresh_token) assert not self.sdb.is_valid(refresh_token) with pytest.raises(ExpiredToken): self.sdb.refresh_token(refresh_token) assert self.sdb.is_valid(access_token) ae2 = AuthnEvent("sub", "salt") sid = self.sdb.create_authz_session(ae2, AREQ) grant = self.sdb[sid]["code"] self.sdb.revoke_token(grant) assert not self.sdb.is_valid(grant) def test_sub_to_authn_event(self): ae = AuthnEvent("sub", "salt", time_stamp=time.time()) sid = self.sdb.create_authz_session(ae, AREQ) sub = self.sdb.do_sub(sid, "client_salt") # given the sub find out whether the authn event is still valid sids = self.sdb.get_sids_by_sub(sub) ae = self.sdb[sids[0]]["authn_event"] assert ae.valid() def test_do_sub_deterministic(self): ae = AuthnEvent("tester", "random_value") sid = self.sdb.create_authz_session(ae, AREQ) self.sdb.do_sub(sid, "other_random_value") info = self.sdb[sid] assert info[ "sub"] == '179670cdee6375c48e577317b2abd7d5cd26a5cdb1cfb7ef84af3d703c71d013' self.sdb.do_sub(sid, "other_random_value", sector_id='http://example.com', subject_type="pairwise") info2 = self.sdb[sid] assert info2[ "sub"] == 'aaa50d80f8780cf1c4beb39e8e126556292f5091b9e39596424fefa2b99d9c53' self.sdb.do_sub(sid, "another_random_value", sector_id='http://other.example.com', subject_type="pairwise") info2 = self.sdb[sid] assert info2[ "sub"] == '62fb630e29f0d41b88e049ac0ef49a9c3ac5418c029d6e4f5417df7e9443976b'
class MyFakeOICServer(Server): def __init__(self, name=""): Server.__init__(self) self.sdb = SessionDB(name) self.name = name self.client = {} self.registration_expires_in = 3600 self.host = "" self.webfinger = WebFinger() self.userinfo_signed_response_alg = "" def http_request(self, path, method="GET", **kwargs): part = urlparse(path) path = part[2] query = part[4] self.host = "%s://%s" % (part.scheme, part.netloc) response = Response response.status_code = 500 response.text = "" if path == ENDPOINT["authorization_endpoint"]: assert method == "GET" response = self.authorization_endpoint(query) elif path == ENDPOINT["token_endpoint"]: assert method == "POST" response = self.token_endpoint(kwargs["data"]) elif path == ENDPOINT["user_info_endpoint"]: assert method == "POST" response = self.userinfo_endpoint(kwargs["data"]) elif path == ENDPOINT["refresh_session_endpoint"]: assert method == "GET" response = self.refresh_session_endpoint(query) elif path == ENDPOINT["check_session_endpoint"]: assert method == "GET" response = self.check_session_endpoint(query) elif path == ENDPOINT["end_session_endpoint"]: assert method == "GET" response = self.end_session_endpoint(query) elif path == ENDPOINT["registration_endpoint"]: if method == "POST": response = self.registration_endpoint(kwargs["data"]) elif path == "/.well-known/webfinger": assert method == "GET" qdict = parse_qs(query) response.status_code = 200 response.text = self.webfinger.response(qdict["resource"][0], "%s/" % self.name) elif path == "/.well-known/openid-configuration": assert method == "GET" response = self.openid_conf() return response def authorization_endpoint(self, query): req = self.parse_authorization_request(query=query) aevent = AuthnEvent("user", "salt", authn_info="acr") sid = self.sdb.create_authz_session(aevent, areq=req) self.sdb.do_sub(sid, "client_salt") _info = self.sdb[sid] if "code" in req["response_type"]: if "token" in req["response_type"]: grant = _info["code"] if 'offline_access' in _info['scope']: _dict = self.sdb.upgrade_to_token(grant, issue_refresh=True) else: _dict = self.sdb.upgrade_to_token(grant) _dict["oauth_state"] = "authz", _dict = by_schema(AuthorizationResponse(), **_dict) resp = AuthorizationResponse(**_dict) # resp.code = grant else: _state = req["state"] resp = AuthorizationResponse(state=_state, code=_info["code"]) else: # "implicit" in req.response_type: grant = _info["code"] params = AccessTokenResponse.c_param.keys() _dict = dict([(k, v) for k, v in self.sdb.upgrade_to_token(grant).items() if k in params]) try: del _dict["refresh_token"] except KeyError: pass if "id_token" in req["response_type"]: _idt = self.make_id_token(_info, issuer=self.name, access_token=_dict["access_token"]) alg = "RS256" ckey = self.keyjar.get_signing_key(alg2keytype(alg), _info["client_id"]) _dict["id_token"] = _idt.to_jwt(key=ckey, algorithm=alg) resp = AccessTokenResponse(**_dict) location = resp.request(req["redirect_uri"]) response = Response() response.headers = {"location": location} response.status_code = 302 response.text = "" return response def token_endpoint(self, data): if "grant_type=refresh_token" in data: req = self.parse_refresh_token_request(body=data) _info = self.sdb.refresh_token(req["refresh_token"], req['client_id']) elif "grant_type=authorization_code" in data: req = self.parse_token_request(body=data) if 'offline_access' in self.sdb[req['code']]['scope']: _info = self.sdb.upgrade_to_token(req["code"], issue_refresh=True) else: _info = self.sdb.upgrade_to_token(req["code"]) else: response = TokenErrorResponse(error="unsupported_grant_type") return response, "" resp = AccessTokenResponse(**by_schema(AccessTokenResponse, **_info)) response = Response() response.headers = {"content-type": "application/json"} response.text = resp.to_json() return response def userinfo_endpoint(self, data): self.parse_user_info_request(data) _info = { "sub": "melgar", "name": "Melody Gardot", "nickname": "Mel", "email": "*****@*****.**", "verified": True, } resp = OpenIDSchema(**_info) response = Response() if self.userinfo_signed_response_alg: alg = self.userinfo_signed_response_alg response.headers = {"content-type": "application/jwt"} key = self.keyjar.get_signing_key(alg2keytype(alg), "", alg=alg) response.text = resp.to_jwt(key, alg) else: response.headers = {"content-type": "application/json"} response.text = resp.to_json() return response def registration_endpoint(self, data): try: req = self.parse_registration_request(data, "json") except ValueError: req = self.parse_registration_request(data) client_secret = rndstr() expires = utc_time_sans_frac() + self.registration_expires_in kwargs = {} if "client_id" not in req: client_id = rndstr(10) registration_access_token = rndstr(20) _client_info = req.to_dict() kwargs.update(_client_info) _client_info.update({ "client_secret": client_secret, "info": req.to_dict(), "expires": expires, "registration_access_token": registration_access_token, "registration_client_uri": "register_endpoint" }) self.client[client_id] = _client_info kwargs["registration_access_token"] = registration_access_token kwargs["registration_client_uri"] = "register_endpoint" try: del kwargs["operation"] except KeyError: pass else: client_id = req.client_id _cinfo = self.client[req.client_id] _cinfo["info"].update(req.to_dict()) _cinfo["client_secret"] = client_secret _cinfo["expires"] = expires resp = RegistrationResponse(client_id=client_id, client_secret=client_secret, client_secret_expires_at=expires, **kwargs) response = Response() response.headers = {"content-type": "application/json"} response.text = resp.to_json() return response def check_session_endpoint(self, query): try: idtoken = self.parse_check_session_request(query=query) except Exception: raise response = Response() response.text = idtoken.to_json() response.headers = {"content-type": "application/json"} return response def refresh_session_endpoint(self, query): self.parse_refresh_session_request(query=query) resp = RegistrationResponse(client_id="anonymous", client_secret="hemligt") response = Response() response.headers = {"content-type": "application/json"} response.text = resp.to_json() return response def end_session_endpoint(self, query): try: req = self.parse_end_session_request(query=query) except Exception: raise # redirect back resp = EndSessionResponse(state=req["state"]) url = resp.request(req["redirect_url"]) response = Response() response.headers = {"location": url} response.status_code = 302 # redirect response.text = "" return response @staticmethod def add_credentials(user, passwd): pass def openid_conf(self): endpoint = {} for point, path in ENDPOINT.items(): endpoint[point] = "%s%s" % (self.host, path) signing_algs = list(jws.SIGNER_ALGS.keys()) resp = ProviderConfigurationResponse( issuer=self.name, scopes_supported=["openid", "profile", "email", "address"], identifiers_supported=["public", "PPID"], flows_supported=[ "code", "token", "code token", "id_token", "code id_token", "token id_token" ], subject_types_supported=["pairwise", "public"], response_types_supported=[ "code", "token", "id_token", "code token", "code id_token", "token id_token", "code token id_token" ], jwks_uri="http://example.com/oidc/jwks", id_token_signing_alg_values_supported=signing_algs, grant_types_supported=["authorization_code", "implicit"], **endpoint) response = Response() response.headers = {"content-type": "application/json"} response.text = resp.to_json() return response