This commit is contained in:
2026-10-29 11:51:44 -04:00
parent 27a6305b22
commit e4be5bfdf4
10 changed files with 432 additions and 71 deletions
@@ -0,0 +1,38 @@
from django.conf import settings
from django.core.exceptions import ValidationError
from django.core.management.base import BaseCommand, CommandError
from django.db import IntegrityError, transaction
from members.models import Account
class Command(BaseCommand):
help = "Explicitly bind an unlinked local account to an independently verified Dex subject."
def add_arguments(self, parser):
parser.add_argument("--account", required=True, help="Local account UUID, not an email address.")
parser.add_argument("--uid", required=True, help="Independently verified LDAP connector uid.")
parser.add_argument("--subject", required=True, help="Exact verified Dex sub for the configured issuer/connector.")
def handle(self, *args, **options):
subject = options["subject"]
if not subject or len(subject) > 512:
raise CommandError("A nonempty Dex subject of at most 512 characters is required.")
try:
with transaction.atomic():
account = Account.objects.select_for_update().get(pk=options["account"])
if account.uid != options["uid"]:
raise CommandError("The verified uid does not match the selected account.")
if account.oidc_sub:
if account.oidc_sub == subject and account.oidc_issuer == settings.DEX_ISSUER:
self.stdout.write("Dex identity already bound.")
return
raise CommandError("Account already has a Dex binding; automatic rebinding is forbidden.")
account.oidc_issuer = settings.DEX_ISSUER
account.oidc_sub = subject
account.save(update_fields=["oidc_issuer", "oidc_sub"])
except (Account.DoesNotExist, ValidationError, ValueError) as exc:
raise CommandError("Select an existing account by UUID.") from exc
except IntegrityError:
raise CommandError("Dex subject is already bound to another account.") from None
self.stdout.write(self.style.SUCCESS("Dex identity bound. No LDAP or billing data changed."))