Skip to content

b12consulting/keypunch

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

31 Commits
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Keypunch is a small client for Keycloak. Its main goal is to automate some administrative tasks like creating a user or trigger an email.

Install

Install latest version:

pip install git+ssh://git@github.com/b12consulting/keypunch

Install a specific version:

pip install git+ssh://git@github.com/b12consulting/keypunch@2c6496ca519

The revision specified in @.. can be replaced by a tag or a branch name.

Usage

Client & endpoint

The first thing to do once a client instance is created is to log in:

from keypunch import KClient

base_url = "http://localhost:8080"
kcli = KClient(base_url=base_url)

# Login
kcli.login("admin", "admin")

# Login with another user
base_url = "http://localhost:8080"
kcli = KClient(base_url=base_url, realm="custom-realm")
kcli.login(
    "org-admin-123-abcd@noreply.phasetwo.io",
    "passwd",
)

The kcli object holds a requests session that manages the cookies, session refresh is not implemented so queries can fail after a few minutes (depending on your Keycloak settings).

The KClient class main job is to instanciate an Endpoint object based on a known path. For example in order to instanciate an endpoint for the "my-new-realm" realm we use the endpoint method that take as first argument the name of the endpoint and any extra argument needed to format the route:

ep = kcli.endpoint("realm", realm="my-new-realm")
print(ep.url)  #'http://localhost:8080/admin/realms/my-new-realm'

We can then trigger the actual query with either post, get, delete, put or form (form is like post but with "application/x-www-form-urlencoded" instead of the default "application/json"):

realm_info = ep.get()

To create a new realm (note that we use the "realms" endpoint this time):

ep = kcli.endpoint("realms")
ep.post(realm="another-new-realm")

In the example above the json payload {"realm":"another-new-realm"} will be sent in the request body.

For GET queries, the method argument will be used as query parameters, so this:

kcli.endpoint("clients", realm="master").get(max=1)

Will query this url: http://localhost:8080/admin/realms/master/clients?max=1

API support

The _paths attribute contains some pre-defined endpoints:

>>> pprint(KClient._paths)
{
 ...
 'realm': '/admin/realms/{realm}',
 ...
}

Only a few API endpoint are supported and are added in opportunistic fashion. You can easily add extra endpoints by updating the KClient._paths dictionary, like this:

KClient._paths.update({
    "roles": "/admin/{realm}/clients/{id}/roles",
    "client-scopes": "/admin//{realm}/client-scopes/{id}/scope-mappings/clients/{client}",
    })

Keycloak API is documented here: https://www.keycloak.org/docs-api/23.0.3/rest-api/index.html , Keycloak-orgs OpenAPI document: https://github.com/p2-inc/phasetwo-docs/blob/master/openapi.yaml

Logging

Enable logging

from keypunch.utils import logger
logger.setLevel("DEBUG")

Alternatively you can set the environment variable:

export KEYPUNCH_DEBUG=1

Testing

In order to run non-mocked test you will have to start a container:

docker run --name keypunch_test --rm -p 8080:8080 \
 -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=admin -e KC_HTTP_RELATIVE_PATH=/ \
 quay.io/phasetwo/phasetwo-keycloak:22.0.5 \
start-dev --spi-email-template-provider=freemarker-plus-mustache \
 --spi-email-template-freemarker-plus-mustache-enabled=true --spi-theme-cache-themes=false

Install test dependencies

pip install ".[test]"

Run tests

pytest tests/

Examples

Realms

# Get master realm info
kcli.endpoint('realm').get()

# Get app realm info
kcli.endpoint("realm", realm="my-new-realm").get()

# List realms
kcli.endpoint("realms").get()

# Create realm
kcli.endpoint('realms').post(
    realm="another-new-realm",
)

# Delete realms
kcli.endpoint('realm', realm="another-new-realm").delete()

Users

# List users
kcli.endpoint('users', realm="my-new-realm").get()

# Create user
kcli.endpoint('users', realm="my-new-realm").post(username="new-user")

Organisation membership

# List orgs
kcli.endpoint('orgs', realm="my-new-realm").get()

# List members
kcli.endpoint(
    'members',
    realm="my-new-realm",
    org_id="c501122a-e007-46d0-b620-cdcc2aa13f4c",
).get()

# Get membership info
kcli.endpoint(
    'user',
    realm="my-new-realm",
    user_id="5db4613c-e740-4617-b86e-6830d2550590",
    org_id="c501122a-e007-46d0-b620-cdcc2aa13f4c",
).get()

Organisation invitations

# Create Invitations
kcli.endpoint(
    'invitations',
    realm="my-new-realm",
    org_id="c501122a-e007-46d0-b620-cdcc2aa13f4c",
).post(
    email="ham@spam.com",
    send=True,
)

# List Invitations
kcli.endpoint(
    "invitations",
    realm="my-new-realm",
    org_id="c501122a-e007-46d0-b620-cdcc2aa13f4c",
).get()

# Delete
kclient.endpoint(
        'invitation',
    realm="test-realm",
    org_id=org_id,
    invitation_id=invitation[0]["id"],
).delete()

Organisation roles

# Get user for a given org role in a realm
kcli.endpoint(
    'role-users',
    realm="gpc_analysis",
    org_id="e397aff2-1b0f-43b8-8305-5b94b9fc853b",
    role="manage-members",
).get()

Trigger mails

# Action email with "UPDATE_PASSWORD" payload -> can be used as forgot password or for first login
kcli.endpoint(
    "execute-actions-email",
    realm="my-new-realm",
    user_id="5db4613c-e740-4617-b86e-6830d2550590",
).put(["UPDATE_PASSWORD"])

# Force user to cycle password (so not a situation were the user
# has forgot it)

kcli.endpoint(
    "reset-password",
    realm="my-new-realm",
    user_id="5db4613c-e740-4617-b86e-6830d2550590",
).put(
    temporary=True,
    type="password",
    value="hamspam",
)

About

Light Python client for Keycloak API

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages