Skip to content

Commit 2014f2f

Browse files
Merge pull request #24 from c00kiemon5ter/feature-multiple-scopes
Support for multiple scopes
2 parents f05a1db + ac5090e commit 2014f2f

File tree

5 files changed

+26
-21
lines changed

5 files changed

+26
-21
lines changed

setup.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
author_email='satosa-dev@lists.sunet.se',
1212
description='OpenID Connect Provider (OP) library in Python.',
1313
install_requires=[
14-
'oic<0.12',
14+
'oic>0.13.1',
1515
'pymongo'
1616
]
1717
)

src/pyop/provider.py

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -126,8 +126,9 @@ def parse_authentication_request(self, request_body, http_headers=None):
126126

127127
def authorize(self, authentication_request, # type: oic.oic.message.AuthorizationRequest
128128
user_id, # type: str
129-
extra_id_token_claims=None
129+
extra_id_token_claims=None,
130130
# type: Optional[Union[Mapping[str, Union[str, List[str]]], Callable[[str, str], Mapping[str, Union[str, List[str]]]]]
131+
extra_scopes=None,
131132
):
132133
# type: (...) -> oic.oic.message.AuthorizationResponse
133134
"""
@@ -166,7 +167,11 @@ def authorize(self, authentication_request, # type: oic.oic.message.Authorizati
166167
if len(authentication_request['response_type']) == 1:
167168
# only id token is issued -> no way of doing userinfo request, so include all claims in ID Token,
168169
# even those requested by the scope parameter
169-
requested_claims.update(scope2claims(authentication_request['scope']))
170+
requested_claims.update(
171+
scope2claims(
172+
authentication_request['scope'], extra_scope_dict=extra_scopes
173+
)
174+
)
170175

171176
user_claims = self.userinfo.get_claims_for(user_id, requested_claims)
172177
response['id_token'] = self._create_signed_id_token(authentication_request['client_id'], sub,
@@ -340,7 +345,7 @@ def _do_code_exchange(self, request, # type: Dict[str, str]
340345
raise InvalidTokenRequest(str(e), token_request) from e
341346

342347
authentication_request = self.authz_state.get_authorization_request_for_code(token_request['code'])
343-
348+
344349
if token_request['client_id'] != authentication_request['client_id']:
345350
logger.info('Authorization code \'%s\' belonging to \'%s\' was used by \'%s\'',
346351
token_request['code'], authentication_request['client_id'], token_request['client_id'])
@@ -415,7 +420,7 @@ def _verify_client_authentication(self, request_body, http_headers=None):
415420
token_request['client_id'] = verify_client_authentication(self.clients, token_request, http_headers.get('Authorization'))
416421
return token_request
417422

418-
def handle_userinfo_request(self, request=None, http_headers=None):
423+
def handle_userinfo_request(self, request=None, http_headers=None, extra_scopes=None):
419424
# type: (Optional[str], Optional[Mapping[str, str]]) -> oic.oic.message.OpenIDSchema
420425
"""
421426
Handles a userinfo request.
@@ -433,7 +438,7 @@ def handle_userinfo_request(self, request=None, http_headers=None):
433438
scope = introspection['scope']
434439
user_id = self.authz_state.get_user_id_for_subject_identifier(introspection['sub'])
435440

436-
requested_claims = scope2claims(scope.split())
441+
requested_claims = scope2claims(scope.split(), extra_scope_dict=extra_scopes)
437442
authentication_request = self.authz_state.get_authorization_request_for_access_token(bearer_token)
438443
requested_claims.update(self._get_requested_claims_in(authentication_request, 'userinfo'))
439444
user_claims = self.userinfo.get_claims_for(user_id, requested_claims)

tests/pyop/test_authz_state.py

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -37,18 +37,18 @@ def authorization_state_factory(self):
3737
def authorization_state(self, authorization_state_factory):
3838
return authorization_state_factory(refresh_token_lifetime=3600)
3939

40-
def assert_access_token(self, access_token, access_token_db, iat):
40+
def assert_access_token(self, authorization_request, access_token, access_token_db, iat):
4141
assert isinstance(access_token, AccessToken)
4242
assert access_token.expires_in == self.TEST_TOKEN_LIFETIME
4343
assert access_token.value
4444
assert access_token.BEARER_TOKEN_TYPE == 'Bearer'
4545

4646
assert access_token.value in access_token_db
47-
self.assert_introspected_token(access_token_db[access_token.value], access_token, iat)
47+
self.assert_introspected_token(authorization_request, access_token_db[access_token.value], access_token, iat)
4848
assert access_token_db[access_token.value]['exp'] == iat + self.TEST_TOKEN_LIFETIME
4949

50-
def assert_introspected_token(self, token_introspection, access_token, iat):
51-
auth_req = self.authorization_request().to_dict()
50+
def assert_introspected_token(self, authorization_request, token_introspection, access_token, iat):
51+
auth_req = authorization_request.to_dict()
5252

5353
assert token_introspection['scope'] == auth_req['scope']
5454
assert token_introspection['client_id'] == auth_req['client_id']
@@ -91,7 +91,7 @@ def test_create_access_token(self, authorization_state_factory, authorization_re
9191
self.set_valid_subject_identifier(authorization_state)
9292

9393
access_token = authorization_state.create_access_token(authorization_request, self.TEST_SUBJECT_IDENTIFIER)
94-
self.assert_access_token(access_token, authorization_state.access_tokens, MOCK_TIME.return_value)
94+
self.assert_access_token(authorization_request, access_token, authorization_state.access_tokens, MOCK_TIME.return_value)
9595

9696
def test_create_access_token_with_scope_other_than_auth_req(self, authorization_state, authorization_request):
9797
scope = ['openid', 'extra']
@@ -115,7 +115,7 @@ def test_introspect_access_token(self, authorization_state_factory, authorizatio
115115
access_token = authorization_state.create_access_token(authorization_request, self.TEST_SUBJECT_IDENTIFIER)
116116
token_introspection = authorization_state.introspect_access_token(access_token.value)
117117
assert token_introspection['active'] is True
118-
self.assert_introspected_token(token_introspection, access_token, MOCK_TIME.return_value)
118+
self.assert_introspected_token(authorization_request, token_introspection, access_token, MOCK_TIME.return_value)
119119

120120
def test_introspect_access_token_with_expired_token(self, authorization_state_factory, authorization_request):
121121
authorization_state = authorization_state_factory(access_token_lifetime=self.TEST_TOKEN_LIFETIME)
@@ -129,7 +129,7 @@ def test_introspect_access_token_with_expired_token(self, authorization_state_fa
129129
with patch('time.time', mock_time2):
130130
token_introspection = authorization_state.introspect_access_token(access_token.value)
131131
assert token_introspection['active'] is False
132-
self.assert_introspected_token(token_introspection, access_token, MOCK_TIME.return_value)
132+
self.assert_introspected_token(authorization_request, token_introspection, access_token, MOCK_TIME.return_value)
133133

134134
@pytest.mark.parametrize('access_token', INVALID_INPUT)
135135
def test_introspect_access_token_with_invalid_access_token(self, access_token, authorization_state):
@@ -149,7 +149,7 @@ def test_exchange_code_for_token(self, authorization_state_factory, authorizatio
149149
authz_code = authorization_state.create_authorization_code(authorization_request, self.TEST_SUBJECT_IDENTIFIER)
150150
access_token = authorization_state.exchange_code_for_token(authz_code)
151151

152-
self.assert_access_token(access_token, authorization_state.access_tokens, MOCK_TIME.return_value)
152+
self.assert_access_token(authorization_request, access_token, authorization_state.access_tokens, MOCK_TIME.return_value)
153153
assert authorization_state.authorization_codes[authz_code]['used'] == True
154154

155155
def test_exchange_code_for_token_with_scope_other_than_auth_req(self, authorization_state,
@@ -248,7 +248,7 @@ def test_use_refresh_token(self, authorization_state_factory, authorization_requ
248248
authorization_state.access_tokens[old_access_token.value]['exp']
249249
assert authorization_state.access_tokens[new_access_token.value]['iat'] > \
250250
authorization_state.access_tokens[old_access_token.value]['iat']
251-
self.assert_access_token(new_access_token, authorization_state.access_tokens, mock_time2.return_value)
251+
self.assert_access_token(authorization_request, new_access_token, authorization_state.access_tokens, mock_time2.return_value)
252252

253253
assert authorization_state.refresh_tokens[refresh_token]['access_token'] == new_access_token.value
254254

@@ -430,4 +430,4 @@ def test_remove_state_for_subject_identifier(self, authorization_state, authoriz
430430

431431
def test_remove_state_for_unknown_subject_identifier(self, authorization_state):
432432
with pytest.raises(InvalidSubjectIdentifier):
433-
authorization_state.delete_state_for_subject_identifier('unknown')
433+
authorization_state.delete_state_for_subject_identifier('unknown')

tests/test_requirements.txt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
1-
responses==0.5.1
2-
pycryptodomex
1+
pytest
2+
responses
3+
pycryptodomex

tox.ini

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,5 @@
22
envlist = py34, py35
33

44
[testenv]
5-
commands = py.test tests/
6-
deps = pytest
7-
-rtests/test_requirements.txt
5+
commands = py.test -rs -vvv tests/
6+
deps = -rtests/test_requirements.txt

0 commit comments

Comments
 (0)