Skip to content

Commit

Permalink
feat!: rename "ServiceAccount" to "ClientCredentials" and KeycloakAdm…
Browse files Browse the repository at this point in the history
…in class methods

The concept "client credentials" is more suited, as it matches
the grant_type and makes it clearer.
  • Loading branch information
derlin committed Mar 21, 2024
1 parent a6a775a commit df55100
Show file tree
Hide file tree
Showing 8 changed files with 34 additions and 28 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,4 @@ mantelo/version.py
.tox/
.mypy/
.venv/
__pycache__
29 changes: 17 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,14 +63,14 @@ Now, assuming you have a Keycloak Server running, what's left is to:
1. authenticate, see [🔐 Authenticate to Keycloak](#-authenticate-to-keycloak)
2. make calls, see [📡 Making calls](#-making-calls)

For a quick test-drive, use the [docker-compose.yml](docker-compose.yml) included
For a quick test drive, use the [docker-compose.yml](docker-compose.yml) included
in this repo and start a Keycloak server locally using `docker compose up`.
Open a Python REPL and type:

```python
from mantelo import KeycloakAdmin

c = KeycloakAdmin.from_credentials(
c = KeycloakAdmin.from_username_password(
server_url="http://localhost:9090",
realm_name="master",
client_id="admin-cli",
Expand Down Expand Up @@ -98,8 +98,8 @@ setup the admin user with the password `admin`.

## 🔐 Authenticate to Keycloak

To authenticate to Keycloak, you can either use a username+password, or a service account (client
ID+client secret).
To authenticate to Keycloak, you can either use a username+password, or client credentials (client
ID+client secret, also known as service account).

The library takes care of fetching a token the first time you need it and keeping it fresh. By
default, it tries to use the refresh token (if available) and always guarantees the token is valid
Expand All @@ -121,7 +121,7 @@ Here is how to connect to the default realm with the admin user and `admin-cli`
```python
from mantelo import KeycloakAdmin

client = KeycloakAdmin.from_credentials(
client = KeycloakAdmin.from_username_password(
server_url="http://localhost:8080", # base Keycloak URL
realm_name="master",
# ↓↓ Authentication
Expand All @@ -138,7 +138,7 @@ you want to query, use the argument `authentication_realm`:
```python
from mantelo import KeycloakAdmin

client = KeycloakAdmin.from_credentials(
client = KeycloakAdmin.from_username_password(
server_url="http://localhost:8080", # base Keycloak URL
realm_name="my-realm", # realm for querying
# ↓↓ Authentication
Expand All @@ -149,7 +149,7 @@ client = KeycloakAdmin.from_credentials(
)
```

### Authenticating with a service account (client ID + secret)
### Authenticating with client credentials (client ID + secret)

To authenticate via a client, the latter needs:

Expand All @@ -164,7 +164,7 @@ Here is how to connect with a client:
```python
from mantelo import KeycloakAdmin

client = KeycloakAdmin.from_service_account(
client = KeycloakAdmin.from_client_credentials(
server_url="http://localhost:8080", # base Keycloak URL
realm_name="master",
# ↓↓ Authentication
Expand All @@ -180,7 +180,7 @@ you want to query, use the argument `authentication_realm`:
```python
from mantelo import KeycloakAdmin

client = KeycloakAdmin.from_service_account(
client = KeycloakAdmin.from_client_credentials(
server_url="http://localhost:8080", # base Keycloak URL
realm_name="my-realm", # realm for querying
# ↓↓ Authentication
Expand All @@ -202,7 +202,7 @@ an existing token directly (not recommended, as tokens are short-lived):
from mantelo.client import BearerAuth, KeycloakAdmin

KeycloakAdmin(
server_url="http://localhost:8080"
server_url="http://localhost:8080",
realm_name="master",
auth=BearerAuth(lambda: "my-token"),
)
Expand All @@ -213,11 +213,16 @@ If you want to go further, you can create your own `Connection` class (or extend

```python
from mantelo.client import BearerAuth, KeycloakAdmin
from mantelo.connection import Connection

connection = Connection(...)
class MyConnection(Connection):
def token(self):
return "<do-something-here>"

connection = MyConnection()

KeycloakAdmin(
server_url="http://localhost:8080"
server_url="http://localhost:8080",
realm_name="master",
auth=BearerAuth(connection.token),
)
Expand Down
8 changes: 4 additions & 4 deletions mantelo/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
from slumber.exceptions import SlumberHttpBaseException

from .connection import (
ClientCredentialsConnection,
OpenidConnection,
ServiceAccountConnection,
UsernamePasswordConnection,
)
from .exceptions import HttpException
Expand Down Expand Up @@ -85,7 +85,7 @@ def create(
)

@classmethod
def from_service_account(
def from_client_credentials(
cls,
server_url: str,
realm_name: str,
Expand All @@ -94,7 +94,7 @@ def from_service_account(
authentication_realm_name: str | None = None,
session: requests.Session | None = None,
):
openid_connection = ServiceAccountConnection(
openid_connection = ClientCredentialsConnection(
server_url=server_url,
realm_name=authentication_realm_name or realm_name,
client_id=client_id,
Expand All @@ -107,7 +107,7 @@ def from_service_account(
)

@classmethod
def from_credentials(
def from_username_password(
cls,
server_url: str,
realm_name: str,
Expand Down
2 changes: 1 addition & 1 deletion mantelo/connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ def _token_exchange_data(self) -> dict:


@define
class ServiceAccountConnection(OpenidConnection):
class ClientCredentialsConnection(OpenidConnection):
client_secret: str

def _token_exchange_data(self) -> dict:
Expand Down
Empty file added tests/__init__.py
Empty file.
4 changes: 2 additions & 2 deletions tests/conftest.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from mantelo.connection import (
UsernamePasswordConnection,
ServiceAccountConnection,
ClientCredentialsConnection,
)
from . import constants
import pytest
Expand All @@ -19,7 +19,7 @@ def openid_connection_password():

@pytest.fixture()
def openid_connection_sa():
return ServiceAccountConnection(
return ClientCredentialsConnection(
server_url=constants.TEST_SERVER_URL,
realm_name=constants.TEST_REALM,
client_id=constants.TEST_CLIENT_ID,
Expand Down
6 changes: 3 additions & 3 deletions tests/test_client.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ def test_create(openid_connection_sa):
def test_password_connection(with_custom_session):
session = requests.Session() if with_custom_session else None

adm = KeycloakAdmin.from_credentials(
adm = KeycloakAdmin.from_username_password(
server_url=constants.TEST_SERVER_URL,
realm_name=constants.TEST_REALM,
client_id=constants.ADMIN_CLI_CLIENT,
Expand All @@ -65,7 +65,7 @@ def test_password_connection(with_custom_session):

@pytest.mark.integration
def test_client_connection():
adm = KeycloakAdmin.from_service_account(
adm = KeycloakAdmin.from_client_credentials(
server_url=constants.TEST_SERVER_URL,
realm_name=constants.TEST_REALM,
client_id=constants.TEST_CLIENT_ID,
Expand All @@ -78,7 +78,7 @@ def test_client_connection():

@pytest.mark.integration
def test_different_auth_realm(openid_connection_admin):
adm = KeycloakAdmin.from_credentials(
adm = KeycloakAdmin.from_username_password(
server_url=openid_connection_admin.server_url,
realm_name=constants.TEST_REALM,
client_id=openid_connection_admin.client_id,
Expand Down
12 changes: 6 additions & 6 deletions tests/test_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from mantelo.connection import (
Token,
AuthenticationException,
ServiceAccountConnection,
ClientCredentialsConnection,
UsernamePasswordConnection,
)
from datetime import datetime, timedelta, timezone
Expand Down Expand Up @@ -123,8 +123,8 @@ def test_userpasswordconnection_init():
assert conn.refresh_timeout == timedelta(seconds=30)


def test_serviceaccountconnection_init():
conn = ServiceAccountConnection(
def test_ClientCredentialsconnection_init():
conn = ClientCredentialsConnection(
server_url="https://kc.test",
realm_name="test",
client_id="foo-client",
Expand Down Expand Up @@ -153,14 +153,14 @@ def test_openidconnection_default_values():
)

# Defaults
conn = ServiceAccountConnection(**args)
conn = ClientCredentialsConnection(**args)
assert isinstance(conn.session, requests.Session)
assert conn.refresh_timeout == timedelta(seconds=30)

# Override defaults
session = requests.Session()
timeout = timedelta(seconds=120)
conn = ServiceAccountConnection(
conn = ClientCredentialsConnection(
**args,
session=session,
refresh_timeout=timeout,
Expand All @@ -169,7 +169,7 @@ def test_openidconnection_default_values():
assert conn.refresh_timeout == timeout

# None values fallback to the default values
conn = ServiceAccountConnection(
conn = ClientCredentialsConnection(
**args,
session=None,
refresh_timeout=None,
Expand Down

0 comments on commit df55100

Please sign in to comment.