diff --git a/authentik/core/migrations/0024_alter_token_identifier.py b/authentik/core/migrations/0024_alter_token_identifier.py new file mode 100644 index 000000000..e3e2200ae --- /dev/null +++ b/authentik/core/migrations/0024_alter_token_identifier.py @@ -0,0 +1,35 @@ +# Generated by Django 3.2.3 on 2021-06-03 09:33 + +from django.apps.registry import Apps +from django.db import migrations, models +from django.db.backends.base.schema import BaseDatabaseSchemaEditor +from django.db.models import Count + + +def fix_duplicates(apps: Apps, schema_editor: BaseDatabaseSchemaEditor): + db_alias = schema_editor.connection.alias + Token = apps.get_model("authentik_core", "token") + identifiers = ( + Token.objects.using(db_alias) + .values("identifier") + .annotate(identifier_count=Count("identifier")) + .filter(identifier_count__gt=1) + ) + for ident in identifiers: + Token.objects.using(db_alias).filter(identifier=ident["identifier"]).delete() + + +class Migration(migrations.Migration): + + dependencies = [ + ("authentik_core", "0023_alter_application_meta_launch_url"), + ] + + operations = [ + migrations.RunPython(fix_duplicates), + migrations.AlterField( + model_name="token", + name="identifier", + field=models.SlugField(max_length=255, unique=True), + ), + ] diff --git a/authentik/core/models.py b/authentik/core/models.py index be634de62..08881172e 100644 --- a/authentik/core/models.py +++ b/authentik/core/models.py @@ -404,7 +404,7 @@ class Token(ManagedModel, ExpiringModel): """Token used to authenticate the User for API Access or confirm another Stage like Email.""" token_uuid = models.UUIDField(primary_key=True, editable=False, default=uuid4) - identifier = models.SlugField(max_length=255) + identifier = models.SlugField(max_length=255, unique=True) key = models.TextField(default=default_token_key) intent = models.TextField( choices=TokenIntents.choices, default=TokenIntents.INTENT_VERIFICATION diff --git a/authentik/outposts/models.py b/authentik/outposts/models.py index c454ad1db..e24b90305 100644 --- a/authentik/outposts/models.py +++ b/authentik/outposts/models.py @@ -376,20 +376,25 @@ class Outpost(models.Model): @property def token(self) -> Token: """Get/create token for auto-generated user""" - token = Token.filter_not_expired( - user=self.user, + managed = f"goauthentik.io/outpost/{self.token_identifier}" + tokens = Token.filter_not_expired( + identifier=self.token_identifier, intent=TokenIntents.INTENT_API, - managed=f"goauthentik.io/outpost/{self.token_identifier}", + managed=managed, ) - if token.exists(): - return token.first() + if tokens.exists(): + token = tokens.first() + if not token.managed: + token.managed = managed + token.save() + return token return Token.objects.create( user=self.user, identifier=self.token_identifier, intent=TokenIntents.INTENT_API, description=f"Autogenerated by authentik for Outpost {self.name}", expiring=False, - managed=f"goauthentik.io/outpost/{self.token_identifier}", + managed=managed, ) def get_required_objects(self) -> Iterable[Union[models.Model, str]]: