Browse Source
feat: Async feature (#566)
feat: Async feature (#566)
* chore: add async client to connection * chore: add async client to keycloak openid * chore: add async client to keycloak uma * chore: add async client and methods to keycloak admin * chore: add async tests for connection and uma class * chore: add async tests for keycloak openid class * chore: add async tests for keycloak admin class * chore: update poetry lock * chore: update poetry lock * fix: poetry files * fix: lint issues * fix: conftest fix * fix: lint test fix * fix: lint test fix * fix: lint test fix * fix: lint test fix * fix: lint test fix * fix: added setuptools * fix: delete request fix and test cases fix * fix: email test case * fix: email test case for older versions * fix: set correct content type on token endpoint * fix: async on missing calls * test: updated tests * chore: deps * fix: preserve original bearer * fix: dont set bearer in refresh token directly * fix: default content type * fix: content type for initial access token * fix: content type for async initial access token * chore: add divergence test * chore: add divergence test for uma and conneciton class * chore: add docs for async module * fix: sphinx error fixes * test: verify signature * test: final divergence tests --------- Co-authored-by: Richard Nemeth <ryshoooo@gmail.com>pull/569/head v4.1.0
pyaiman
7 months ago
committed by
GitHub
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
15 changed files with 10090 additions and 116 deletions
-
3.gitignore
-
1docs/source/getting_started.rst
-
408docs/source/modules/async.rst
-
294poetry.lock
-
11pyproject.toml
-
108src/keycloak/connection.py
-
4139src/keycloak/keycloak_admin.py
-
722src/keycloak/keycloak_openid.py
-
374src/keycloak/keycloak_uma.py
-
130src/keycloak/openid_connection.py
-
59tests/test_connection.py
-
3123tests/test_keycloak_admin.py
-
513tests/test_keycloak_openid.py
-
316tests/test_keycloak_uma.py
-
1tox.env
@ -0,0 +1,408 @@ |
|||||
|
.. admin: |
||||
|
|
||||
|
Use Python Keycloak Asynchronously |
||||
|
================================== |
||||
|
|
||||
|
Asynchronous admin client |
||||
|
------------------------- |
||||
|
|
||||
|
Configure admin client |
||||
|
------------------------ |
||||
|
|
||||
|
.. code-block:: python |
||||
|
|
||||
|
|
||||
|
admin = KeycloakAdmin( |
||||
|
server_url="http://localhost:8080/", |
||||
|
username='example-admin', |
||||
|
password='secret', |
||||
|
realm_name="master", |
||||
|
user_realm_name="only_if_other_realm_than_master") |
||||
|
|
||||
|
|
||||
|
Configure admin client with connection |
||||
|
----------------------------------------- |
||||
|
|
||||
|
.. code-block:: python |
||||
|
|
||||
|
from keycloak import KeycloakAdmin |
||||
|
from keycloak import KeycloakOpenIDConnection |
||||
|
|
||||
|
keycloak_connection = KeycloakOpenIDConnection( |
||||
|
server_url="http://localhost:8080/", |
||||
|
username='example-admin', |
||||
|
password='secret', |
||||
|
realm_name="master", |
||||
|
user_realm_name="only_if_other_realm_than_master", |
||||
|
client_id="my_client", |
||||
|
client_secret_key="client-secret", |
||||
|
verify=True) |
||||
|
|
||||
|
keycloak_admin = KeycloakAdmin(connection=keycloak_connection) |
||||
|
|
||||
|
|
||||
|
Create user asynchronously |
||||
|
---------------------------- |
||||
|
|
||||
|
.. code-block:: python |
||||
|
|
||||
|
new_user = await keycloak_admin.a_create_user({"email": "example@example.com", |
||||
|
"username": "example@example.com", |
||||
|
"enabled": True, |
||||
|
"firstName": "Example", |
||||
|
"lastName": "Example"}) |
||||
|
|
||||
|
|
||||
|
Add user asynchronously and raise exception if username already exists |
||||
|
----------------------------------------------------------------------- |
||||
|
|
||||
|
The exist_ok currently defaults to True for backwards compatibility reasons. |
||||
|
|
||||
|
.. code-block:: python |
||||
|
|
||||
|
new_user = await keycloak_admin.a_create_user({"email": "example@example.com", |
||||
|
"username": "example@example.com", |
||||
|
"enabled": True, |
||||
|
"firstName": "Example", |
||||
|
"lastName": "Example"}, |
||||
|
exist_ok=False) |
||||
|
|
||||
|
Add user asynchronously and set password |
||||
|
---------------------------------------- |
||||
|
|
||||
|
.. code-block:: python |
||||
|
|
||||
|
new_user = await keycloak_admin.a_create_user({"email": "example@example.com", |
||||
|
"username": "example@example.com", |
||||
|
"enabled": True, |
||||
|
"firstName": "Example", |
||||
|
"lastName": "Example", |
||||
|
"credentials": [{"value": "secret","type": "password",}]}) |
||||
|
|
||||
|
|
||||
|
Add user asynchronous and specify a locale |
||||
|
------------------------------------------- |
||||
|
|
||||
|
.. code-block:: python |
||||
|
|
||||
|
new_user = await keycloak_admin.a_create_user({"email": "example@example.fr", |
||||
|
"username": "example@example.fr", |
||||
|
"enabled": True, |
||||
|
"firstName": "Example", |
||||
|
"lastName": "Example", |
||||
|
"attributes": { |
||||
|
"locale": ["fr"] |
||||
|
}}) |
||||
|
|
||||
|
Asynchronous User counter |
||||
|
------------------------------ |
||||
|
|
||||
|
.. code-block:: python |
||||
|
|
||||
|
count_users = await keycloak_admin.a_users_count() |
||||
|
|
||||
|
Get users Returns a list of users asynchronously, filtered according to query parameters |
||||
|
----------------------------------------------------------------------------------------- |
||||
|
|
||||
|
.. code-block:: python |
||||
|
|
||||
|
users = await keycloak_admin.a_get_users({}) |
||||
|
|
||||
|
Get user ID asynchronously from username |
||||
|
----------------------------------------- |
||||
|
|
||||
|
.. code-block:: python |
||||
|
|
||||
|
user_id_keycloak = await keycloak_admin.a_get_user_id("username-keycloak") |
||||
|
|
||||
|
|
||||
|
Get user asynchronously |
||||
|
------------------------------ |
||||
|
|
||||
|
.. code-block:: python |
||||
|
|
||||
|
user = await keycloak_admin.a_get_user("user-id-keycloak") |
||||
|
|
||||
|
Update user asynchronously |
||||
|
------------------------------ |
||||
|
|
||||
|
.. code-block:: python |
||||
|
|
||||
|
response = await keycloak_admin.a_update_user(user_id="user-id-keycloak", |
||||
|
payload={'firstName': 'Example Update'}) |
||||
|
|
||||
|
|
||||
|
Update user password asynchronously |
||||
|
------------------------------------ |
||||
|
|
||||
|
.. code-block:: python |
||||
|
|
||||
|
response = await keycloak_admin.a_set_user_password(user_id="user-id-keycloak", password="secret", temporary=True) |
||||
|
|
||||
|
|
||||
|
Get user credentials asynchronously |
||||
|
------------------------------------ |
||||
|
|
||||
|
.. code-block:: python |
||||
|
|
||||
|
credentials = await keycloak_admin.a_get_credentials(user_id='user_id') |
||||
|
|
||||
|
Get user credential asynchronously by ID |
||||
|
----------------------------------------- |
||||
|
|
||||
|
.. code-block:: python |
||||
|
|
||||
|
credential = await keycloak_admin.a_get_credential(user_id='user_id', credential_id='credential_id') |
||||
|
|
||||
|
Delete user credential asynchronously |
||||
|
--------------------------------------- |
||||
|
|
||||
|
.. code-block:: python |
||||
|
|
||||
|
response = await keycloak_admin.a_delete_credential(user_id='user_id', credential_id='credential_id') |
||||
|
|
||||
|
Delete User asynchronously |
||||
|
------------------------------ |
||||
|
|
||||
|
.. code-block:: python |
||||
|
|
||||
|
response = await keycloak_admin.a_delete_user(user_id="user-id-keycloak") |
||||
|
|
||||
|
Get consents granted asynchronously by the user |
||||
|
------------------------------------------------ |
||||
|
|
||||
|
.. code-block:: python |
||||
|
|
||||
|
consents = await keycloak_admin.a_consents_user(user_id="user-id-keycloak") |
||||
|
|
||||
|
Send user action asynchronously |
||||
|
--------------------------------- |
||||
|
|
||||
|
.. code-block:: python |
||||
|
|
||||
|
response = await keycloak_admin.a_send_update_account(user_id="user-id-keycloak", |
||||
|
payload=['UPDATE_PASSWORD']) |
||||
|
|
||||
|
Send verify email asynchronously |
||||
|
---------------------------------- |
||||
|
|
||||
|
.. code-block:: python |
||||
|
|
||||
|
response = await keycloak_admin.a_send_verify_email(user_id="user-id-keycloak") |
||||
|
|
||||
|
Get sessions associated asynchronously with the user |
||||
|
----------------------------------------------------- |
||||
|
|
||||
|
.. code-block:: python |
||||
|
|
||||
|
sessions = await keycloak_admin.a_get_sessions(user_id="user-id-keycloak") |
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
Asynchronous OpenID Client |
||||
|
=========================== |
||||
|
|
||||
|
Asynchronous Configure client OpenID |
||||
|
------------------------------------- |
||||
|
|
||||
|
.. code-block:: python |
||||
|
|
||||
|
from keycloak import KeycloakOpenID |
||||
|
|
||||
|
# Configure client |
||||
|
# For versions older than 18 /auth/ must be added at the end of the server_url. |
||||
|
keycloak_openid = KeycloakOpenID(server_url="http://localhost:8080/", |
||||
|
client_id="example_client", |
||||
|
realm_name="example_realm", |
||||
|
client_secret_key="secret") |
||||
|
|
||||
|
|
||||
|
Get .well_know asynchronously |
||||
|
------------------------------ |
||||
|
|
||||
|
.. code-block:: python |
||||
|
|
||||
|
config_well_known = await keycloak_openid.a_well_known() |
||||
|
|
||||
|
|
||||
|
Get code asynchronously with OAuth authorization request |
||||
|
--------------------------------------------------------- |
||||
|
|
||||
|
.. code-block:: python |
||||
|
|
||||
|
auth_url = await keycloak_openid.a_auth_url( |
||||
|
redirect_uri="your_call_back_url", |
||||
|
scope="email", |
||||
|
state="your_state_info") |
||||
|
|
||||
|
|
||||
|
Get access token asynchronously with code |
||||
|
---------------------------------------------- |
||||
|
|
||||
|
.. code-block:: python |
||||
|
|
||||
|
access_token = await keycloak_openid.a_token( |
||||
|
grant_type='authorization_code', |
||||
|
code='the_code_you_get_from_auth_url_callback', |
||||
|
redirect_uri="your_call_back_url") |
||||
|
|
||||
|
|
||||
|
Get access asynchronously token with user and password |
||||
|
------------------------------------------------------- |
||||
|
|
||||
|
.. code-block:: python |
||||
|
|
||||
|
token = await keycloak_openid.a_token("user", "password") |
||||
|
token = await keycloak_openid.a_token("user", "password", totp="012345") |
||||
|
|
||||
|
|
||||
|
Get token asynchronously using Token Exchange |
||||
|
---------------------------------------------- |
||||
|
|
||||
|
.. code-block:: python |
||||
|
|
||||
|
token = await keycloak_openid.a_exchange_token(token['access_token'], |
||||
|
"my_client", "other_client", "some_user") |
||||
|
|
||||
|
|
||||
|
Refresh token asynchronously |
||||
|
---------------------------------------------- |
||||
|
|
||||
|
.. code-block:: python |
||||
|
|
||||
|
token = await keycloak_openid.a_refresh_token(token['refresh_token']) |
||||
|
|
||||
|
Get UserInfo asynchronously |
||||
|
---------------------------------------------- |
||||
|
|
||||
|
.. code-block:: python |
||||
|
|
||||
|
userinfo = await keycloak_openid.a_userinfo(token['access_token']) |
||||
|
|
||||
|
Logout asynchronously |
||||
|
---------------------------------------------- |
||||
|
|
||||
|
.. code-block:: python |
||||
|
|
||||
|
await keycloak_openid.a_logout(token['refresh_token']) |
||||
|
|
||||
|
Get certs asynchronously |
||||
|
---------------------------------------------- |
||||
|
|
||||
|
.. code-block:: python |
||||
|
|
||||
|
certs = await keycloak_openid.a_certs() |
||||
|
|
||||
|
Introspect RPT asynchronously |
||||
|
---------------------------------------------- |
||||
|
|
||||
|
.. code-block:: python |
||||
|
|
||||
|
token_rpt_info = await keycloak_openid.a_introspect(await keycloak_openid.a_introspect(token['access_token'], |
||||
|
rpt=rpt['rpt'], |
||||
|
token_type_hint="requesting_party_token")) |
||||
|
|
||||
|
Introspect token asynchronously |
||||
|
---------------------------------------------- |
||||
|
|
||||
|
.. code-block:: python |
||||
|
|
||||
|
token_info = await keycloak_openid.a_introspect(token['access_token']) |
||||
|
|
||||
|
|
||||
|
Decode token asynchronously |
||||
|
---------------------------------------------- |
||||
|
|
||||
|
.. code-block:: python |
||||
|
|
||||
|
token_info = await keycloak_openid.a_decode_token(token['access_token']) |
||||
|
# Without validation |
||||
|
token_info = await keycloak_openid.a_decode_token(token['access_token'], validate=False) |
||||
|
|
||||
|
|
||||
|
Get UMA-permissions asynchronously by token |
||||
|
---------------------------------------------- |
||||
|
|
||||
|
.. code-block:: python |
||||
|
|
||||
|
token = await keycloak_openid.a_token("user", "password") |
||||
|
permissions = await keycloak_openid.a_uma_permissions(token['access_token']) |
||||
|
|
||||
|
Get UMA-permissions asynchronously by token with specific resource and scope requested |
||||
|
--------------------------------------------------------------------------------------- |
||||
|
|
||||
|
.. code-block:: python |
||||
|
|
||||
|
token = await keycloak_openid.a_token("user", "password") |
||||
|
permissions = await keycloak_openid.a_uma_permissions(token['access_token'], permissions="Resource#Scope") |
||||
|
|
||||
|
Get auth status asynchronously for a specific resource and scope by token |
||||
|
-------------------------------------------------------------------------- |
||||
|
|
||||
|
.. code-block:: python |
||||
|
|
||||
|
token = await keycloak_openid.a_token("user", "password") |
||||
|
auth_status = await keycloak_openid.a_has_uma_access(token['access_token'], "Resource#Scope") |
||||
|
|
||||
|
|
||||
|
|
||||
|
|
||||
|
Asynchronous UMA |
||||
|
======================== |
||||
|
|
||||
|
|
||||
|
Asynchronous Configure client UMA |
||||
|
---------------------------------- |
||||
|
|
||||
|
.. code-block:: python |
||||
|
|
||||
|
from keycloak import KeycloakOpenIDConnection |
||||
|
from keycloak import KeycloakUMA |
||||
|
|
||||
|
keycloak_connection = KeycloakOpenIDConnection( |
||||
|
server_url="http://localhost:8080/", |
||||
|
realm_name="master", |
||||
|
client_id="my_client", |
||||
|
client_secret_key="client-secret") |
||||
|
|
||||
|
keycloak_uma = KeycloakUMA(connection=keycloak_connection) |
||||
|
|
||||
|
|
||||
|
Create a resource set asynchronously |
||||
|
--------------------------------------- |
||||
|
|
||||
|
.. code-block:: python |
||||
|
|
||||
|
resource_set = await keycloak_uma.a_resource_set_create({ |
||||
|
"name": "example_resource", |
||||
|
"scopes": ["example:read", "example:write"], |
||||
|
"type": "urn:example"}) |
||||
|
|
||||
|
List resource sets asynchronously |
||||
|
---------------------------------- |
||||
|
|
||||
|
.. code-block:: python |
||||
|
|
||||
|
resource_sets = await uma.a_resource_set_list() |
||||
|
|
||||
|
Get resource set asynchronously |
||||
|
-------------------------------- |
||||
|
|
||||
|
.. code-block:: python |
||||
|
|
||||
|
latest_resource = await uma.a_resource_set_read(resource_set["_id"]) |
||||
|
|
||||
|
Update resource set asynchronously |
||||
|
------------------------------------- |
||||
|
|
||||
|
.. code-block:: python |
||||
|
|
||||
|
latest_resource["name"] = "New Resource Name" |
||||
|
await uma.a_resource_set_update(resource_set["_id"], latest_resource) |
||||
|
|
||||
|
Delete resource set asynchronously |
||||
|
------------------------------------ |
||||
|
.. code-block:: python |
||||
|
|
||||
|
await uma.a_resource_set_delete(resource_id=resource_set["_id"]) |
4139
src/keycloak/keycloak_admin.py
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
3123
tests/test_keycloak_admin.py
File diff suppressed because it is too large
View File
File diff suppressed because it is too large
View File
Write
Preview
Loading…
Cancel
Save
Reference in new issue