Browse Source

Merged in add_idps (pull request #12)

Add get_idps

Approved-by: Marcos Pereira <marcospereira.mpj@gmail.com>
pull/12/head
Martin Devlin 6 years ago
committed by Marcos Pereira
parent
commit
75ab5702eb
  1. 9
      CHANGELOG.md
  2. 18
      README.md
  3. 4
      docs/source/conf.py
  4. 14
      docs/source/index.rst
  5. 9
      keycloak/exceptions.py
  6. 107
      keycloak/keycloak_admin.py
  7. 3
      keycloak/urls_patterns.py
  8. 1
      requirements.txt
  9. 4
      setup.py

9
CHANGELOG.md

@ -1,4 +1,3 @@
Changelog
============
@ -6,7 +5,7 @@ All notable changes to this project will be documented in this file.
## [0.5.0] - 2017-08-21
* Basic functions for Keycloak API (well_know, token, userinfo, logout, certs,
* Basic functions for Keycloak API (well_know, token, userinfo, logout, certs,
entitlement, instropect)
## [0.6.0] - 2017-08-23
@ -39,4 +38,8 @@ entitlement, instropect)
* Add groups functions
* Add Admin Tasks for user and client role management
* Function to trigger user sync from provider
* Optional parameter: verify
## [0.12.1] - 2018-08-04
* Add get_idps
* Rework group functions

18
README.md

@ -45,6 +45,9 @@ The documentation for python-keycloak is available on [readthedocs](http://pytho
* [Marcos Pereira](marcospereira.mpj@gmail.com)
* [Martin Devlin](martin.devlin@pearson.com)
* [Shon T. Urbas](shon.urbas@gmail.com>)
* [Markus Spanier]()
* [Remco Kranenburg]()
* [Remco Kranenburg]()
## Usage
@ -203,8 +206,21 @@ groups = keycloak_admin.get_groups()
group = keycloak_admin.get_group(group_id='group_id')
# Get group by name
group = keycloak_admin.get_group_by_name(name_or_path='group_id', search_in_subgroups=True)
group = keycloak_admin.get_group_by_path(path='/group/subgroup', search_in_subgroups=True)
# Function to trigger user sync from provider
sync_users(storage_id="storage_di", action="action")
# Get client role id from name
role_id = keycloak_admin.get_client_role_id(client_id=client_id, role_name="test")
# Get all roles for the realm or client
realm_roles = keycloak_admin.get_roles()
# Assign client role to user. Note that BOTH role_name and role_id appear to be required.
keycloak_admin.assign_client_role(client_id=client_id, user_id=user_id, role_id=role_id, role_name="test")
# Get all ID Providers
idps = keycloak_admin.get_idps()
```

4
docs/source/conf.py

@ -60,9 +60,9 @@ author = 'Marcos Pereira'
# built documents.
#
# The short X.Y version.
version = '0.12.0'
version = '0.12.1'
# The full version, including alpha/beta/rc tags.
release = '0.12.0'
release = '0.12.1'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.

14
docs/source/index.rst

@ -43,6 +43,7 @@ python-keycloak depends on:
* Python 3
* `requests <http://docs.python-requests.org/en/master/>`_
* `python-jose <http://python-jose.readthedocs.io/en/latest/>`_
* `simplejson <https://simplejson.readthedocs.io/en/latest/>`_
Tests Dependencies
------------------
@ -81,7 +82,8 @@ Main methods::
keycloak_openid = KeycloakOpenID(server_url="http://localhost:8080/auth/",
client_id="example_client",
realm_name="example_realm",
client_secret_key="secret")
client_secret_key="secret",
verify=True)
# Get WellKnow
config_well_know = keycloak_openid.well_know()
@ -216,6 +218,12 @@ Main methods::
# Create client role
keycloak_admin.create_client_role(client_id, "test")
# Get client role id from name
role_id = keycloak_admin.get_client_role_id(client_id=client_id, role_name="test")
# Get all roles for the realm or client
realm_roles = keycloak_admin.get_roles()
# Assign client role to user. Note that BOTH role_name and role_id appear to be required.
keycloak_admin.assign_client_role(client_id="client_id", user_id="user_id", role_id="role_id", role_name="test")
@ -228,8 +236,8 @@ Main methods::
# Get group
group = keycloak_admin.get_group(group_id='group_id')
# Get group by name
group = keycloak_admin.get_group_by_name(name_or_path='group_id', search_in_subgroups=True)
# Get group by path
group = keycloak_admin.get_group_by_path(path='/group/subgroup', search_in_subgroups=True)
# Function to trigger user sync from provider
sync_users(storage_id="storage_di", action="action")

9
keycloak/exceptions.py

@ -16,6 +16,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import requests
from simplejson import JSONDecodeError
class KeycloakError(Exception):
@ -67,16 +68,20 @@ class KeycloakInvalidTokenError(KeycloakOperationError):
pass
def raise_error_from_response(response, error, expected_code=200):
def raise_error_from_response(response, error, expected_code=200, skip_exists=False):
if expected_code == response.status_code:
if expected_code == requests.codes.no_content:
return {}
try:
return response.json()
except ValueError:
except JSONDecodeError as e:
return response.content
if skip_exists and response.status_code == 409:
return {"Already exists"}
try:
message = response.json()['message']
except (KeyError, ValueError):

107
keycloak/keycloak_admin.py

@ -123,6 +123,19 @@ class KeycloakAdmin:
data_raw = self.connection.raw_get(URL_ADMIN_USERS.format(**params_path), **query)
return raise_error_from_response(data_raw, KeycloakGetError)
def get_idps(self):
"""
Returns a list of ID Providers,
IdentityProviderRepresentation
https://www.keycloak.org/docs-api/3.3/rest-api/index.html#_identityproviderrepresentation
:return: array IdentityProviderRepresentation
"""
params_path = {"realm-name": self.realm_name}
data_raw = self.connection.raw_get(URL_ADMIN_IDPS.format(**params_path))
return raise_error_from_response(data_raw, KeycloakGetError)
def create_user(self, payload):
"""
Create a new user Username must be unique
@ -135,6 +148,12 @@ class KeycloakAdmin:
:return: UserRepresentation
"""
params_path = {"realm-name": self.realm_name}
exists = self.get_user_id(username=payload['username'])
if exists is not None:
return str(exists)
data_raw = self.connection.raw_post(URL_ADMIN_USERS.format(**params_path),
data=json.dumps(payload))
return raise_error_from_response(data_raw, KeycloakGetError, expected_code=201)
@ -333,7 +352,28 @@ class KeycloakAdmin:
data_raw = self.connection.raw_get(URL_ADMIN_GROUP.format(**params_path))
return raise_error_from_response(data_raw, KeycloakGetError)
def get_group_by_name(self, name_or_path, search_in_subgroups=False):
def get_subgroups(self, group, path):
"""
Utility function to iterate through nested group structures
GroupRepresentation
http://www.keycloak.org/docs-api/3.2/rest-api/#_grouprepresentation
:param name: group (GroupRepresentation)
:param path: group path (string)
:return: Keycloak server response (GroupRepresentation)
"""
for subgroup in group["subGroups"]:
if subgroup['path'] == path:
return subgroup
elif subgroup["subGroups"]:
for subgroup in group["subGroups"]:
return self.get_subgroups(subgroup, path)
return None
def get_group_by_path(self, path, search_in_subgroups=False):
"""
Get group id based on name or path.
A straight name or path match with a top-level group will return first.
@ -342,7 +382,6 @@ class KeycloakAdmin:
GroupRepresentation
http://www.keycloak.org/docs-api/3.2/rest-api/#_grouprepresentation
:param name: group name
:param path: group path
:param search_in_subgroups: True if want search in the subgroups
:return: Keycloak server response (GroupRepresentation)
@ -352,48 +391,48 @@ class KeycloakAdmin:
# TODO: Review this code is necessary
for group in groups:
if group['name'] == name_or_path or group['path'] == name_or_path:
if group['path'] == path:
return group
elif search_in_subgroups and group["subGroups"]:
for subgroup in group["subGroups"]:
if subgroup['name'] == name_or_path or subgroup['path'] == name_or_path:
return subgroup
res = self.get_subgroups(group, path)
if res != None:
return res
return None
def create_group(self, name=None, client_roles={}, realm_roles=[], sub_groups=[], path=None, parent=None):
def create_group(self, payload, parent=None, skip_exists=False):
"""
Create a group in the Realm
Creates a group in the Realm
:param payload: GroupRepresentation
:param parent: parent group's id. Required to create a sub-group.
GroupRepresentation
http://www.keycloak.org/docs-api/3.2/rest-api/#_grouprepresentation
:param name: group name
:param client_roles: (Dict) Client roles to include in groupp # Not demonstrated to work
:param realm_roles: (List) Realm roles to include in group # Not demonstrated to work
:param sub_groups: (List) Subgroups to include in groupp # Not demonstrated to work
:param path: group path
:param parent: parent group's id. Required to create a sub-group.
:return: Keycloak server response (GroupRepresentation)
:return: Http response
"""
name = payload['name']
path = payload['path']
exists = None
if name is None and path is not None:
path="/" + name
elif path is not None:
exists = self.get_group_by_path(path=path, search_in_subgroups=True)
data = {"name": name or path,
"path": path,
"clientRoles": client_roles,
"realmRoles": realm_roles,
"subGroups": sub_groups}
if exists is not None:
return str(exists)
if parent is None:
params_path = {"realm-name": self.realm_name}
data_raw = self.connection.raw_post(URL_ADMIN_GROUPS.format(**params_path),
data=json.dumps(data))
params_path = {"realm-name": self.realm_name}
data_raw = self.connection.raw_post(URL_ADMIN_GROUPS.format(**params_path),
data=json.dumps(payload))
else:
params_path = {"realm-name": self.realm_name, "id": parent}
data_raw = self.connection.raw_post(URL_ADMIN_GROUP_CHILD.format(**params_path),
data=json.dumps(data))
return raise_error_from_response(data_raw, KeycloakGetError, expected_code=201)
params_path = {"realm-name": self.realm_name, "id": parent,}
data_raw = self.connection.raw_post(URL_ADMIN_GROUP_CHILD.format(**params_path),
data=json.dumps(payload))
return raise_error_from_response(data_raw, KeycloakGetError, expected_code=201, skip_exists=skip_exists)
def group_set_permissions(self, group_id, enabled=True):
"""
@ -496,7 +535,7 @@ class KeycloakAdmin:
return None
def create_client(self, payload):
def create_client(self, payload, skip_exists=False):
"""
Create a client
@ -509,7 +548,7 @@ class KeycloakAdmin:
params_path = {"realm-name": self.realm_name}
data_raw = self.connection.raw_post(URL_ADMIN_CLIENTS.format(**params_path),
data=json.dumps(payload))
return raise_error_from_response(data_raw, KeycloakGetError, expected_code=201)
return raise_error_from_response(data_raw, KeycloakGetError, expected_code=201, skip_exists=skip_exists)
def delete_client(self, client_id):
"""
@ -591,7 +630,7 @@ class KeycloakAdmin:
role = self.get_client_role(client_id, role_name)
return role.get("id")
def create_client_role(self, payload):
def create_client_role(self, payload, skip_exists=False):
"""
Create a client role
@ -605,7 +644,7 @@ class KeycloakAdmin:
params_path = {"realm-name": self.realm_name, "id": self.client_id}
data_raw = self.connection.raw_post(URL_ADMIN_CLIENT_ROLES.format(**params_path),
data=json.dumps(payload))
return raise_error_from_response(data_raw, KeycloakGetError, expected_code=201)
return raise_error_from_response(data_raw, KeycloakGetError, expected_code=201, skip_exists=skip_exists)
def delete_client_role(self, role_name):
"""

3
keycloak/urls_patterns.py

@ -35,6 +35,7 @@ URL_ADMIN_RESET_PASSWORD = "admin/realms/{realm-name}/users/{id}/reset-password"
URL_ADMIN_GET_SESSIONS = "admin/realms/{realm-name}/users/{id}/sessions"
URL_ADMIN_USER_CLIENT_ROLES = "admin/realms/{realm-name}/users/{id}/role-mappings/clients/{client-id}"
URL_ADMIN_USER_GROUP = "admin/realms/{realm-name}/users/{id}/groups/{group-id}"
URL_ADMIN_USER_PASSWORD = "admin/realms/{realm-name}/users/{id}/reset-password"
URL_ADMIN_SERVER_INFO = "admin/serverinfo"
@ -51,3 +52,5 @@ URL_ADMIN_CLIENT_ROLE = "admin/realms/{realm-name}/clients/{id}/roles/{role-name
URL_ADMIN_REALM_ROLES = "admin/realms/{realm-name}/roles"
URL_ADMIN_USER_STORAGE = "admin/realms/{realm-name}/user-storage/{id}/sync"
URL_ADMIN_IDPS = "admin/realms/{realm}/identity-provider/instances"

1
requirements.txt

@ -1,3 +1,4 @@
requests>=2.18.4
httmock>=1.2.5
python-jose>=1.4.0
simplejson

4
setup.py

@ -4,7 +4,7 @@ from setuptools import setup
setup(
name='python-keycloak',
version='0.11.1',
version='0.12.1',
url='https://bitbucket.org/agriness/python-keycloak',
license='GNU General Public License - V3',
author='Marcos Pereira',
@ -12,7 +12,7 @@ setup(
keywords='keycloak openid',
description=u'python-keycloak is a Python package providing access to the Keycloak API.',
packages=['keycloak', 'keycloak.authorization', 'keycloak.tests'],
install_requires=['requests==2.18.4', 'httmock==1.2.5', 'python-jose==1.4.0'],
install_requires=['requests==2.18.4', 'httmock==1.2.5', 'python-jose==1.4.0', 'simplejson'],
classifiers=[
'Programming Language :: Python :: 3',
'License :: OSI Approved :: GNU General Public License v3 (GPLv3)',

Loading…
Cancel
Save