blueprints: allow setting of token key in blueprint context (#4995)
closes #4717 Signed-off-by: Jens Langhammer <jens@goauthentik.io>
This commit is contained in:
parent
1d5f399b61
commit
4d8d405e70
|
@ -19,10 +19,8 @@ class Command(BaseCommand):
|
||||||
for blueprint_path in options.get("blueprints", []):
|
for blueprint_path in options.get("blueprints", []):
|
||||||
content = BlueprintInstance(path=blueprint_path).retrieve()
|
content = BlueprintInstance(path=blueprint_path).retrieve()
|
||||||
importer = Importer(content)
|
importer = Importer(content)
|
||||||
valid, logs = importer.validate()
|
valid, _ = importer.validate()
|
||||||
if not valid:
|
if not valid:
|
||||||
for log in logs:
|
|
||||||
getattr(LOGGER, log.pop("log_level"))(**log)
|
|
||||||
self.stderr.write("blueprint invalid")
|
self.stderr.write("blueprint invalid")
|
||||||
sys_exit(1)
|
sys_exit(1)
|
||||||
importer.apply()
|
importer.apply()
|
||||||
|
|
|
@ -40,6 +40,10 @@ from authentik.lib.models import SerializerModel
|
||||||
from authentik.outposts.models import OutpostServiceConnection
|
from authentik.outposts.models import OutpostServiceConnection
|
||||||
from authentik.policies.models import Policy, PolicyBindingModel
|
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:
|
def is_model_allowed(model: type[Model]) -> bool:
|
||||||
"""Check if model is allowed"""
|
"""Check if model is allowed"""
|
||||||
|
@ -158,7 +162,12 @@ class Importer:
|
||||||
raise EntryInvalidError(f"Model {model} not allowed")
|
raise EntryInvalidError(f"Model {model} not allowed")
|
||||||
if issubclass(model, BaseMetaModel):
|
if issubclass(model, BaseMetaModel):
|
||||||
serializer_class: type[Serializer] = model.serializer()
|
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:
|
try:
|
||||||
serializer.is_valid(raise_exception=True)
|
serializer.is_valid(raise_exception=True)
|
||||||
except ValidationError as exc:
|
except ValidationError as exc:
|
||||||
|
@ -217,7 +226,12 @@ class Importer:
|
||||||
always_merger.merge(full_data, updated_identifiers)
|
always_merger.merge(full_data, updated_identifiers)
|
||||||
serializer_kwargs["data"] = full_data
|
serializer_kwargs["data"] = full_data
|
||||||
|
|
||||||
serializer: Serializer = model().serializer(**serializer_kwargs)
|
serializer: Serializer = model().serializer(
|
||||||
|
context={
|
||||||
|
SERIALIZER_CONTEXT_BLUEPRINT: entry,
|
||||||
|
},
|
||||||
|
**serializer_kwargs,
|
||||||
|
)
|
||||||
try:
|
try:
|
||||||
serializer.is_valid(raise_exception=True)
|
serializer.is_valid(raise_exception=True)
|
||||||
except ValidationError as exc:
|
except ValidationError as exc:
|
||||||
|
|
|
@ -16,6 +16,7 @@ from rest_framework.viewsets import ModelViewSet
|
||||||
from authentik.api.authorization import OwnerSuperuserPermissions
|
from authentik.api.authorization import OwnerSuperuserPermissions
|
||||||
from authentik.api.decorators import permission_required
|
from authentik.api.decorators import permission_required
|
||||||
from authentik.blueprints.api import ManagedSerializer
|
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.used_by import UsedByMixin
|
||||||
from authentik.core.api.users import UserSerializer
|
from authentik.core.api.users import UserSerializer
|
||||||
from authentik.core.api.utils import PassiveSerializer
|
from authentik.core.api.utils import PassiveSerializer
|
||||||
|
@ -29,6 +30,11 @@ class TokenSerializer(ManagedSerializer, ModelSerializer):
|
||||||
|
|
||||||
user_obj = UserSerializer(required=False, source="user", read_only=True)
|
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]:
|
def validate(self, attrs: dict[Any, str]) -> dict[Any, str]:
|
||||||
"""Ensure only API or App password tokens are created."""
|
"""Ensure only API or App password tokens are created."""
|
||||||
request: Request = self.context.get("request")
|
request: Request = self.context.get("request")
|
||||||
|
|
|
@ -18,7 +18,7 @@ When authenticating with a flow, you'll get an authenticated Session cookie, tha
|
||||||
|
|
||||||
### API Token
|
### 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
|
### JWT Token
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
```
|
|
@ -16,6 +16,7 @@ module.exports = {
|
||||||
"blueprints/v1/structure",
|
"blueprints/v1/structure",
|
||||||
"blueprints/v1/tags",
|
"blueprints/v1/tags",
|
||||||
"blueprints/v1/example",
|
"blueprints/v1/example",
|
||||||
|
"blueprints/v1/models",
|
||||||
"blueprints/v1/meta",
|
"blueprints/v1/meta",
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|
Reference in New Issue