Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

blueprints: allow setting of token key in blueprint context #4995

Merged
merged 1 commit into from Mar 17, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -19,10 +19,8 @@ def handle(self, *args, **options):
for blueprint_path in options.get("blueprints", []):
content = BlueprintInstance(path=blueprint_path).retrieve()
importer = Importer(content)
valid, logs = importer.validate()
valid, _ = importer.validate()
if not valid:
for log in logs:
getattr(LOGGER, log.pop("log_level"))(**log)
self.stderr.write("blueprint invalid")
sys_exit(1)
importer.apply()
Expand Down
18 changes: 16 additions & 2 deletions authentik/blueprints/v1/importer.py
Expand Up @@ -40,6 +40,10 @@
from authentik.outposts.models import OutpostServiceConnection
from authentik.policies.models import Policy, PolicyBindingModel

# Context set when the serializer is created in a blueprint context
# Update website/developer-docs/blueprints/v1/models.md when used
SERIALIZER_CONTEXT_BLUEPRINT = "blueprint_entry"


def is_model_allowed(model: type[Model]) -> bool:
"""Check if model is allowed"""
Expand Down Expand Up @@ -158,7 +162,12 @@ def _validate_single(self, entry: BlueprintEntry) -> Optional[BaseSerializer]:
raise EntryInvalidError(f"Model {model} not allowed")
if issubclass(model, BaseMetaModel):
serializer_class: type[Serializer] = model.serializer()
serializer = serializer_class(data=entry.get_attrs(self.__import))
serializer = serializer_class(
data=entry.get_attrs(self.__import),
context={
SERIALIZER_CONTEXT_BLUEPRINT: entry,
},
)
try:
serializer.is_valid(raise_exception=True)
except ValidationError as exc:
Expand Down Expand Up @@ -217,7 +226,12 @@ def _validate_single(self, entry: BlueprintEntry) -> Optional[BaseSerializer]:
always_merger.merge(full_data, updated_identifiers)
serializer_kwargs["data"] = full_data

serializer: Serializer = model().serializer(**serializer_kwargs)
serializer: Serializer = model().serializer(
context={
SERIALIZER_CONTEXT_BLUEPRINT: entry,
},
**serializer_kwargs,
)
try:
serializer.is_valid(raise_exception=True)
except ValidationError as exc:
Expand Down
6 changes: 6 additions & 0 deletions authentik/core/api/tokens.py
Expand Up @@ -16,6 +16,7 @@
from authentik.api.authorization import OwnerSuperuserPermissions
from authentik.api.decorators import permission_required
from authentik.blueprints.api import ManagedSerializer
from authentik.blueprints.v1.importer import SERIALIZER_CONTEXT_BLUEPRINT
from authentik.core.api.used_by import UsedByMixin
from authentik.core.api.users import UserSerializer
from authentik.core.api.utils import PassiveSerializer
Expand All @@ -29,6 +30,11 @@ class TokenSerializer(ManagedSerializer, ModelSerializer):

user_obj = UserSerializer(required=False, source="user", read_only=True)

def __init__(self, *args, **kwargs) -> None:
super().__init__(*args, **kwargs)
if SERIALIZER_CONTEXT_BLUEPRINT in self.context:
self.fields["key"] = CharField()

def validate(self, attrs: dict[Any, str]) -> dict[Any, str]:
"""Ensure only API or App password tokens are created."""
request: Request = self.context.get("request")
Expand Down
2 changes: 1 addition & 1 deletion website/developer-docs/api/api.md
Expand Up @@ -18,7 +18,7 @@ When authenticating with a flow, you'll get an authenticated Session cookie, tha

### API Token

Superusers can create tokens to authenticate as any user with a static key, which can optionally be expiring and auto-rotate.
Users can create tokens to authenticate as any user with a static key, which can optionally be expiring and auto-rotate.

### JWT Token

Expand Down
27 changes: 27 additions & 0 deletions website/developer-docs/blueprints/v1/models.md
@@ -0,0 +1,27 @@
# Models

Some models behave differently and allow for access to different API fields when created via blueprint.

### `authentik_core.token`

:::info
Requires authentik 2023.4
:::

Via the standard API, a token's key cannot be changed, it can only be rotated. This is to ensure a high entropy in it's key, and to prevent insecure data from being used. However, when provisioning tokens via a blueprint, it may be required to set a token to an existing value.

With blueprints, the field `key` can be set, to set the token's key to any value.

For example:

```yaml
# [...]
- model: authentik_core.token
state: present
identifiers:
identifier: my-token
attrs:
key: this-should-be-a-long-value
user: !KeyOf my-user
intent: api
```
1 change: 1 addition & 0 deletions website/sidebarsDev.js
Expand Up @@ -16,6 +16,7 @@ module.exports = {
"blueprints/v1/structure",
"blueprints/v1/tags",
"blueprints/v1/example",
"blueprints/v1/models",
"blueprints/v1/meta",
],
},
Expand Down