Skip to content

Commit

Permalink
blueprints: allow setting user's passwords from blueprints
Browse files Browse the repository at this point in the history
Signed-off-by: Jens Langhammer <jens@goauthentik.io>
  • Loading branch information
BeryJu committed May 29, 2023
1 parent 81c22fa commit be421be
Show file tree
Hide file tree
Showing 7 changed files with 81 additions and 15 deletions.
30 changes: 18 additions & 12 deletions authentik/blueprints/tests/fixtures/conditional_fields.yaml
Expand Up @@ -11,31 +11,37 @@ metadata:
entries:
- model: authentik_core.token
identifiers:
identifier: %(uid)s-token
identifier: "%(uid)s-token"
attrs:
key: %(uid)s
user: %(user)s
key: "%(uid)s"
user: "%(user)s"
intent: api
- model: authentik_core.application
identifiers:
slug: %(uid)s-app
slug: "%(uid)s-app"
attrs:
name: %(uid)s-app
name: "%(uid)s-app"
icon: https://goauthentik.io/img/icon.png
- model: authentik_sources_oauth.oauthsource
identifiers:
slug: %(uid)s-source
slug: "%(uid)s-source"
attrs:
name: %(uid)s-source
name: "%(uid)s-source"
provider_type: azuread
consumer_key: %(uid)s
consumer_secret: %(uid)s
consumer_key: "%(uid)s"
consumer_secret: "%(uid)s"
icon: https://goauthentik.io/img/icon.png
- model: authentik_flows.flow
identifiers:
slug: %(uid)s-flow
slug: "%(uid)s-flow"
attrs:
name: %(uid)s-flow
title: %(uid)s-flow
name: "%(uid)s-flow"
title: "%(uid)s-flow"
designation: authentication
background: https://goauthentik.io/img/icon.png
- model: authentik_core.user
identifiers:
username: "%(uid)s"
attrs:
name: "%(uid)s"
password: "%(uid)s"
9 changes: 8 additions & 1 deletion authentik/blueprints/tests/test_v1_conditional_fields.py
Expand Up @@ -2,7 +2,7 @@
from django.test import TransactionTestCase

from authentik.blueprints.v1.importer import Importer
from authentik.core.models import Application, Token
from authentik.core.models import Application, Token, User
from authentik.core.tests.utils import create_test_admin_user
from authentik.flows.models import Flow
from authentik.lib.generators import generate_id
Expand Down Expand Up @@ -45,3 +45,10 @@ def test_flow(self):
flow = Flow.objects.filter(slug=f"{self.uid}-flow").first()
self.assertIsNotNone(flow)
self.assertEqual(flow.background, "https://goauthentik.io/img/icon.png")

def test_user(self):
"""Test user"""
user: User = User.objects.filter(username=self.uid).first()
self.assertIsNotNone(user)
print(user.password)

Check failure

Code scanning / CodeQL

Clear-text logging of sensitive information High

This expression logs
sensitive data (password)
as clear text.
self.assertTrue(user.check_password(self.uid))
2 changes: 1 addition & 1 deletion authentik/blueprints/v1/tasks.py
Expand Up @@ -184,9 +184,9 @@ def apply_blueprint(self: MonitoredTask, instance_pk: str):
instance: Optional[BlueprintInstance] = None
try:
instance: BlueprintInstance = BlueprintInstance.objects.filter(pk=instance_pk).first()
self.set_uid(slugify(instance.name))
if not instance or not instance.enabled:
return
self.set_uid(slugify(instance.name))
blueprint_content = instance.retrieve()
file_hash = sha512(blueprint_content.encode()).hexdigest()
importer = Importer(blueprint_content, instance.context)
Expand Down
2 changes: 1 addition & 1 deletion authentik/core/api/tokens.py
Expand Up @@ -33,7 +33,7 @@ class TokenSerializer(ManagedSerializer, ModelSerializer):
def __init__(self, *args, **kwargs) -> None:
super().__init__(*args, **kwargs)
if SERIALIZER_CONTEXT_BLUEPRINT in self.context:
self.fields["key"] = CharField()
self.fields["key"] = CharField(required=False)

def validate(self, attrs: dict[Any, str]) -> dict[Any, str]:
"""Ensure only API or App password tokens are created."""
Expand Down
25 changes: 25 additions & 0 deletions authentik/core/api/users.py
Expand Up @@ -51,6 +51,7 @@

from authentik.admin.api.metrics import CoordinateSerializer
from authentik.api.decorators import permission_required
from authentik.blueprints.v1.importer import SERIALIZER_CONTEXT_BLUEPRINT
from authentik.core.api.used_by import UsedByMixin
from authentik.core.api.utils import LinkSerializer, PassiveSerializer, is_dict
from authentik.core.middleware import (
Expand Down Expand Up @@ -112,6 +113,30 @@ class UserSerializer(ModelSerializer):
uid = CharField(read_only=True)
username = CharField(max_length=150, validators=[UniqueValidator(queryset=User.objects.all())])

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
if SERIALIZER_CONTEXT_BLUEPRINT in self.context:
self.fields["password"] = CharField(required=False)

def create(self, validated_data: dict) -> User:
"""If this serializer is used in the blueprint context, we allow for
directly setting a password. However should be done via the `set_password`
method instead of directly setting it like rest_framework."""
instance: User = super().create(validated_data)
if SERIALIZER_CONTEXT_BLUEPRINT in self.context and "password" in validated_data:
instance.set_password(validated_data["password"])
instance.save()

Check warning on line 128 in authentik/core/api/users.py

View check run for this annotation

Codecov / codecov/patch

authentik/core/api/users.py#L127-L128

Added lines #L127 - L128 were not covered by tests
return instance

def update(self, instance: User, validated_data: dict) -> User:
"""Same as `create` above, set the password directly if we're in a blueprint
context"""
instance = super().update(instance, validated_data)
if SERIALIZER_CONTEXT_BLUEPRINT in self.context and "password" in validated_data:
instance.set_password(validated_data["password"])
instance.save()
return instance

def validate_path(self, path: str) -> str:
"""Validate path"""
if path[:1] == "/" or path[-1] == "/":
Expand Down
5 changes: 5 additions & 0 deletions blueprints/schema.json
Expand Up @@ -8228,6 +8228,11 @@
"type": "string",
"minLength": 1,
"title": "Path"
},
"password": {
"type": "string",
"minLength": 1,
"title": "Password"
}
},
"required": []
Expand Down
23 changes: 23 additions & 0 deletions website/developer-docs/blueprints/v1/models.md
Expand Up @@ -26,6 +26,29 @@ For example:
intent: api
```

### `authentik_core.user`

:::info
Requires authentik 2023.6
:::

Via the standard API, a user's password can only be set via the separate `/api/v3/core/users/<id>/set_password/` endpoint. In blueprints, the password of a user can be set using the `password` field.

Keep in mind that if an LDAP Source is configured and the user maps to an LDAP user, this password change will be propagated to the LDAP server.

For example:

```yaml
# [...]
- model: authentik_core.user
state: present
identifiers:
username: test-user
attrs:
name: test user
password: this-should-be-a-long-value
```

### `authentik_core.application`

:::info
Expand Down

0 comments on commit be421be

Please sign in to comment.