Skip to content
This repository was archived by the owner on Jun 23, 2023. It is now read-only.

Commit 41d7f5e

Browse files
authored
Merge pull request #109 from IdentityPython/develop
Version 2.1.0
2 parents b3605a0 + 07ad50d commit 41d7f5e

17 files changed

+586
-114
lines changed

docs/source/contents/conf.rst

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -156,9 +156,35 @@ An example::
156156
backchannel_logout_session_supported: True
157157
check_session_iframe: https://127.0.0.1:5000/check_session_iframe
158158

159-
-------------
159+
---------
160+
client_db
161+
---------
162+
163+
If you're running an OP with static client registration you want to keep the
164+
registered clients in a database separate from the session database since
165+
it will change independent of the OP process. In this case you need this.
166+
If you are on the other hand only allowing dynamic client registration then
167+
keeping registered clients in the session database makes total sense.
168+
169+
The class you reference in the specification MUST be a subclass of
170+
oidcmsg.storage.DictType and have some of the methods a dictionary has.
171+
172+
Note also that this class MUST support the dump and load methods as defined
173+
in :py:class:`oidcmsg.impexp.ImpExp`.
174+
175+
An example::
176+
177+
client_db: {
178+
"class": 'oidcmsg.abfile.AbstractFileSystem',
179+
"kwargs": {
180+
'fdir': full_path("afs"),
181+
'value_conv': 'oidcmsg.util.JSON'
182+
}
183+
}
184+
185+
--------------
160186
cookie_handler
161-
-------------
187+
--------------
162188

163189
An example::
164190

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ build-backend = "setuptools.build_meta"
77

88
[metadata]
99
name = "oidcop"
10-
version = "2.0.0"
10+
version = "2.1.0"
1111
author = "Roland Hedberg"
1212
author_email = "[email protected]"
1313
description = "Python implementation of an OAuth2 AS and an OIDC Provider"

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ def run_tests(self):
7272
"Programming Language :: Python :: 3.9",
7373
"Topic :: Software Development :: Libraries :: Python Modules"],
7474
install_requires=[
75-
"oidcmsg==1.3.3-1",
75+
"oidcmsg==1.4.0",
7676
"cryptojwt==1.5.2",
7777
"pyyaml",
7878
"jinja2>=2.11.3",

src/oidcop/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import secrets
22

3-
__version__ = "2.0.1"
3+
__version__ = "2.1.0"
44

55
DEF_SIGN_ALG = {
66
"id_token": "RS256",

src/oidcop/configure.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,7 @@ class EntityConfiguration(Base):
192192
"base_url": "",
193193
"capabilities": None,
194194
"claims_interface": None,
195+
"client_db": None,
195196
"cookie_handler": None,
196197
"endpoint": {},
197198
"httpc_params": {},

src/oidcop/endpoint_context.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -93,7 +93,7 @@ class EndpointContext(OidcContext):
9393
"args": {},
9494
# "authn_broker": AuthnBroker,
9595
# "authz": AuthzHandling,
96-
"cdb": {},
96+
"cdb": "DICT_TYPE",
9797
"conf": {},
9898
# "cookie_handler": None,
9999
"cwd": "",
@@ -129,8 +129,15 @@ def __init__(
129129
OidcContext.__init__(self, conf, keyjar, entity_id=conf.get("issuer", ""))
130130
self.conf = conf
131131

132+
_client_db = conf.get("client_db")
133+
if _client_db:
134+
logger.debug(f"Loading client db using: {_client_db}")
135+
self.cdb = importer(_client_db["class"])(**_client_db["kwargs"])
136+
else:
137+
logger.debug("No special client db, will use memory based dictionary")
138+
self.cdb = {}
139+
132140
# For my Dev environment
133-
self.cdb = {}
134141
self.jti_db = {}
135142
self.registration_access_token = {}
136143
# self.session_db = {}

src/oidcop/oidc/userinfo.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
from oidcmsg import oidc
1111
from oidcmsg.message import Message
1212
from oidcmsg.oauth2 import ResponseMessage
13+
from oidcop.session.claims import claims_match
1314

1415
from oidcop.endpoint import Endpoint
1516
from oidcop.token.exception import UnknownToken
@@ -140,6 +141,8 @@ def process_request(self, request=None, **kwargs):
140141
user_id=_session_info["user_id"], claims_restriction=_claims
141142
)
142143
info["sub"] = _grant.sub
144+
if _grant.add_acr_value("userinfo"):
145+
info["acr"] = _grant.authentication_event["authn_info"]
143146
else:
144147
info = {
145148
"error": "invalid_request",

src/oidcop/session/grant.py

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99

1010
from oidcop.authn_event import AuthnEvent
1111
from oidcop.session import MintingNotAllowed
12+
from oidcop.session.claims import claims_match
1213
from oidcop.session.token import AccessToken
1314
from oidcop.session.token import AuthorizationCode
1415
from oidcop.session.token import IDToken
@@ -180,6 +181,14 @@ def find_scope(self, based_on):
180181

181182
return self.scope
182183

184+
def add_acr_value(self, claims_release_point):
185+
_release = self.claims.get(claims_release_point)
186+
if _release:
187+
_acr_request = _release.get("acr")
188+
_used_acr = self.authentication_event.get("authn_info")
189+
return claims_match(_used_acr, _acr_request)
190+
return False
191+
183192
def payload_arguments(
184193
self,
185194
session_id: str,
@@ -221,6 +230,10 @@ def payload_arguments(
221230
user_info = endpoint_context.claims_interface.get_user_claims(user_id, _claims_restriction)
222231
payload.update(user_info)
223232

233+
# Should I add the acr value
234+
if self.add_acr_value(claims_release_point):
235+
payload["acr"] = self.authentication_event["authn_info"]
236+
224237
return payload
225238

226239
def mint_token(

src/oidcop/session/manager.py

Lines changed: 62 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ def __init__(self, salt: Optional[str] = "", filename: Optional[str] = ""):
3838
if os.path.isfile(filename):
3939
self.salt = open(filename).read()
4040
elif not os.path.isfile(filename) and os.path.exists(
41-
filename
41+
filename
4242
): # Not a file, Something else
4343
raise ConfigurationError("Salt filename points to something that is not a file")
4444
else:
@@ -73,8 +73,10 @@ class SessionManager(Database):
7373
init_args = ["handler"]
7474

7575
def __init__(
76-
self, handler: TokenHandler, conf: Optional[dict] = None, sub_func: Optional[dict] = None,
76+
self, handler: TokenHandler, conf: Optional[dict] = None,
77+
sub_func: Optional[dict] = None,
7778
):
79+
super(SessionManager, self).__init__()
7880
self.conf = conf or {}
7981

8082
# these won't change runtime
@@ -125,9 +127,9 @@ def __setattr__(self, key, value):
125127

126128
def _init_db(self):
127129
Database.__init__(
128-
self,
129-
key=self.load_key(),
130-
salt=self.load_salt()
130+
self,
131+
key=self.load_key(),
132+
salt=self.load_salt()
131133
)
132134

133135
def get_user_info(self, uid: str) -> UserSessionInfo:
@@ -153,14 +155,14 @@ def find_token(self, session_id: str, token_value: str) -> Optional[SessionToken
153155
return None # pragma: no cover
154156

155157
def create_grant(
156-
self,
157-
authn_event: AuthnEvent,
158-
auth_req: AuthorizationRequest,
159-
user_id: str,
160-
client_id: Optional[str] = "",
161-
sub_type: Optional[str] = "public",
162-
token_usage_rules: Optional[dict] = None,
163-
scopes: Optional[list] = None,
158+
self,
159+
authn_event: AuthnEvent,
160+
auth_req: AuthorizationRequest,
161+
user_id: str,
162+
client_id: Optional[str] = "",
163+
sub_type: Optional[str] = "public",
164+
token_usage_rules: Optional[dict] = None,
165+
scopes: Optional[list] = None,
164166
) -> str:
165167
"""
166168
@@ -175,29 +177,31 @@ def create_grant(
175177
"""
176178
sector_identifier = auth_req.get("sector_identifier_uri", "")
177179

180+
_claims = auth_req.get("claims", {})
181+
178182
grant = Grant(
179183
authorization_request=auth_req,
180184
authentication_event=authn_event,
181-
sub=self.sub_func[sub_type](
182-
user_id, salt=self.salt, sector_identifier=sector_identifier
183-
),
185+
sub=self.sub_func[sub_type](user_id, salt=self.salt,
186+
sector_identifier=sector_identifier),
184187
usage_rules=token_usage_rules,
185188
scope=scopes,
189+
claims=_claims
186190
)
187191

188192
self.set([user_id, client_id, grant.id], grant)
189193

190194
return self.encrypted_session_id(user_id, client_id, grant.id)
191195

192196
def create_session(
193-
self,
194-
authn_event: AuthnEvent,
195-
auth_req: AuthorizationRequest,
196-
user_id: str,
197-
client_id: Optional[str] = "",
198-
sub_type: Optional[str] = "public",
199-
token_usage_rules: Optional[dict] = None,
200-
scopes: Optional[list] = None,
197+
self,
198+
authn_event: AuthnEvent,
199+
auth_req: AuthorizationRequest,
200+
user_id: str,
201+
client_id: Optional[str] = "",
202+
sub_type: Optional[str] = "public",
203+
token_usage_rules: Optional[dict] = None,
204+
scopes: Optional[list] = None,
201205
) -> str:
202206
"""
203207
Create part of a user session. The parts added are user- and client
@@ -309,10 +313,10 @@ def revoke_token(self, session_id: str, token_value: str, recursive: bool = Fals
309313
self._revoke_dependent(grant, token)
310314

311315
def get_authentication_events(
312-
self,
313-
session_id: Optional[str] = "",
314-
user_id: Optional[str] = "",
315-
client_id: Optional[str] = "",
316+
self,
317+
session_id: Optional[str] = "",
318+
user_id: Optional[str] = "",
319+
client_id: Optional[str] = "",
316320
) -> List[AuthnEvent]:
317321
"""
318322
Return the authentication events that exists for a user/client combination.
@@ -371,10 +375,10 @@ def revoke_grant(self, session_id: str):
371375
self.set(_path, _info)
372376

373377
def grants(
374-
self,
375-
session_id: Optional[str] = "",
376-
user_id: Optional[str] = "",
377-
client_id: Optional[str] = "",
378+
self,
379+
session_id: Optional[str] = "",
380+
user_id: Optional[str] = "",
381+
client_id: Optional[str] = "",
378382
) -> List[Grant]:
379383
"""
380384
Find all grant connected to a user session
@@ -395,13 +399,13 @@ def grants(
395399
return [self.get([user_id, client_id, gid]) for gid in _csi.subordinate]
396400

397401
def get_session_info(
398-
self,
399-
session_id: str,
400-
user_session_info: bool = False,
401-
client_session_info: bool = False,
402-
grant: bool = False,
403-
authentication_event: bool = False,
404-
authorization_request: bool = False,
402+
self,
403+
session_id: str,
404+
user_session_info: bool = False,
405+
client_session_info: bool = False,
406+
grant: bool = False,
407+
authentication_event: bool = False,
408+
authorization_request: bool = False,
405409
) -> dict:
406410
"""
407411
Returns information connected to a session.
@@ -448,14 +452,21 @@ def get_session_info(
448452

449453
return res
450454

455+
def _compatible_sid(self, sid):
456+
# To be backward compatible is this an old time sid
457+
p = self.unpack_session_key(sid)
458+
if len(p) == 3:
459+
sid = self.encrypted_session_id(*p)
460+
return sid
461+
451462
def get_session_info_by_token(
452-
self,
453-
token_value: str,
454-
user_session_info: bool = False,
455-
client_session_info: bool = False,
456-
grant: bool = False,
457-
authentication_event: bool = False,
458-
authorization_request: bool = False,
463+
self,
464+
token_value: str,
465+
user_session_info: bool = False,
466+
client_session_info: bool = False,
467+
grant: bool = False,
468+
authentication_event: bool = False,
469+
authorization_request: bool = False,
459470
) -> dict:
460471
_token_info = self.token_handler.info(token_value)
461472
sid = _token_info.get("sid")
@@ -464,6 +475,9 @@ def get_session_info_by_token(
464475
if not sid:
465476
raise WrongTokenClass
466477

478+
# To be backward compatible is this an old time sid
479+
sid = self._compatible_sid(sid)
480+
467481
return self.get_session_info(
468482
sid,
469483
user_session_info=user_session_info,
@@ -475,7 +489,8 @@ def get_session_info_by_token(
475489

476490
def get_session_id_by_token(self, token_value: str) -> str:
477491
_token_info = self.token_handler.info(token_value)
478-
return _token_info["sid"]
492+
sid = _token_info.get("sid")
493+
return self._compatible_sid(sid)
479494

480495
def add_grant(self, user_id: str, client_id: str, **kwargs) -> Grant:
481496
"""

src/oidcop/token/__init__.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,13 @@
1515

1616
logger = logging.getLogger(__name__)
1717

18+
ALT_TOKEN_NAME = {
19+
"authorization_code": "A",
20+
"access_token": "T",
21+
"refresh_token": "R",
22+
"id_token": "I"
23+
}
24+
1825

1926
def is_expired(exp, when=0):
2027
if exp < 0:
@@ -28,6 +35,11 @@ def is_expired(exp, when=0):
2835
class Token(object):
2936
def __init__(self, token_class, lifetime=300, **kwargs):
3037
self.token_class = token_class
38+
try:
39+
self.alt_token_name = ALT_TOKEN_NAME[token_class]
40+
except KeyError:
41+
self.alt_token_name = ""
42+
3143
self.lifetime = lifetime
3244
self.kwargs = kwargs
3345

@@ -70,7 +82,8 @@ def __init__(self, password, token_class="", token_type="Bearer", **kwargs):
7082
self.crypt = Crypt(password)
7183
self.token_type = token_type
7284

73-
def __call__(self, session_id: Optional[str] = "", token_class: Optional[str] = "", **payload) -> str:
85+
def __call__(self, session_id: Optional[str] = "", token_class: Optional[str] = "",
86+
**payload) -> str:
7487
"""
7588
Return a token.
7689
@@ -112,9 +125,10 @@ def info(self, token: str) -> dict:
112125
:return: dictionary with info about the token
113126
"""
114127
_res = dict(zip(["_id", "token_class", "sid", "exp"], self.split_token(token)))
115-
if _res["token_class"] != self.token_class:
128+
if _res["token_class"] not in [self.token_class, self.alt_token_name]:
116129
raise WrongTokenClass(_res["token_class"])
117130
else:
131+
_res["token_class"] = self.token_class
118132
_res["handler"] = self
119133
return _res
120134

0 commit comments

Comments
 (0)