Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

kcapi

Package Overview
Dependencies
Maintainers
1
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

kcapi

A module to automate stuff in Keycloak.

  • 1.1.2
  • PyPI
  • Socket score

Maintainers
1

Keycloak API

Python module to automate Keycloak or Red Hat Single Sign-On (RHSSO) configuration.

How To Install

pip install kcapi

Testing

To run the test you would need a Keycloak instance, you can run one locally or in the cloud then you just have to follow this steps:

python3.10 -m venv .venv
source .venv/bin/activate
pip install requests

# Setup SSO server - go to https://developers.redhat.com/developer-sandbox/get-started,
# launch sandbox environment, +Add, select some "Red Hat Single Sign-On..." template.
export KC_USER=admin
export KC_PASSWORD=admin_password
export KC_REALM=myrealm  # do not use master realm, it cannot be removed
export KC_ENDPOINT=https://my-first-sso-me-me-dev.apps.sandbox.x8i5.p1.openshiftapps.com

python -m unittest

API

OpenID

This class takes care of OpenID login using password owner credentials flow.

Constructor
from rhsso import OpenID

oid_client = OpenID({
        "client_id": "admin-cli",
        "username": USER,
        "password": PASSWORD,
        "grant_type":"password",
        "realm" : "master"
        }, endpoint)
  • client_id: Client Identifier in Keycloak.
  • username: Login username for the Realm.
  • password: Login password for the Realm.
  • grant_type: The grant type you want to use (usually password).
  • endpoint: A Keycloak or RHSSO URL endpoint, something like: https://my_keycloak.com.
Methods
getToken

This will initiate a session with the Keycloak server and will return a OpenID token back.

oid_client.getToken() #glTeDLlmmpLYoAAUMcFQqNOMjw5dA
createAdminClient

This static method should be used in order to access the master Realm in Keycloak.

    oidc = OpenID.createAdminClient(self.USER, self.PASSWORD)
    oidc.getToken() #glTeDLlmmpLYoAAUMcFQqNOMjw5dA

Keycloak

This class builds all the Keycloak configuration REST resources by using REST conventions we can target the majority of Keycloak services.

Constructor
kc = Keycloak(token, self.ENDPOINT)

The constructor takes two parameters:

  • token: A token with enough priviledge to perform the desired operation.
  • endpoint: A Keycloak or RHSSO URL endpoint, something like: https://my_keycloak.com.
Methods
build

This methods build a REST client (capabilities detailed below) targeting a specific Keycloak REST resource.

groups = kc.build('groups', 'my_realm')

# Create a group called DC
state = groups.create({"name": "DC"}).isOk()

In this example we build the 'groups' API for my_realm Realm.

Supported Resources

Here is a quick list of supported resources:

As long as you find a REST endpoint that follow the standard you can use this method to build a client around it, an example of this is the non well documented components endpoint.

  • components: This API allows you configure things like user federation or Realm keys.

  • authentication: Provide access to built-in and/or custom authentication flows.


admin

Similar to the build method but the client points to the master realm, allowing us operation such as realm creation.

    main_realm = kc.admin()

    # Creates a realm called my_realm
    main_realm.create({"enabled": "true", "id": my_realm, "realm": my_realm})

REST API

When you use the build or admin methods you will get back a REST class pointing to the Keycloak resource, keep in mind that this class don't check that the resource is valid, this is done to keep it flexible and to make it easy to adapt to new Keycloak REST API changes in the future.

Usage

In order to create one you need to build method we have used before:

batman = {
    "enabled":'true',
    "attributes":{},
    "username":"batman",
    "firstName":"Bruce",
    "lastName":"Wayne",
    "emailVerified":""
}

users = kc.build('users', 'DC')

# Create a user called batman in DC
state = users.create(batman).isOk()
Methods

Following the example above lets see the methods we have starting with the usual CRUD methods:

create

This method POST a dictionary into any given resource:

batman = {
    "enabled":'true',
    "attributes":{},
    "username":"batman",
    "firstName":"Bruce",
    "lastName":"Wayne",
    "emailVerified":""
}

state = users.create(batman).isOk()
  • dictionary: Dictionary with the fields we want to POST to the server.
update

This method performs a PUT on the resource.

batman_update = {
    "firstName":"Bruno",
    "emailVerified": True
}

id = 'bf81a9d9-811f-4807-bd69-3d74eecbe9f4'

state = users.update(id, batman_update).isOk()
  • id: Id of the resource in Keycloak.
  • dictionary: Dictionary representing the updated fields.
remove

This method sends a DELETE to the pointed resource.

batman_update = {
    "firstName":"Bruno",
    "emailVerified": True
}

id = 'bf81a9d9-811f-4807-bd69-3d74eecbe9f4'

state = users.remove(id).isOk()
  • id: Id of the resource in Keycloak.
  • dictionary: Dictionary representing the updated fields.
get

Send a GET request to retrieve a specific Keycloak resource.

id = 'bf81a9d9-811f-4807-bd69-3d74eecbe9f4'

user = users.get(id).response()
all

Return all objects of a particular resource type.

users = kc.build('users', 'DC')

# Create a user called batman in DC
user_list = users.all() # [ {id:'xxx-yyy', username: 'batman', ...} ]   
findFirst

Finds a resource by passing an arbitrary key/value pair.

users = kc.build('users', 'DC')

users.findFirst({"key":"username", "value": 'batman'})
exist

Check if a resource matching the provided id exists:

users = kc.build('users', 'DC')
id = 'bf81a9d9-811f-4807-bd69-3d74eecbe9f4'

users.exists(id) #True
existByKV

Check if a resource matching the provided key/value pair, exists.

users = kc.build('users', 'DC')

users.existByKV("username", 'batman') #False

ResponseHandler

Each CRUD method returns a ResponseHandler class with the following methods.

Methods
response

returns the requests response object.

users.update(id, batman_update).response().status_code #HTTP 201
isOk

Return True if the request complete successfully otherwise it will raise an exception.

state = users.update(id, batman_update).isOk() # Return True here.
verify

Does the same as isOk but it allow you to chain more methods.

batman_update = {
    "firstName":"Bruno",
    "emailVerified": True
}

id = 'bf81a9d9-811f-4807-bd69-3d74eecbe9f4'

cookies = users.update(id, batman_update).verify().response().cookies # Get cookies.

Specialisations

Realms

KeycloakCaches

This class handles the Keycloak caches.

Instantiation
# Creates a REST API instance target the Realms API. 
realms = kc.build('realms', 'my_realm') 

# Gets the cache Realms cache API. 
caches = realms.caches(self.REALM)
clearUserCache

This method tells Keycloak to clear the user cache.

caches.clearUserCache()
clearRealmCache

This method tells Keycloak to clear the realm cache.

caches.clearRealmCache()
clearKeyCache

This method tells Keycloak to clear the external public key cache for clients and identity providers.

caches.clearKeyCache()

For more information on how this caches works follow this link.

Users

updateCredentials

Update user credentials.

user_credentials = {
          'temporary': False,
          'value':'12345'
}

state = users.updateCredentials(user_info, user_credentials).isOk() # Updated user password.

Where:

  • temporary: Boolean where if True provide a temporary password just for the first login.
  • value: String with the password.
joinGroup

Add a user into a existing group.

First we need a group:

def createDCGroup():
  group = kc.build('groups', 'heroes')
  return group.create({"name": "DC"}).isOk()

Then we can join the group the following way:

  createDCGroup()

  users = kc.build('users', 'heroes')
  user = {"key": "username", "value": "batman"}
  group = {"key": "name", "value": "DC"}

  users.joinGroup(user, group).isOk()

The API works by matching the first occurrence between the provided key/value for the two resources (User and Group), this can help in various situation for example if we want to target the user by uuid.

Using uuid as user identifier.

  createDCGroup()

  users = kc.build('users', 'heroes')
  user = {"key": "uuid", "value": "23e4567-e89b-..."}
  group = {"key": "name", "value": "DC"}

  users.joinGroup(user, group).isOk()

Or we want to use the group id:

  user = {"key": "uuid", "value": "23e4567-e89b-..."}
  group = {"key": "id", "value": "f8d91722-a1f0-45e..."}

  users.joinGroup(user, group).isOk()

If the field criteria don't return a unique value, the first entry in the list will be used.

leaveGroup

Remove a user from a group.

  createDCGroup()

  users = kc.build('users', 'heroes')
  user = {"key": "username", "value": "batman"}
  group = {"key": "uuid", "value": "123e4567-e89b-..."}

  users.leaveGroup(user, group).isOk()

  user = {"key": "uuid", "value": "12d3-a456-4"}
  group = {"key": "id", "value": "123e4567-e89b-..."}

  users.leaveGroup(user, group).isOk()

The same rules for key/value discussed above also applies here.

Groups

To manage the relationship between realm level roles and groups, we can use the RealmsRolesMapping.

To get an instance of this class you need to instantiate the group resource class:

groups = kc.build('groups', 'heroes')

And use the method realmRoles passing a valid group dictionary:

realmsRoles = groups.realmRoles({"key":"name", "value":'DC'})

Then we get a class with following methods:

add

Add a list of existing roles to a group.

def makeRoles(self):
    roles = kc.build('roles', self.realm)
    lvl1 = roles.create({"name": "level-1"}).isOk()
    lvl2 = roles.create({"name": "level-2"}).isOk()
    return lvl1 and lvl2


if makeRoles():
  realmsRoles = groups.realmRoles({"key":"name", "value":'DC'})
  realmsRoles.add(["level-1", "level-2"])
remove

Remove a list of associated roles from a group.

realmsRoles = groups.realmRoles({"key":"name", "value":'DC'})
realmsRoles.remove(["level-1", "level-2"])

Roles

composite

In Keycloak we can map roles to other roles, this method allow you to do just that.

role_watch = self.kc.build('roles', 'my-realm').find('watch')
added = role_watch.add_composite('view')

FAQs


Did you know?

Socket

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Install

Related posts

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc