Skip to content

Commit 776cb75

Browse files
author
Peter Slump
committed
Added UMA 1 client
1 parent 84f1b30 commit 776cb75

3 files changed

Lines changed: 285 additions & 0 deletions

File tree

src/keycloak/realm.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from keycloak.client import KeycloakClient
44
from keycloak.openid_connect import KeycloakOpenidConnect
55
from keycloak.uma import KeycloakUMA
6+
from keycloak.uma1 import KeycloakUMA1
67

78

89
class KeycloakRealm(object):
@@ -70,10 +71,28 @@ def authz(self, client_id):
7071
def uma(self):
7172
"""
7273
Get UMA client
74+
75+
This method is here for backwards compatibility
76+
7377
:return: keycloak.uma.KeycloakUMA
7478
"""
79+
return self.uma2
80+
81+
@property
82+
def uma2(self):
83+
"""
84+
Starting from Keycloak 4 UMA2 is supported
85+
:rtype: keycloak.uma.KeycloakUMA
86+
"""
7587
return KeycloakUMA(realm=self)
7688

89+
@property
90+
def uma1(self):
91+
"""
92+
:rtype: keycloak.uma1.KeycloakUMA1
93+
"""
94+
return KeycloakUMA1(realm=self)
95+
7796
def close(self):
7897
if self._client is not None:
7998
self._client.close()

src/keycloak/uma1.py

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
import json
2+
3+
try:
4+
from urllib.parse import urlencode # noqa: F401
5+
except ImportError:
6+
from urllib import urlencode # noqa: F401
7+
8+
from keycloak.mixins import WellKnownMixin
9+
10+
PATH_WELL_KNOWN = "auth/realms/{}/.well-known/uma-configuration"
11+
12+
13+
class KeycloakUMA1(WellKnownMixin, object):
14+
DEFAULT_HEADERS = {"Content-type": 'application/json'}
15+
16+
_realm = None
17+
_well_known = None
18+
_dumps = staticmethod(json.dumps)
19+
20+
def __init__(self, realm):
21+
"""
22+
:type realm: keycloak.realm.KeycloakRealm
23+
"""
24+
self._realm = realm
25+
26+
def get_path_well_known(self):
27+
return PATH_WELL_KNOWN
28+
29+
def resource_set_create(self, token, name, **kwargs):
30+
"""
31+
Create a resource set.
32+
33+
https://docs.kantarainitiative.org/uma/rec-oauth-resource-reg-v1_0_1.html#rfc.section.2.2.1
34+
35+
:param str token: client access token
36+
:param str name:
37+
:param str uri: (optional)
38+
:param str type: (optional)
39+
:param list scopes: (optional)
40+
:param str icon_uri: (optional)
41+
:rtype: str
42+
"""
43+
return self._realm.client.post(
44+
self.well_known['resource_set_registration_endpoint'],
45+
data=self._get_data(name=name, **kwargs),
46+
headers=self.get_headers(token)
47+
)
48+
49+
def resource_set_update(self, token, id, name, **kwargs):
50+
"""
51+
Update a resource set.
52+
53+
https://docs.kantarainitiative.org/uma/rec-oauth-resource-reg-v1_0_1.html#update-resource-set
54+
55+
:param str token: client access token
56+
:param str id: Identifier of the resource set
57+
:param str name:
58+
:param str uri: (optional)
59+
:param str type: (optional)
60+
:param list scopes: (optional)
61+
:param str icon_uri: (optional)
62+
:rtype: str
63+
"""
64+
return self._realm.client.put(
65+
'{}/{}'.format(
66+
self.well_known['resource_set_registration_endpoint'], id),
67+
data=self._get_data(name=name, **kwargs),
68+
headers=self.get_headers(token)
69+
)
70+
71+
def resource_set_read(self, token, id):
72+
"""
73+
Read a resource set.
74+
75+
https://docs.kantarainitiative.org/uma/rec-oauth-resource-reg-v1_0_1.html#read-resource-set
76+
77+
:param str token: client access token
78+
:param str id: Identifier of the resource set
79+
:rtype: dict
80+
"""
81+
return self._realm.client.get(
82+
'{}/{}'.format(
83+
self.well_known['resource_set_registration_endpoint'], id),
84+
headers=self.get_headers(token)
85+
)
86+
87+
def resource_set_delete(self, token, id):
88+
"""
89+
Delete a resource set.
90+
91+
https://docs.kantarainitiative.org/uma/rec-oauth-resource-reg-v1_0_1.html#delete-resource-set
92+
93+
:param str token: client access token
94+
:param str id: Identifier of the resource set
95+
"""
96+
return self._realm.client.delete(
97+
'{}/{}'.format(
98+
self.well_known['resource_set_registration_endpoint'], id),
99+
headers=self.get_headers(token)
100+
)
101+
102+
def resource_set_list(self, token, **kwargs):
103+
"""
104+
List a resource set.
105+
106+
https://docs.kantarainitiative.org/uma/rec-oauth-resource-reg-v1_0_1.html#list-resource-sets
107+
108+
:param str token: client access token
109+
:param str name: (optional)
110+
:param str uri: (optional)
111+
:param str owner: (optional)
112+
:param str type: (optional)
113+
:param str scope: (optional)
114+
:rtype: list
115+
"""
116+
return self._realm.client.get(
117+
self.well_known['resource_set_registration_endpoint'],
118+
headers=self.get_headers(token),
119+
**kwargs
120+
)
121+
122+
@classmethod
123+
def get_headers(cls, token):
124+
return dict(cls.DEFAULT_HEADERS, **{
125+
"Authorization": "Bearer " + token,
126+
})
127+
128+
@staticmethod
129+
def get_payload(name, scopes=None, **kwargs):
130+
return dict(name=name, scopes=scopes or [], **kwargs)
131+
132+
def _get_data(self, *args, **kwargs):
133+
return self._dumps(self.get_payload(*args, **kwargs))

tests/keycloak/test_uma1.py

Lines changed: 133 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,133 @@
1+
from unittest import TestCase
2+
3+
import mock
4+
5+
from keycloak.uma1 import KeycloakUMA1
6+
from keycloak.realm import KeycloakRealm
7+
from keycloak.well_known import KeycloakWellKnown
8+
9+
10+
class KeycloakUma1TestCase(TestCase):
11+
12+
def setUp(self):
13+
self.realm = mock.MagicMock(spec_set=KeycloakRealm)
14+
self.uma_client = KeycloakUMA1(realm=self.realm)
15+
self.uma_client.well_known.contents = {
16+
'resource_set_registration_endpoint':
17+
'https://resource_registration',
18+
}
19+
20+
def test_well_known(self):
21+
"""
22+
Case: .well-known is requested
23+
Expected: it's returned and the second time the same is returned
24+
"""
25+
well_known = self.uma_client.well_known
26+
27+
self.assertIsInstance(well_known, KeycloakWellKnown)
28+
self.assertEqual(well_known, self.uma_client.well_known)
29+
30+
def test_get_headers(self):
31+
result = self.uma_client.get_headers(token='test-token')
32+
self.assertEqual(result, {
33+
"Authorization": "Bearer test-token",
34+
"Content-type": 'application/json'
35+
})
36+
37+
def test_get_payload(self):
38+
result = self.uma_client.get_payload(
39+
name='test-name',
40+
scopes=['test-scope'],
41+
optional_param='test-optional-param'
42+
)
43+
44+
self.assertEqual(result, {
45+
'name': 'test-name',
46+
'scopes': ['test-scope'],
47+
'optional_param': 'test-optional-param'
48+
})
49+
50+
result = self.uma_client.get_payload(
51+
name='test-name',
52+
optional_param='test-optional-param'
53+
)
54+
55+
self.assertEqual(result, {
56+
'name': 'test-name',
57+
'scopes': [],
58+
'optional_param': 'test-optional-param'
59+
})
60+
61+
def test_resource_set_create(self):
62+
result = self.uma_client.resource_set_create(
63+
token='test-token',
64+
name='test-name',
65+
optional_param='test-optional-param'
66+
)
67+
68+
self.realm.client.post.assert_called_once_with(
69+
'https://resource_registration',
70+
data=self.uma_client._get_data(
71+
name='test-name',
72+
optional_param='test-optional-param'
73+
),
74+
headers=self.uma_client.get_headers('test-token')
75+
)
76+
self.assertEqual(result, self.realm.client.post.return_value)
77+
78+
def test_resource_set_update(self):
79+
result = self.uma_client.resource_set_update(
80+
token='test-token',
81+
id='test-id',
82+
name='test-name',
83+
optional_param='test-optional-param'
84+
)
85+
86+
self.realm.client.put.assert_called_once_with(
87+
'https://resource_registration/test-id',
88+
data=self.uma_client._get_data(
89+
name='test-name',
90+
optional_param='test-optional-param'
91+
),
92+
headers=self.uma_client.get_headers('test-token')
93+
)
94+
self.assertEqual(result, self.realm.client.put.return_value)
95+
96+
def test_resource_set_read(self):
97+
result = self.uma_client.resource_set_read(
98+
token='test-token',
99+
id='test-id',
100+
)
101+
102+
self.realm.client.get.assert_called_once_with(
103+
'https://resource_registration/test-id',
104+
headers=self.uma_client.get_headers('test-token')
105+
)
106+
self.assertEqual(result, self.realm.client.get.return_value)
107+
108+
def test_resource_set_delete(self):
109+
result = self.uma_client.resource_set_delete(
110+
token='test-token',
111+
id='test-id',
112+
)
113+
114+
self.realm.client.delete.assert_called_once_with(
115+
'https://resource_registration/test-id',
116+
headers=self.uma_client.get_headers('test-token')
117+
)
118+
self.assertEqual(result, self.realm.client.delete.return_value)
119+
120+
def test_resource_set_list(self):
121+
result = self.uma_client.resource_set_list(
122+
token='test-token',
123+
name='test-name',
124+
owner='test-owner'
125+
)
126+
127+
self.realm.client.get.assert_called_once_with(
128+
'https://resource_registration',
129+
name='test-name',
130+
owner='test-owner',
131+
headers=self.uma_client.get_headers('test-token')
132+
)
133+
self.assertEqual(result, self.realm.client.get.return_value)

0 commit comments

Comments
 (0)