Skip to content

Commit 38dffac

Browse files
Merge branch 'master' into auto-refresh-admin-token
2 parents 1e80655 + 97a2e0f commit 38dffac

File tree

9 files changed

+193
-54
lines changed

9 files changed

+193
-54
lines changed

Pipfile.lock

Lines changed: 19 additions & 19 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

docs/source/conf.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,9 @@
6060
# built documents.
6161
#
6262
# The short X.Y version.
63-
version = '0.17.4'
63+
version = '0.17.6'
6464
# The full version, including alpha/beta/rc tags.
65-
release = '0.17.4'
65+
release = '0.17.6'
6666

6767
# The language for content autogenerated by Sphinx. Refer to documentation
6868
# for a list of supported languages.

docs/source/index.rst

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,14 @@ Main methods::
9292
client_secret_key="secret",
9393
verify=True)
9494

95+
# Optionally, you can pass custom headers that will be added to all HTTP calls
96+
# keycloak_openid = KeycloakOpenID(server_url="http://localhost:8080/auth/",
97+
# client_id="example_client",
98+
# realm_name="example_realm",
99+
# client_secret_key="secret",
100+
# verify=True,
101+
# custom_headers={'CustomHeader': 'value'})
102+
95103
# Get WellKnow
96104
config_well_know = keycloak_openid.well_know()
97105

@@ -143,6 +151,14 @@ Main methods::
143151
realm_name="example_realm",
144152
verify=True)
145153

154+
# Optionally, you can pass custom headers that will be added to all HTTP calls
155+
#keycloak_admin = KeycloakAdmin(server_url="http://localhost:8080/auth/",
156+
# username='example-admin',
157+
# password='secret',
158+
# realm_name="example_realm",
159+
# verify=True,
160+
# custom_headers={'CustomHeader': 'value'})
161+
146162
# Add user
147163
new_user = keycloak_admin.create_user({"email": "[email protected]",
148164
"username": "[email protected]",

keycloak/__init__.py

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,25 @@
11
# -*- coding: utf-8 -*-
22
#
3+
# The MIT License (MIT)
4+
#
35
# Copyright (C) 2017 Marcos Pereira <[email protected]>
46
#
5-
# This program is free software: you can redistribute it and/or modify
6-
# it under the terms of the GNU Lesser General Public License as published by
7-
# the Free Software Foundation, either version 3 of the License, or
8-
# (at your option) any later version.
7+
# Permission is hereby granted, free of charge, to any person obtaining a copy of
8+
# this software and associated documentation files (the "Software"), to deal in
9+
# the Software without restriction, including without limitation the rights to
10+
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
11+
# the Software, and to permit persons to whom the Software is furnished to do so,
12+
# subject to the following conditions:
913
#
10-
# This program is distributed in the hope that it will be useful,
11-
# but WITHOUT ANY WARRANTY; without even the implied warranty of
12-
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13-
# GNU Lesser General Public License for more details.
14+
# The above copyright notice and this permission notice shall be included in all
15+
# copies or substantial portions of the Software.
1416
#
15-
# You should have received a copy of the GNU Lesser General Public License
16-
# along with this program. If not, see <http://www.gnu.org/licenses/>.
17+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
19+
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
20+
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
21+
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22+
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
1723

1824
from .keycloak_admin import *
1925
from .keycloak_openid import *

keycloak/authorization/__init__.py

Lines changed: 16 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,25 @@
11
# -*- coding: utf-8 -*-
22
#
3+
# The MIT License (MIT)
4+
#
35
# Copyright (C) 2017 Marcos Pereira <[email protected]>
46
#
5-
# This program is free software: you can redistribute it and/or modify
6-
# it under the terms of the GNU Lesser General Public License as published by
7-
# the Free Software Foundation, either version 3 of the License, or
8-
# (at your option) any later version.
7+
# Permission is hereby granted, free of charge, to any person obtaining a copy of
8+
# this software and associated documentation files (the "Software"), to deal in
9+
# the Software without restriction, including without limitation the rights to
10+
# use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
11+
# the Software, and to permit persons to whom the Software is furnished to do so,
12+
# subject to the following conditions:
913
#
10-
# This program is distributed in the hope that it will be useful,
11-
# but WITHOUT ANY WARRANTY; without even the implied warranty of
12-
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13-
# GNU Lesser General Public License for more details.
14+
# The above copyright notice and this permission notice shall be included in all
15+
# copies or substantial portions of the Software.
1416
#
15-
# You should have received a copy of the GNU Lesser General Public License
16-
# along with this program. If not, see <http://www.gnu.org/licenses/>.
17+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
19+
# FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
20+
# COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
21+
# IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
22+
# CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
1723

1824
import ast
1925
import json

keycloak/keycloak_admin.py

Lines changed: 70 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@
4646
class KeycloakAdmin:
4747

4848
PAGE_SIZE = 100
49-
49+
5050
_server_url = None
5151
_username = None
5252
_password = None
@@ -57,9 +57,11 @@ class KeycloakAdmin:
5757
_auto_refresh_token = None
5858
_connection = None
5959
_token = None
60+
_custom_headers = None
61+
_user_realm_name = None
6062

61-
def __init__(self, server_url, username, password, realm_name='master', client_id='admin-cli', verify=True, client_secret_key=None,
62-
auto_refresh_token=None):
63+
def __init__(self, server_url, username, password, realm_name='master', client_id='admin-cli', verify=True,
64+
client_secret_key=None, custom_headers=None, user_realm_name=None, auto_refresh_token=None):
6365
"""
6466
6567
:param server_url: Keycloak server url
@@ -69,6 +71,7 @@ def __init__(self, server_url, username, password, realm_name='master', client_i
6971
:param client_id: client id
7072
:param verify: True if want check connection SSL
7173
:param client_secret_key: client secret key
74+
:param custom_headers: dict of custom header to pass to each HTML request
7275
:param auto_refresh_token: list of methods that allows automatic token refresh. ex: ['get', 'put', 'post', 'delete']
7376
"""
7477
self.server_url = server_url
@@ -79,11 +82,12 @@ def __init__(self, server_url, username, password, realm_name='master', client_i
7982
self.verify = verify
8083
self.client_secret_key = client_secret_key
8184
self.auto_refresh_token = auto_refresh_token or []
85+
self.user_realm_name = user_realm_name
86+
self.custom_headers = custom_headers
8287

8388
# Get token Admin
8489
self.get_token()
8590

86-
8791
@property
8892
def server_url(self):
8993
return self._server_url
@@ -160,6 +164,22 @@ def token(self, value):
160164
def auto_refresh_token(self):
161165
return self._auto_refresh_token
162166

167+
@property
168+
def user_realm_name(self):
169+
return self._user_realm_name
170+
171+
@user_realm_name.setter
172+
def user_realm_name(self, value):
173+
self._user_realm_name = value
174+
175+
@property
176+
def custom_headers(self):
177+
return self._custom_headers
178+
179+
@custom_headers.setter
180+
def custom_headers(self, value):
181+
self._custom_headers = value
182+
163183
@auto_refresh_token.setter
164184
def auto_refresh_token(self, value):
165185
allowed_methods = {'get', 'post', 'put', 'delete'}
@@ -226,13 +246,13 @@ def get_realms(self):
226246

227247
def create_realm(self, payload, skip_exists=False):
228248
"""
229-
Create a client
249+
Create a realm
230250
231251
ClientRepresentation: http://www.keycloak.org/docs-api/3.3/rest-api/index.html#_realmrepresentation
232252
233253
:param skip_exists: Skip if Realm already exist.
234254
:param payload: RealmRepresentation
235-
:return: Keycloak server response (UserRepresentation)
255+
:return: Keycloak server response (RealmRepresentation)
236256
"""
237257

238258
data_raw = self.raw_post(URL_ADMIN_REALMS,
@@ -732,6 +752,20 @@ def create_client(self, payload, skip_exists=False):
732752
data=json.dumps(payload))
733753
return raise_error_from_response(data_raw, KeycloakGetError, expected_code=201, skip_exists=skip_exists)
734754

755+
def update_client(self, client_id, payload):
756+
"""
757+
Update a client
758+
759+
:param client_id: Client id
760+
:param payload: ClientRepresentation
761+
762+
:return: Http response
763+
"""
764+
params_path = {"realm-name": self.realm_name, "id": client_id}
765+
data_raw = self.connection.raw_put(URL_ADMIN_CLIENT.format(**params_path),
766+
data=json.dumps(payload))
767+
return raise_error_from_response(data_raw, KeycloakGetError, expected_code=204)
768+
735769
def delete_client(self, client_id):
736770
"""
737771
Get representation of the client
@@ -860,6 +894,21 @@ def assign_client_role(self, user_id, client_id, roles):
860894
data=json.dumps(payload))
861895
return raise_error_from_response(data_raw, KeycloakGetError, expected_code=204)
862896

897+
def create_realm_role(self, payload, skip_exists=False):
898+
"""
899+
Create a new role for the realm or client
900+
901+
:param realm: realm name (not id)
902+
:param rep: RoleRepresentation https://www.keycloak.org/docs-api/5.0/rest-api/index.html#_rolerepresentation
903+
:return Keycloak server response
904+
"""
905+
906+
params_path = {"realm-name": self.realm_name}
907+
data_raw = self.connection.raw_post(URL_ADMIN_REALM_ROLES.format(**params_path),
908+
data=json.dumps(payload))
909+
return raise_error_from_response(data_raw, KeycloakGetError, expected_code=201, skip_exists=skip_exists)
910+
911+
863912
def assign_realm_roles(self, user_id, client_id, roles):
864913
"""
865914
Assign realm roles to a user
@@ -1109,16 +1158,27 @@ def raw_delete(self, *args, **kwargs):
11091158

11101159
def get_token(self):
11111160
self.keycloak_openid = KeycloakOpenID(server_url=self.server_url, client_id=self.client_id,
1112-
realm_name=self.realm_name, verify=self.verify,
1113-
client_secret_key=self.client_secret_key)
1161+
realm_name=self.user_realm_name or self.realm_name, verify=self.verify,
1162+
client_secret_key=self.client_secret_key
1163+
custom_headers=self.custom_headers)
11141164

11151165
grant_type = ["password"]
11161166
if self.client_secret_key:
11171167
grant_type = ["client_credentials"]
1168+
11181169
self._token = self.keycloak_openid.token(self.username, self.password, grant_type=grant_type)
1170+
1171+
headers = {
1172+
'Authorization': 'Bearer ' + self.token.get('access_token'),
1173+
'Content-Type': 'application/json'
1174+
}
1175+
1176+
if self.custom_headers is not None:
1177+
# merge custom headers to main headers
1178+
headers.update(self.custom_headers)
1179+
11191180
self._connection = ConnectionManager(base_url=self.server_url,
1120-
headers={'Authorization': 'Bearer ' + self.token.get('access_token'),
1121-
'Content-Type': 'application/json'},
1181+
headers=headers,
11221182
timeout=60,
11231183
verify=self.verify)
11241184

keycloak/keycloak_openid.py

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -43,20 +43,25 @@
4343

4444
class KeycloakOpenID:
4545

46-
def __init__(self, server_url, realm_name, client_id, client_secret_key=None, verify=True):
46+
def __init__(self, server_url, realm_name, client_id, client_secret_key=None, verify=True, custom_headers=None):
4747
"""
4848
4949
:param server_url: Keycloak server url
5050
:param client_id: client id
5151
:param realm_name: realm name
5252
:param client_secret_key: client secret key
5353
:param verify: True if want check connection SSL
54+
:param custom_headers: dict of custom header to pass to each HTML request
5455
"""
5556
self._client_id = client_id
5657
self._client_secret_key = client_secret_key
5758
self._realm_name = realm_name
59+
headers = dict()
60+
if custom_headers is not None:
61+
# merge custom headers to main headers
62+
headers.update(custom_headers)
5863
self._connection = ConnectionManager(base_url=server_url,
59-
headers={},
64+
headers=headers,
6065
timeout=60,
6166
verify=verify)
6267

0 commit comments

Comments
 (0)