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 ๐.
- The motivation behind mantelo
- Getting started with mantelo
- The best library out there, really?
- Mantelo needs YOU
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:
Submit PR(s) and postpone everything for months (slow),
Use raw HTTP requests instead of python-keycloak in part of the code (ugly),
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
๐ 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",
)
โน 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",
}
],
})
Did you get it? Instead of implementing methods to reach endpoints, mantelo uses simple rules to translate Python code to HTTP calls. More formally:
Each property is a path appended to the base URL. If the
server_url
ishttps://localhost:8080
and the realm ismaster
, the base URL ishttps://localhost:8080/admin/realms/master
. Underscores are automatically translated to dashes.The same goes for methods, useful for specifying path parameters: the first argument is appended to the path.
-
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)
-
All the above methods have
kwargs
which are appended as query parameters.POST/PUT/PATCH support
data
(a dictionary converted to JSON) orfiles
for specifying the body of the request.
Every request returns either the content of the response's body (parsed from JSON) or an instance of HttpException
if the response's status code is not in the 2XX
range.
And that's it! This magic is possible thanks to slumber, a library I will feature in a separate article (stay tuned ๐).
The best library out there, really?
I stated earlier I believe mantelo is the best Keycloak Admin library. Let me summarize the premises of such a bold claim:
mantelo's footprint is ridiculous: it is currently less than 1000 lines of code and has only 3 direct dependencies.
mantelo is the only library that can rightfully claim it supports the whole Keycloak Admin interface and is always up-to-date.
mantelo abstracts away authentication (and refresh tokens), which is always tricky to get right.
mantelo gives you access to the exact URL that was called (or the
requests.Response
in case of an error) so debugging is easy.mantelo is flexible: you can tweak it easily if you need to.
I am aware wrapping instead of implementing has a drawback: if Keycloak changes the Admin API, your code has to be updated (no encapsulation). But I believe it is a small price to pay, especially since Keycloak proved to be careful about retro-compatibility. What do you think?
Mantelo needs YOU
I've put a lot of work into this library, not just on the code, but everything around itโlike documentation, making it easy to test, setting up the GitHub repo, and more.
But mantelo isn't complete without users and a community. So please, give it a shot, star it, criticize it, open an issue... I'm excited to see where we can take it together!