feat: implement password setup link for tenant registration

When admin approves tenant registration:
- Owner account created in tenant schema (in addition to admin@localhost)
- Owner assigned 'owner' role with full permissions
- Password setup email sent with secure 7-day token link
- Owner sets password via link and auto-logs into their shop

Key changes:
- Added password_setup_token fields to TenantRegistration model
- Created tenants/services.py with formatted email service
- Modified _approve_registration to create owner account
- Added password_setup_confirm view with token validation
- Created password setup template and URL route
- Added admin action to resend password setup emails

Security:
- Token expires after 7 days
- Password not transmitted in email (secure setup link)
- Owner account inactive until password set
- Admin@localhost preserved for system administrator access

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2025-12-01 22:08:18 +03:00
parent 0ce854644e
commit fb4bcf37ec
7 changed files with 348 additions and 2 deletions

View File

@@ -0,0 +1,56 @@
{% extends 'base.html' %}
{% block title %}Установка пароля{% endblock %}
{% block content %}
<div class="container">
<div class="form-container">
<h2 class="text-center mb-4">Установка пароля</h2>
<!-- Приветственное сообщение -->
<div class="alert alert-info">
<strong>Добро пожаловать!</strong> Ваш магазин <strong>{{ tenant.name }}</strong> активирован.
<br>Установите пароль для входа в систему.
</div>
<div class="tab-content">
<div class="tab-pane fade show active" id="setup-password">
<form method="post">
{% csrf_token %}
{% include 'accounts/password_input.html' with field_name='password1' field_label='Пароль' required=True %}
{% include 'accounts/password_input.html' with field_name='password2' field_label='Подтверждение пароля' required=True %}
<button type="submit" class="btn btn-primary w-100">Установить пароль и войти</button>
</form>
<!-- Информация -->
<div class="text-center mt-3">
<small class="text-muted">
После установки пароля вы автоматически войдете в свой магазин.
</small>
</div>
</div>
</div>
</div>
</div>
<script>
// Добавляем обработчик для показа/скрытия пароля
document.querySelectorAll('.show-password-btn').forEach(button => {
button.addEventListener('click', function() {
const targetId = this.getAttribute('data-target');
const targetInput = document.getElementById(targetId);
const icon = this.querySelector('i');
if (targetInput.type === 'password') {
targetInput.type = 'text';
icon.classList.remove('bi-eye');
icon.classList.add('bi-eye-slash');
} else {
targetInput.type = 'password';
icon.classList.remove('bi-eye-slash');
icon.classList.add('bi-eye');
}
});
});
</script>
{% endblock %}