# Plane User Migration Plan

## Цель

Связать существующего пользователя Plane с Authentik identity без потери workspace, projects, tasks, comments, attachments и истории.

## Current baseline

Текущий рабочий Plane workspace:

```text
workspace slug: nodedc
workspace name: NODE DC
workspace owner: dcctouch@gmail.com
codex user: codex@nodedc.local
```

Перед реализацией OIDC нужно дополнительно снять точный snapshot:

- users;
- profiles;
- workspace memberships;
- project memberships;
- issue ownership;
- issue assignees;
- comments;
- attachments;
- uploaded files/storage.

## Backup before changes

Перед любыми изменениями Plane auth:

```text
plane_db dump
plane-app/plane.env
uploads / MinIO volumes
redis, если в нем есть важные очереди/сессии
rabbitmq, если есть невыполненные jobs
```

## Mapping model

Минимальная модель:

```text
external_identity_link
  id
  provider = authentik
  authentik_sub
  email
  plane_user_id
  created_at
  last_login_at
  status
```

Уникальность:

- `provider + authentik_sub`;
- `provider + plane_user_id`;
- желательно `provider + email`, если email используется для migration auto-link.

## Login flow

```text
Authentik login
  -> Plane OIDC callback
  -> validate state/nonce/token
  -> validate issuer/audience/exp/sub
  -> check nodedc:taskmanager:access
  -> find external_identity_link by authentik_sub
  -> login existing plane_user_id
```

Migration auto-link допускается только контролируемо:

```text
if no link and email matches existing Plane user and migration mode enabled:
  create external_identity_link
else:
  deny or provisioning flow
```

## Forbidden during migration

- Не пересоздавать существующего Plane user.
- Не менять `created_by`.
- Не менять `owner_id`.
- Не менять `assignee_id`.
- Не менять `member_id`.
- Не делать bulk update связей задач без отдельной проверки.

## Management command target

Целевая команда:

```bash
python manage.py link_authentik_user --email admin@example.ru --sub <authentik-sub>
```

Требования:

- idempotent;
- проверяет конфликтующий mapping;
- ничего не меняет в задачах/workspace;
- выводит summary;
- поддерживает dry-run.
