From b43c1adb3dee33bb452be780e7be1b3e78359537 Mon Sep 17 00:00:00 2001 From: Michael Schlenker Date: Thu, 29 Jun 2017 12:13:48 +0200 Subject: [PATCH 1/7] Refactor sdb to remove password, seed and secret from its API The seed parameter is unused. The password and secrets are directly passed to the DefaultToken Factory and unused otherwise (and badly named as that is not a password) --- src/oic/utils/sdb.py | 51 ++++++++++++++++++++++++++++---------------- 1 file changed, 33 insertions(+), 18 deletions(-) diff --git a/src/oic/utils/sdb.py b/src/oic/utils/sdb.py index 08b3bf00f..47313b87f 100644 --- a/src/oic/utils/sdb.py +++ b/src/oic/utils/sdb.py @@ -12,6 +12,7 @@ from cryptography.fernet import Fernet from oic import rndstr +from oic.exception import ImproperlyConfigured from oic.oic import AuthorizationRequest from oic.utils.time_util import time_sans_frac from oic.utils.time_util import utc_time_sans_frac @@ -339,33 +340,49 @@ def remove(self, token): self._db.pop(token) +def create_session_db(base_url, + secret, + password, + db=None, + token_expires_in=3600, + grant_expires_in=600, + refresh_token_expires_in=86400): + + code_factory = DefaultToken(secret, password, typ='A', + lifetime=grant_expires_in) + token_factory = DefaultToken(secret, password, typ='T', + lifetime=token_expires_in) + db = {} if db is None else db + + return SessionDB( + base_url, db, + refresh_db=None, + code_factory=code_factory, + token_factory=token_factory, + refresh_token_factory=None) + + class SessionDB(object): - def __init__(self, base_url, db=None, secret="Ab01FG65", - token_expires_in=3600, password="4-amino-1H-pyrimidine-2-one", - grant_expires_in=600, seed="", refresh_db=None, + def __init__(self, base_url, db, refresh_db=None, refresh_token_expires_in=86400, token_factory=None, code_factory=None, refresh_token_factory=None): + self.base_url = base_url - if db is None: - db = {} self._db = db + self.uid2sid = {} - self.token_factory = {} + self.token_factory = { + 'code': code_factory, + 'access_token': token_factory, + } self.token_factory_order = ['code', 'access_token'] - if code_factory: - self.token_factory['code'] = code_factory - else: - self.token_factory['code'] = DefaultToken(secret, password, typ='A', - lifetime=grant_expires_in) - if token_factory: - self.token_factory['access_token'] = token_factory - else: - self.token_factory['access_token'] = DefaultToken( - secret, password, typ='T', lifetime=token_expires_in) if refresh_token_factory: + if refresh_db: + raise ImproperlyConfigured( + "Only use one of refresh_db or refresh_token_factory") self._refresh_db = None self.token_factory['refresh_token'] = refresh_token_factory self.token_factory_order.append('refresh_token') @@ -376,8 +393,6 @@ def __init__(self, base_url, db=None, secret="Ab01FG65", self.access_token = self.token_factory['access_token'] self.token = self.access_token - self.uid2sid = {} - self.seed = seed or secret def _get_token_key(self, item, order=None): if order is None: From df78eda98ee7fc258649c79196080ab98158634c Mon Sep 17 00:00:00 2001 From: Michael Schlenker Date: Thu, 29 Jun 2017 12:17:58 +0200 Subject: [PATCH 2/7] Fix all tests for the new reduced SessionDB API Moved a lot of SessionDB Init into the conftest.py fixtures. --- tests/conftest.py | 36 +++++++++++++++++++++++++++++++---- tests/mitmsrv.py | 4 ++-- tests/test_claims_provider.py | 5 ++--- tests/test_oauth2_provider.py | 11 +++++------ tests/test_oic.py | 5 ++--- tests/test_oic_consumer.py | 26 +++++++++++-------------- tests/test_oic_provider.py | 13 ++++++------- tests/test_sdb.py | 5 ++--- tests/test_token.py | 3 +++ tests/test_x_client.py | 9 ++++----- tests/test_x_dynreg.py | 4 ++-- tests/test_x_provider.py | 3 +++ 12 files changed, 74 insertions(+), 50 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index dc1ca4f55..38bf6ee06 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -5,14 +5,42 @@ from oic.oic.provider import Provider from oic.utils.authn.client import verify_client from oic.utils.authz import AuthzHandling -from oic.utils.sdb import SessionDB +from oic.utils.sdb import create_session_db @pytest.fixture -def provider(): - issuer = "https://op.foo.com" +def session_db_factory(): + def fac(issuer): + return create_session_db(issuer, + secret='supersecret', password='badpassword') + return fac + + +@pytest.fixture +def session_db(session_db_factory): + return session_db_factory("https://op.example.com") + + +@pytest.fixture +def fake_oic_server(session_db_factory): + from tests.fakeoicsrv import MyFakeOICServer + def fac(name): + return MyFakeOICServer(name, session_db_factory=session_db_factory) + return fac + + +@pytest.fixture +def mitm_server(session_db_factory): + from tests.mitmsrv import MITMServer + def fac(name): + return MITMServer(name, session_db_factory=session_db_factory) + return fac + + +@pytest.fixture +def provider(session_db): + issuer = "https://op.example.com" client_db = {} - session_db = SessionDB(issuer), verification_function = verify_client authz_handler = AuthzHandling() symkey = None diff --git a/tests/mitmsrv.py b/tests/mitmsrv.py index 1e9f32dd6..159f63d38 100644 --- a/tests/mitmsrv.py +++ b/tests/mitmsrv.py @@ -51,9 +51,9 @@ def __getitem__(self, item): class MITMServer(Server): - def __init__(self, name=""): + def __init__(self, name="", session_db_factory=None): Server.__init__(self) - self.sdb = SessionDB(name) + self.sdb = session_db_factory(name) self.name = name self.client = {} self.registration_expires_in = 3600 diff --git a/tests/test_claims_provider.py b/tests/test_claims_provider.py index 698b730d3..91d58f543 100644 --- a/tests/test_claims_provider.py +++ b/tests/test_claims_provider.py @@ -18,7 +18,6 @@ from oic.utils.keyio import KeyBundle from oic.utils.keyio import KeyJar from oic.utils.keyio import keybundle_from_local_file -from oic.utils.sdb import SessionDB from oic.utils.userinfo import UserInfo __author__ = 'rohe0002' @@ -108,8 +107,8 @@ class TestClaimsServer(object): } @pytest.fixture(autouse=True) - def create_claims_server(self, keyjar): - self.srv = ClaimsServer("pyoicserv", SessionDB("https://example.com"), + def create_claims_server(self, keyjar, session_db): + self.srv = ClaimsServer("pyoicserv", session_db, TestClaimsServer.CDB, UserInfo(USERDB), verify_client, keyjar=keyjar, diff --git a/tests/test_oauth2_provider.py b/tests/test_oauth2_provider.py index 8e3066ffb..05891c69d 100644 --- a/tests/test_oauth2_provider.py +++ b/tests/test_oauth2_provider.py @@ -15,7 +15,6 @@ from oic.oauth2.message import AuthorizationResponse from oic.oauth2.message import TokenErrorResponse from oic.oauth2.provider import Provider -from oic.utils import sdb from oic.utils.authn.authn_context import AuthnBroker from oic.utils.authn.client import verify_client from oic.utils.authn.user import UserAuthnMethod @@ -92,19 +91,19 @@ def verify_outcome(msg, prefix, lista): class TestProvider(object): @pytest.fixture(autouse=True) - def create_provider(self): + def create_provider(self, session_db_factory): self.provider = Provider("pyoicserv", - sdb.SessionDB(ISSUER), CDB, + session_db_factory(ISSUER), CDB, AUTHN_BROKER, AUTHZ, verify_client, baseurl='https://example.com/as') - def test_init(self): - provider = Provider("pyoicserv", sdb.SessionDB(ISSUER), + def test_init(self, session_db_factory): + provider = Provider("pyoicserv", session_db_factory(ISSUER), CDB, AUTHN_BROKER, AUTHZ, verify_client) assert provider - provider = Provider("pyoicserv", sdb.SessionDB(ISSUER), + provider = Provider("pyoicserv", session_db_factory(ISSUER), CDB, AUTHN_BROKER, AUTHZ, verify_client, urlmap={"client1": ["https://example.com/authz"]}) diff --git a/tests/test_oic.py b/tests/test_oic.py index 773472216..e78980906 100644 --- a/tests/test_oic.py +++ b/tests/test_oic.py @@ -10,7 +10,6 @@ from jwkest.jws import alg2keytype from jwkest.jws import left_hash from jwkest.jwt import JWT -from tests.fakeoicsrv import MyFakeOICServer from oic.oauth2.exception import OtherError from oic.oic import DEF_SIGN_ALG @@ -72,7 +71,7 @@ def _eq(l1, l2): class TestClient(object): @pytest.fixture(autouse=True) - def create_client(self): + def create_client(self, fake_oic_server): self.redirect_uri = "http://example.com/redirect" self.client = Client(CLIENT_ID, client_authn_method=CLIENT_AUTHN_METHOD) self.client.redirect_uris = [self.redirect_uri] @@ -84,7 +83,7 @@ def create_client(self): self.client.keyjar[""] = KC_RSA self.client.behaviour = { "request_object_signing_alg": DEF_SIGN_ALG["openid_request_object"]} - self.mfos = MyFakeOICServer() + self.mfos = fake_oic_server("http://example.com") self.mfos.keyjar = KEYJ self.client.http_request = self.mfos.http_request diff --git a/tests/test_oic_consumer.py b/tests/test_oic_consumer.py index a76b16f60..18fff1fc7 100644 --- a/tests/test_oic_consumer.py +++ b/tests/test_oic_consumer.py @@ -7,8 +7,6 @@ import responses from jwkest import BadSignature from jwkest.jwk import SYMKey -from tests.fakeoicsrv import MyFakeOICServer -from tests.mitmsrv import MITMServer from oic.oauth2.message import MissingSigningKey from oic.oic import DEF_SIGN_ALG @@ -25,7 +23,6 @@ from oic.utils.keyio import KeyBundle from oic.utils.keyio import KeyJar from oic.utils.keyio import keybundle_from_local_file -from oic.utils.sdb import SessionDB from oic.utils.time_util import utc_time_sans_frac __author__ = 'rohe0002' @@ -89,7 +86,7 @@ def test_clean_response(): class TestOICConsumer(): @pytest.fixture(autouse=True) - def setup_consumer(self): + def setup_consumer(self, fake_oic_server, session_db_factory): client_id = "client_1" client_config = { "client_id": client_id, @@ -97,7 +94,7 @@ def setup_consumer(self): # 'config': {} } - self.consumer = Consumer(SessionDB(SERVER_INFO["issuer"]), + self.consumer = Consumer(session_db_factory(SERVER_INFO["issuer"]), CONFIG, client_config, SERVER_INFO) self.consumer.behaviour = { "request_object_signing_alg": DEF_SIGN_ALG["openid_request_object"]} @@ -111,7 +108,7 @@ def setup_consumer(self): self.consumer.client_secret = "hemlig" self.consumer.secret_type = "basic" - mfos = MyFakeOICServer("http://localhost:8088") + mfos = fake_oic_server("http://localhost:8088") mfos.keyjar = SRVKEYS self.consumer.http_request = mfos.http_request @@ -436,9 +433,9 @@ def real_test_discover(self): 'code token', 'code id_token', 'id_token token']) - def test_discover(self): + def test_discover(self, fake_oic_server): c = Consumer(None, None) - mfos = MyFakeOICServer("https://localhost:8088") + mfos = fake_oic_server("https://localhost:8088") mfos.keyjar = SRVKEYS c.http_request = mfos.http_request @@ -446,9 +443,9 @@ def test_discover(self): res = c.discover(principal) assert res == "https://localhost:8088/" - def test_provider_config(self): + def test_provider_config(self, fake_oic_server): c = Consumer(None, None) - mfos = MyFakeOICServer("https://example.com") + mfos = fake_oic_server("https://example.com") mfos.keyjar = SRVKEYS c.http_request = mfos.http_request @@ -477,15 +474,14 @@ def test_provider_config(self): assert info["end_session_endpoint"] == "https://example.com/end_session" - def test_client_register(self): + def test_client_register(self, fake_oic_server): c = Consumer(None, None) c.application_type = "web" c.application_name = "My super service" c.redirect_uris = ["https://example.com/authz"] c.contact = ["foo@example.com"] - - mfos = MyFakeOICServer("https://example.com") + mfos = fake_oic_server("https://example.com") mfos.keyjar = SRVKEYS c.http_request = mfos.http_request location = c.discover("foo@example.com") @@ -535,8 +531,8 @@ def test_faulty_id_token_in_access_token_response(self): with pytest.raises(BadSignature): c.parse_response(AccessTokenResponse, _json, sformat="json") - def test_faulty_idtoken_from_accesstoken_endpoint(self): - mfos = MITMServer("http://localhost:8088") + def test_faulty_idtoken_from_accesstoken_endpoint(self, mitm_server): + mfos = mitm_server("http://localhost:8088") mfos.keyjar = SRVKEYS self.consumer.http_request = mfos.http_request _state = "state0" diff --git a/tests/test_oic_provider.py b/tests/test_oic_provider.py index 2cc719b91..739b62c07 100644 --- a/tests/test_oic_provider.py +++ b/tests/test_oic_provider.py @@ -49,7 +49,6 @@ from oic.utils.keyio import ec_init from oic.utils.keyio import keybundle_from_local_file from oic.utils.sdb import AuthnEvent -from oic.utils.sdb import SessionDB from oic.utils.time_util import epoch_in_a_while from oic.utils.userinfo import UserInfo @@ -176,8 +175,8 @@ def authenticated_as(self, cookie=None, **kwargs): class TestProvider(object): @pytest.fixture(autouse=True) - def create_provider(self): - self.provider = Provider(SERVER_INFO["issuer"], SessionDB(SERVER_INFO["issuer"]), + def create_provider(self, session_db_factory): + self.provider = Provider(SERVER_INFO["issuer"], session_db_factory(SERVER_INFO["issuer"]), CDB, AUTHN_BROKER, USERINFO, AUTHZ, verify_client, SYMKEY, urlmap=URLMAP, @@ -767,9 +766,9 @@ def test_verify_redirect_uris_with_non_https_redirect_uri_implicit_flow( assert str(exc_info.value) == "None https redirect_uri not allowed" - def test_provider_key_setup(self, tmpdir): + def test_provider_key_setup(self, tmpdir, session_db_factory): path = tmpdir.strpath - provider = Provider("pyoicserv", SessionDB(SERVER_INFO["issuer"]), None, + provider = Provider("pyoicserv", session_db_factory(SERVER_INFO["issuer"]), None, None, None, None, None, None) provider.baseurl = "http://www.example.com" provider.key_setup(path, path, sig={"format": "jwk", "alg": "RSA"}) @@ -1060,8 +1059,8 @@ def test_endsession_endpoint_with_post_logout_redirect_uri(self): id_token["sub"]) # verify session has been removed self._assert_cookies_expired(resp.headers) - def test_session_state_in_auth_req_for_session_support(self): - provider = Provider(SERVER_INFO["issuer"], SessionDB(SERVER_INFO["issuer"]), CDB, + def test_session_state_in_auth_req_for_session_support(self, session_db_factory): + provider = Provider(SERVER_INFO["issuer"], session_db_factory(SERVER_INFO["issuer"]), CDB, AUTHN_BROKER, USERINFO, AUTHZ, verify_client, SYMKEY, urlmap=URLMAP, keyjar=KEYJAR) diff --git a/tests/test_sdb.py b/tests/test_sdb.py index 017facc48..d1c2abff1 100644 --- a/tests/test_sdb.py +++ b/tests/test_sdb.py @@ -14,7 +14,6 @@ from oic.utils.sdb import DefaultToken from oic.utils.sdb import DictRefreshDB from oic.utils.sdb import ExpiredToken -from oic.utils.sdb import SessionDB from oic.utils.sdb import WrongTokenType __author__ = 'rohe0002' @@ -99,8 +98,8 @@ def test_type_and_key(self): class TestSessionDB(object): @pytest.fixture(autouse=True) - def create_sdb(self): - self.sdb = SessionDB("https://example.com/") + def create_sdb(self, session_db_factory): + self.sdb = session_db_factory("https://example.com/") def test_create_authz_session(self): ae = AuthnEvent("uid", "salt") diff --git a/tests/test_token.py b/tests/test_token.py index 330f4bb32..110c60347 100644 --- a/tests/test_token.py +++ b/tests/test_token.py @@ -189,6 +189,9 @@ def create_sdb(self): 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', diff --git a/tests/test_x_client.py b/tests/test_x_client.py index 67e4184db..6963d8cab 100644 --- a/tests/test_x_client.py +++ b/tests/test_x_client.py @@ -2,7 +2,6 @@ from oic.extension.client import Client from oic.extension.provider import Provider from oic.extension.token import JWTToken -from oic.utils import sdb from oic.utils.authn.authn_context import AuthnBroker from oic.utils.authn.client import verify_client from oic.utils.authn.user import UserAuthnMethod @@ -52,28 +51,28 @@ def test_pkce_create(): assert _eq(list(args.keys()), ['code_challenge_method', 'code_challenge']) -def test_pkce_verify_256(): +def test_pkce_verify_256(session_db_factory): _cli = Client(config={'code_challenge': {'method': 'S256', 'length': 64}}) args, cv = _cli.add_code_challenge() authn_broker = AuthnBroker() authn_broker.add("UNDEFINED", DummyAuthn(None, "username")) _prov = Provider("as", - sdb.SessionDB(SERVER_INFO["issuer"]), CDB, + session_db_factory(SERVER_INFO["issuer"]), CDB, authn_broker, Implicit(), verify_client) assert _prov.verify_code_challenge(cv, args['code_challenge']) is True assert _prov.verify_code_challenge(cv, args['code_challenge'], 'S256') is True -def test_pkce_verify_512(): +def test_pkce_verify_512(session_db_factory): _cli = Client(config={'code_challenge': {'method': 'S512', 'length': 96}}) args, cv = _cli.add_code_challenge() authn_broker = AuthnBroker() authn_broker.add("UNDEFINED", DummyAuthn(None, "username")) _prov = Provider("as", - sdb.SessionDB(SERVER_INFO["issuer"]), CDB, + session_db_factory(SERVER_INFO["issuer"]), CDB, authn_broker, Implicit(), verify_client) assert _prov.verify_code_challenge(cv, args['code_challenge'], 'S512') is True diff --git a/tests/test_x_dynreg.py b/tests/test_x_dynreg.py index d25320e92..961182d0b 100644 --- a/tests/test_x_dynreg.py +++ b/tests/test_x_dynreg.py @@ -90,12 +90,12 @@ class TestProvider(object): } @pytest.fixture(autouse=True) - def create_provider(self): + def create_provider(self, session_db_factory): authn_broker = AuthnBroker() authn_broker.add("UNDEFINED", DummyAuthn(None, "username")) self.provider = Provider("pyoicserv", - sdb.SessionDB( + session_db_factory( TestProvider.SERVER_INFO["issuer"]), TestProvider.CDB, authn_broker, Implicit(), diff --git a/tests/test_x_provider.py b/tests/test_x_provider.py index f1a9cf299..162681e3d 100644 --- a/tests/test_x_provider.py +++ b/tests/test_x_provider.py @@ -141,6 +141,9 @@ def create_provider(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', From 2eeee80066c8a488a8b92a8b5faf6083f533d3bb Mon Sep 17 00:00:00 2001 From: Michael Schlenker Date: Thu, 29 Jun 2017 12:18:17 +0200 Subject: [PATCH 3/7] Fix tests --- tests/fakeoicsrv.py | 5 ++--- tests/test_token.py | 1 + tests/test_x_provider.py | 1 + 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/tests/fakeoicsrv.py b/tests/fakeoicsrv.py index 4ec5c5abb..0a839bdde 100644 --- a/tests/fakeoicsrv.py +++ b/tests/fakeoicsrv.py @@ -16,7 +16,6 @@ from oic.oic.message import RegistrationResponse from oic.oic.message import TokenErrorResponse from oic.utils.sdb import AuthnEvent -from oic.utils.sdb import SessionDB from oic.utils.time_util import utc_time_sans_frac from oic.utils.webfinger import WebFinger @@ -51,9 +50,9 @@ def __getitem__(self, item): class MyFakeOICServer(Server): - def __init__(self, name=""): + def __init__(self, name="", session_db_factory=None): Server.__init__(self) - self.sdb = SessionDB(name) + self.sdb = session_db_factory(name) self.name = name self.client = {} self.registration_expires_in = 3600 diff --git a/tests/test_token.py b/tests/test_token.py index 110c60347..07cd5b79d 100644 --- a/tests/test_token.py +++ b/tests/test_token.py @@ -10,6 +10,7 @@ from oic.utils.keyio import KeyJar from oic.utils.sdb import AccessCodeUsed from oic.utils.sdb import AuthnEvent +from oic.utils.sdb import DefaultToken from oic.utils.sdb import ExpiredToken from oic.utils.sdb import SessionDB diff --git a/tests/test_x_provider.py b/tests/test_x_provider.py index 162681e3d..87f704406 100644 --- a/tests/test_x_provider.py +++ b/tests/test_x_provider.py @@ -24,6 +24,7 @@ from oic.utils.authz import Implicit from oic.utils.keyio import KeyBundle from oic.utils.keyio import KeyJar +from oic.utils.sdb import DefaultToken from oic.utils.sdb import SessionDB from oic.utils.sdb import lv_pack from oic.utils.sdb import lv_unpack From c5d950b4d0a71934bfd5bf786d835fbfa5c30a46 Mon Sep 17 00:00:00 2001 From: Michael Schlenker Date: Thu, 29 Jun 2017 13:08:07 +0200 Subject: [PATCH 4/7] Fix Examples Change all examples to the shorter SessionDB API. --- oauth_example/as/as.py | 11 ++++++++--- oidc_example/op1/claims_provider.py | 7 +++++-- oidc_example/op1/oc_config.py.example | 4 ++++ oidc_example/op1/oc_server.py | 14 ++++++++++---- oidc_example/op2/server.py | 12 +++++++++--- oidc_example/op3/server.py | 11 +++++++++-- .../simple_op/src/provider/server/server.py | 6 ++++-- 7 files changed, 49 insertions(+), 16 deletions(-) diff --git a/oauth_example/as/as.py b/oauth_example/as/as.py index ccef22d98..79ab84c45 100755 --- a/oauth_example/as/as.py +++ b/oauth_example/as/as.py @@ -198,7 +198,8 @@ def application(self, environ, start_response): # This is where session information is stored # This serve is stateful. - from oic.utils.sdb import SessionDB + from oic import rndstr + from oic.utils.sdb import SessionDB, DefaultToken # Parse the command arguments parser = argparse.ArgumentParser() @@ -277,11 +278,15 @@ def application(self, environ, start_response): oas.jwks_uri = "{}/{}".format(oas.baseurl, jwks_file_name) # Initiate the SessionDB + _code = DefaultToken(rndstr(32), rndstr(32), typ='A', lifetime=600) _token = JWTToken('T', oas.keyjar, {'code': 3600, 'token': 900}, iss=config.issuer, sign_alg='RS256') _refresh_token = JWTToken('R', oas.keyjar, {'': 86400}, iss=config.issuer, sign_alg='RS256') - oas.sdb = SessionDB(config.SERVICE_URL, token_factory=_token, + oas.sdb = SessionDB(config.SERVICE_URL, + db={}, + code_factory=_code, + token_factory=_token, refresh_token_factory=_refresh_token) # set some parameters @@ -326,7 +331,7 @@ def application(self, environ, start_response): oas.keyjar.add_kb(iss, kb) _app = Application(oas) - + # Initiate the web server SRV = wsgiserver.CherryPyWSGIServer(('0.0.0.0', args.port), _app.application) diff --git a/oidc_example/op1/claims_provider.py b/oidc_example/op1/claims_provider.py index 44f79423d..5c2b9419b 100755 --- a/oidc_example/op1/claims_provider.py +++ b/oidc_example/op1/claims_provider.py @@ -226,7 +226,7 @@ def application(environ, start_response): from cherrypy.wsgiserver import ssl_builtin from oic.oic.claims_provider import ClaimsServer - from oic.utils.sdb import SessionDB + from oic.utils.sdb import create_session_db parser = argparse.ArgumentParser() parser.add_argument('-v', dest='verbose', action='store_true') @@ -241,7 +241,10 @@ def application(environ, start_response): # in memory session storage config = json.loads(open(args.config).read()) - OAS = ClaimsServer(config["issuer"], SessionDB(), cdb, userinfo, + sdb = create_session_db(config["issuer"], + config["SESSION_KEY"], + password="changethis") + OAS = ClaimsServer(config["issuer"], sdb, cdb, userinfo, verify_client) if "keys" in config: diff --git a/oidc_example/op1/oc_config.py.example b/oidc_example/op1/oc_config.py.example index f43023fcc..2160b75e4 100644 --- a/oidc_example/op1/oc_config.py.example +++ b/oidc_example/op1/oc_config.py.example @@ -22,6 +22,10 @@ AUTHORIZATION = { COOKIENAME= 'pyoic' COOKIETTL = 4*60 # 4 hours SYM_KEY = "SoLittleTime,Got" + +# Encryption Key and HMAC key to +SESSION_KEY = "a"*64 + SERVER_CERT = "certs/server.crt" SERVER_KEY = "certs/server.key" #CERT_CHAIN="certs/chain.pem" diff --git a/oidc_example/op1/oc_server.py b/oidc_example/op1/oc_server.py index 49a30ac08..ac0df9931 100755 --- a/oidc_example/op1/oc_server.py +++ b/oidc_example/op1/oc_server.py @@ -481,7 +481,8 @@ def new_trace_log(self, key): #from cherrypy.wsgiserver import ssl_builtin from cherrypy.wsgiserver import ssl_pyopenssl - from oic.utils.sdb import SessionDB + from oic import rndstr + from oic.utils.sdb import create_session_db parser = argparse.ArgumentParser() parser.add_argument('-v', dest='verbose', action='store_true') @@ -532,18 +533,23 @@ def new_trace_log(self, key): else: kwargs = {"verify_ssl": True} + # In-Memory SessionDB issuing DefaultTokens + sdb = create_session_db(config.baseurl, + secret=rndstr(32), + password=rndstr(32)) + if args.test: URLS.append((r'tracelog', trace_log)) - OAS = TestProvider(config.issuer, SessionDB(config.baseurl), cdb, ac, + OAS = TestProvider(config.issuer, sdb, cdb, ac, None, authz, config.SYM_KEY) elif args.XpressConnect: from XpressConnect import XpressConnectProvider - OAS = XpressConnectProvider(config.issuer, SessionDB(config.baseurl), + OAS = XpressConnectProvider(config.issuer, sdb, cdb, ac, None, authz, verify_client, config.SYM_KEY) else: - OAS = Provider(config.issuer, SessionDB(config.baseurl), cdb, ac, None, + OAS = Provider(config.issuer, sdb, cdb, ac, None, authz, verify_client, config.SYM_KEY, **kwargs) diff --git a/oidc_example/op2/server.py b/oidc_example/op2/server.py index 67a01c7ac..00c06d998 100755 --- a/oidc_example/op2/server.py +++ b/oidc_example/op2/server.py @@ -233,7 +233,7 @@ def token(self, environ, start_response): # noinspection PyUnusedLocal def authorization(self, environ, start_response): - return wsgi_wrapper(environ, start_response, + return wsgi_wrapper(environ, start_response, self.oas.authorization_endpoint, logger=logger) # noinspection PyUnusedLocal @@ -367,7 +367,8 @@ def application(self, environ, start_response): from cherrypy import wsgiserver from cherrypy.wsgiserver.ssl_builtin import BuiltinSSLAdapter - from oic.utils.sdb import SessionDB + from oic import rndstr + from oic.utils.sdb import create_session_db parser = argparse.ArgumentParser() parser.add_argument('-v', dest='verbose', action='store_true') @@ -545,7 +546,12 @@ def application(self, environ, start_response): else: pass - OAS = Provider(_issuer, SessionDB(_issuer), cdb, ac, None, + # In-Memory non persistent SessionDB + sdb = create_session_db(_issuer, + secret=rndstr(32), + password=rndstr(32)) + + OAS = Provider(_issuer, sdb, cdb, ac, None, authz, verify_client, config.SYM_KEY, **kwargs) OAS.baseurl = _issuer diff --git a/oidc_example/op3/server.py b/oidc_example/op3/server.py index 6828d6227..b1d4ab8ad 100755 --- a/oidc_example/op3/server.py +++ b/oidc_example/op3/server.py @@ -12,6 +12,8 @@ import importlib from mako.lookup import TemplateLookup +from oic import rndstr + from oic.oic.provider import AuthorizationEndpoint from oic.oic.provider import EndSessionEndpoint from oic.oic.provider import Provider @@ -35,7 +37,7 @@ from cherrypy import wsgiserver from cherrypy.wsgiserver.ssl_builtin import BuiltinSSLAdapter -from oic.utils.sdb import SessionDB +from oic.utils.sdb import create_session_db @@ -368,9 +370,14 @@ def application(self, environ, start_response): authz = AuthzHandling() clientDB = shelve_wrapper.open(config.CLIENTDB) + # In-Memory non-persistent SessionDB issuing DefaultTokens + sessionDB = create_session_db(config.ISSUER, + secret=rndstr(32), + password=rndstr(32)) + provider = Provider( name=config.ISSUER, # name - sdb=SessionDB(config.ISSUER), # session database. + sdb=sessionDB, # session database. cdb=clientDB, # client database authn_broker=authnBroker, # authn broker userinfo=None, # user information diff --git a/oidc_example/simple_op/src/provider/server/server.py b/oidc_example/simple_op/src/provider/server/server.py index d0d080a51..e0b87f667 100644 --- a/oidc_example/simple_op/src/provider/server/server.py +++ b/oidc_example/simple_op/src/provider/server/server.py @@ -33,7 +33,7 @@ from oic.utils.http_util import get_or_post from oic.utils.http_util import get_post from oic.utils.keyio import keyjar_init -from oic.utils.sdb import SessionDB +from oic.utils.sdb import create_session_db from oic.utils.userinfo import UserInfo from oic.utils.webfinger import OIC_ISSUER from oic.utils.webfinger import WebFinger @@ -199,7 +199,9 @@ def main(): userinfo = UserInfo(i) client_db = {} - provider = Provider(issuer, SessionDB(issuer), client_db, authn_broker, + session_db = create_session_db(issuer, + secret=rndstr(32), password=rndstr(32)) + provider = Provider(issuer, session_db, client_db, authn_broker, userinfo, AuthzHandling(), verify_client, None) provider.baseurl = issuer provider.symkey = rndstr(16) From 7953e65d7d146b76b7cbccf6bcccc457f52b2d5b Mon Sep 17 00:00:00 2001 From: Michael Schlenker Date: Thu, 29 Jun 2017 13:27:51 +0200 Subject: [PATCH 5/7] Add docstring for create_session_db --- src/oic/utils/sdb.py | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/src/oic/utils/sdb.py b/src/oic/utils/sdb.py index 47313b87f..a9363d2f5 100644 --- a/src/oic/utils/sdb.py +++ b/src/oic/utils/sdb.py @@ -340,13 +340,25 @@ def remove(self, token): self._db.pop(token) -def create_session_db(base_url, - secret, - password, - db=None, - token_expires_in=3600, - grant_expires_in=600, +def create_session_db(base_url, secret, password, db=None, + token_expires_in=3600, grant_expires_in=600, refresh_token_expires_in=86400): + """ + Convenience wrapper for SessionDB construction + + Using this you can create a very basic non persistant + session database that issues opaque DefaultTokens. + + :param base_url: Same as base_url parameter of `SessionDB`. + :param secret: Secret to pass to `DefaultToken` class. + :param password: Secret key to pass to `DefaultToken` class. + :param db: Storage for the session data, usually a dict. + :param token_expires_in: Expiry time for access tokens in seconds. + :param grant_expires_in: Expiry time for access codes in seconds. + :param refresh_token_expires_in: Expiry time for refresh tokens. + + :return: A constructed `SessionDB` object. + """ code_factory = DefaultToken(secret, password, typ='A', lifetime=grant_expires_in) @@ -359,7 +371,9 @@ def create_session_db(base_url, refresh_db=None, code_factory=code_factory, token_factory=token_factory, - refresh_token_factory=None) + refresh_token_expires_in=refresh_token_expires_in, + refresh_token_factory=None, + ) class SessionDB(object): @@ -370,6 +384,8 @@ def __init__(self, base_url, db, refresh_db=None, self.base_url = base_url self._db = db + + # TODO: uid2sid should have a persistence option too. self.uid2sid = {} self.token_factory = { @@ -379,6 +395,9 @@ def __init__(self, base_url, db, refresh_db=None, self.token_factory_order = ['code', 'access_token'] + # TODO: This should simply be a factory like all the others too, + # even for the default case. + if refresh_token_factory: if refresh_db: raise ImproperlyConfigured( From 1d52bb5de5ee11bb312ef85c7216f283cf6a4fbd Mon Sep 17 00:00:00 2001 From: Michael Schlenker Date: Thu, 29 Jun 2017 13:31:48 +0200 Subject: [PATCH 6/7] Changelog --- CHANGELOG.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c14f6864..46d7a2e10 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,11 +13,11 @@ The format is based on the [KeepAChangeLog] project. - [#325]: `oic.oic.claims_match` implementation refactored. - [#368]: `oic.oauth2.Client.construct_AccessTokenRequest()` as well as `oic.oic.Client` are now able to perform proper Resource Owner Password Credentials Grant - [#374]: Made the to_jwe/from_jwe methods of Message accept list of keys value of parameter keys. +- [#387]: Refactored the `oic.utils.sdb.SessionDB` constructor API. - [#380]: Made cookie_path and cookie_domain configurable via Provider like the cookie_name. - [#386]: An exception will now be thrown if a sub claim received from the userinfo endpoint is not the same as a sub claim previously received in an ID Token. - [#392]: Made sid creation simpler and faster - ### Fixed - [#313]: Catch exception correctly - [#319]: Fix sanitize on strings starting with "B" or "U" @@ -30,9 +30,10 @@ The format is based on the [KeepAChangeLog] project. ### Security - [#349]: Changed crypto algorithm used by `oic.utils.sdb.Crypt` for token encryption to Fernet. Old stored tokens are incompatible. -- [#363]: Fixed IV reuse for CookieDealer class. Replaced the encrypt-then-mac construction with a proper AEAD (AES-SIV). +- [#363]: Fixed IV reuse for CookieDealer class. Replaced the encrypt-then-mac construction with a proper AEAD (AES-SIV). [#313]: https://github.com/OpenIDC/pyoidc/issues/313 +[#387]: https://github.com/OpenIDC/pyoidc/pull/387 [#318]: https://github.com/OpenIDC/pyoidc/pull/318 [#319]: https://github.com/OpenIDC/pyoidc/pull/319 [#324]: https://github.com/OpenIDC/pyoidc/pull/324 From 9cf4dc469cc780d67e316685c34a411d29078234 Mon Sep 17 00:00:00 2001 From: Michael Schlenker Date: Thu, 29 Jun 2017 14:07:04 +0200 Subject: [PATCH 7/7] pylama fixes --- tests/conftest.py | 2 ++ tests/mitmsrv.py | 1 - tests/test_x_dynreg.py | 1 - 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 38bf6ee06..c2861b055 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -24,6 +24,7 @@ def session_db(session_db_factory): @pytest.fixture def fake_oic_server(session_db_factory): from tests.fakeoicsrv import MyFakeOICServer + def fac(name): return MyFakeOICServer(name, session_db_factory=session_db_factory) return fac @@ -32,6 +33,7 @@ def fac(name): @pytest.fixture def mitm_server(session_db_factory): from tests.mitmsrv import MITMServer + def fac(name): return MITMServer(name, session_db_factory=session_db_factory) return fac diff --git a/tests/mitmsrv.py b/tests/mitmsrv.py index 159f63d38..6fcfecb0f 100644 --- a/tests/mitmsrv.py +++ b/tests/mitmsrv.py @@ -16,7 +16,6 @@ from oic.oic.message import RegistrationResponse from oic.oic.message import TokenErrorResponse from oic.utils.sdb import AuthnEvent -from oic.utils.sdb import SessionDB from oic.utils.time_util import utc_time_sans_frac from oic.utils.webfinger import WebFinger diff --git a/tests/test_x_dynreg.py b/tests/test_x_dynreg.py index 961182d0b..c1b5803a1 100644 --- a/tests/test_x_dynreg.py +++ b/tests/test_x_dynreg.py @@ -9,7 +9,6 @@ from oic.extension.message import make_software_statement from oic.extension.message import unpack_software_statement from oic.extension.provider import Provider -from oic.utils import sdb from oic.utils.authn.authn_context import AuthnBroker from oic.utils.authn.client import BearerHeader from oic.utils.authn.client import ClientSecretBasic