DEV Community

Cover image for Introducing Mantelo - The Best Keycloak Admin Client for Python
Lucy Linder
Lucy Linder

Posted on • Originally published at blog.derlin.ch

Introducing Mantelo - The Best Keycloak Admin Client for Python

I'm thrilled to present my newest open-source project! Mantelo is a super small yet super powerful Python library for interacting with the Keycloak Admin API.

Mantelo [manหˆtelo], from German "Mantel", from Late Latin "mantum" means "cloak" in Esperanto.

Born from the frustration of encountering incomplete support in existing libraries, this is the only Python client that guarantees full coverage of all the routes in the Keycloak Admin RESTful API. How? By wrapping instead of implementing. Let me explain ๐Ÿ˜Š.


TOC generated with bitdowntoc.


The motivation behind mantelo

When I started working with Keycloak in Python, I first opted for the renowned python-keycloak, considered the most complete library.

It worked well until I had to query a resource that was not supported. I diligently submitted a pull request to add a get_idp method, which took three months to be merged, and a bit more to be released. This doesn't mean the maintainers are incompetent (they are great!), just that PRs in significant open-source projects take time.

Everything proceeded smoothly until I encountered another wall: python-keycloak implements none of the endpoints related to TOTP. I had three choices:

  1. Submit PR(s) and postpone everything for months (slow),

  2. Use raw HTTP requests instead of python-keycloak in part of the code (ugly),

  3. Create a library that supports ALL the endpoints, so I will never be in this position again (fun!).

It took a weekend for the code, and a few more for the publishing, documentation, and all the other ornaments of an open-source project. But there it is!

Getting started with mantelo

Mantelo is hosted on GitHub, available on PyPi, and hosts its documentation at readthedocs: https://mantelo.readthedocs.io/en/latest/.

๐Ÿ Installing

To no one's surprise:

pip install mantelo
Enter fullscreen mode Exit fullscreen mode

๐Ÿ” Authenticating

The first step is to create a KeycloakAdmin and specify a way of authenticating. Mantelo supports natively username/password and client credentials (OpenID), taking care of tokens and refreshes behind the scenes.

More details are available in the docs, but for the demo let's use the default admin user with the default admin-cli client and connect to the master realm running on a local Keycloak instance:

from mantelo import KeycloakAdmin

adm = KeycloakAdmin.from_username_password(
    # base Keycloak URL
    server_url="http://localhost:8080",
    # realm to interact with (and authenticate to if not specified otherwise)
    realm_name="master",
    # Client to use for authentication
    client_id="admin-cli",
    # User to use for authentication
    username="admin",
    # Password to use for authentication
    password="CHANGE-ME",
)
Enter fullscreen mode Exit fullscreen mode

โ„น This code doesn't interact with Keycloak: Mantelo is lazy and only fetches/refreshes the token when needed (e.g. on the first request).

๐Ÿ“ž Making calls

You are now ready to interact with the Admin API. One advantage of mantelo is that it doesn't provide any specific method, it only offers an object-oriented interface to the RESTful API. It ensues any endpoint present in the Admin API is callable using the adm object.

First, let's look at an example. Let's say you need to manage users. Here is a sample of what you can do:

# Get the list of users
# โฎ• GET /admin/realms/{realm}/users
adm.users.get()
# Search users for "foo bar"
# โฎ• GET /admin/realms/{realm}/users?search=foo+bar
adm.users.get(search="foo bar")
# Get user count
# โฎ• GET /admin/realms/{realm}/users/count
adm.users.count.get()
# Get a specific user by ID
# โฎ• GET /admin/realms/{realm}/users/725209cd-9076-417b-a404-149a3fb8e35b
adm.users("725209cd-9076-417b-a404-149a3fb8e35b").get()
# Create a new user, with credentials
# โฎ• POST /admin/realms/{realm}/users
adm.users.post({
    "username": "example",
    "firstName": "Winston",
    "lastName": "Smith",
    "email": "orwell@1984.uk",
    "emailVerified": True,
    "enabled": True,
    "credentials": [
        {
            "type": "password",
            "value": "juliaMyLove",
        }
    ],
})
Enter fullscreen mode Exit fullscreen mode

Did you get it? Instead of implementing methods to reach endpoints, mantelo uses simple rules to translate Python code to HTTP calls. More formally:

  1. Each property is a path appended to the base URL. If the server_url is https://localhost:8080 and the realm is master, the base URL is https://localhost:8080/admin/realms/master. Underscores are automatically translated to dashes.

  2. The same goes for methods, useful for specifying path parameters: the first argument is appended to the path.

  3. The actual HTTP call is triggered by ending a "chain" with a call to one of:

    • .get(**kwargs)
    • .options(**kwargs)
    • .head(**kwargs)
    • .post(data=None, files=None, **kwargs)
    • .patch(data=None, files=None, **kwargs)
    • .put(data=None, files=None, **kwargs)
    • .delete(**kwargs)
  4. All the above methods have