mirror of
https://github.com/retspen/webvirtcloud
synced 2026-11-08 11:16:47 +00:00
fix(admin): setting a user's password deletes the user's API tokens
An administrator resets a password to close an account someone else took over, but the API tokens stayed valid: a stolen token kept working with the new password, for as long as it was made to last. Setting a password on the user's Password page now deletes that user's tokens, and the page says so. A user who changes their own password keeps their tokens (scripts keep working); they can delete them on their profile.
This commit is contained in:
parent
4f7bca9754
commit
c5d0672c68
8 changed files with 39 additions and 20 deletions
|
|
@ -810,7 +810,7 @@ The REST API (Django REST Framework, documented with `drf-spectacular`) takes th
|
|||
* **Swagger UI:** `http://<server>/swagger/`
|
||||
* **ReDoc UI:** `http://<server>/redoc/`
|
||||
* **OpenAPI 3.0 schema:** `http://<server>/api/schema/` (JSON or YAML)
|
||||
* **API tokens**, for scripts and tools: **Profile → API Tokens**. A token acts as its user and is shown once; send it as a header:
|
||||
* **API tokens**, for scripts and tools: **Profile → API Tokens**. A token acts as its user and is shown once; an administrator who sets the user's password deletes them. Send it as a header:
|
||||
|
||||
```bash
|
||||
curl -H "Authorization: Token wvc_..." https://<server>/api/v1/instances/
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@
|
|||
<div class="row g-3">
|
||||
<div class="col-lg-3">
|
||||
<h2 class="h6 fw-semibold mb-1 text-break">{{ edited_user.username }}</h2>
|
||||
<p class="small text-body-secondary mb-0">{% trans "How the user signs in." %}</p>
|
||||
<p class="small text-body-secondary mb-0">{% trans "How the user signs in." %} {% trans "Changing it deletes the user's API tokens." %}</p>
|
||||
</div>
|
||||
<div class="col-lg-9 form-section-fields">
|
||||
{% trans "Signs in with the password set here." as with_help %}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
user form says what its fields mean, and a password set by an administrator
|
||||
can be one the user must change."""
|
||||
|
||||
from accounts.models import UserAttributes
|
||||
from accounts.models import ApiToken, UserAttributes
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.messages import get_messages
|
||||
from django.test import TestCase
|
||||
|
|
@ -140,6 +140,15 @@ class UserPagesTests(TestCase):
|
|||
self.client.post(url, {"password1": "New-pw-4567y", "password2": "New-pw-4567y"})
|
||||
self.assertFalse(UserAttributes.objects.get(user=self.alice).must_change_password)
|
||||
|
||||
def test_a_password_set_by_an_administrator_ends_the_users_api_tokens(self):
|
||||
# a reset closes a taken-over account: a stolen token must not outlive it
|
||||
ApiToken.objects.create(self.alice, expiry=None, name="script")
|
||||
ApiToken.objects.create(self.admin, expiry=None, name="mine")
|
||||
url = reverse("admin:user_update_password", args=[self.alice.pk])
|
||||
self.client.post(url, {"password1": "New-pw-4567x", "password2": "New-pw-4567x"})
|
||||
self.assertFalse(ApiToken.objects.filter(user=self.alice).exists())
|
||||
self.assertTrue(ApiToken.objects.filter(user=self.admin).exists())
|
||||
|
||||
def test_a_user_without_a_password_cannot_be_made_to_change_it(self):
|
||||
# the change form asks for the old password: one they never had
|
||||
url = reverse("admin:user_update_password", args=[self.alice.pk])
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
from datetime import timedelta
|
||||
|
||||
from accounts.models import Instance, UserAttributes, UserInstance
|
||||
from accounts.models import ApiToken, Instance, UserAttributes, UserInstance
|
||||
from appsettings.settings import app_settings
|
||||
from django.conf import settings
|
||||
from django.contrib import messages
|
||||
|
|
@ -208,6 +208,8 @@ def user_update_password(request, pk):
|
|||
user = form.save()
|
||||
UserAttributes.objects.update_or_create(
|
||||
user=user, defaults={"must_change_password": form.cleaned_data["must_change_password"]})
|
||||
# a reset closes a taken-over account: its tokens would outlive the old password
|
||||
ApiToken.objects.filter(user=user).delete()
|
||||
update_session_auth_hash(request, user) # Important!
|
||||
messages.success(
|
||||
request, _("Password changed for %(user)s") % {"user": user.username}
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -913,16 +913,16 @@ msgstr "Sonuç"
|
|||
msgid "IP"
|
||||
msgstr "IP"
|
||||
|
||||
#: admin/templates/admin/logs.html:56 admin/views.py:286 instances/states.py:19
|
||||
#: admin/templates/admin/logs.html:56 admin/views.py:288 instances/states.py:19
|
||||
#: instances/views.py:419 instances/views.py:421 logs/views.py:17
|
||||
msgid "Unknown"
|
||||
msgstr "Bilinmeyen"
|
||||
|
||||
#: admin/templates/admin/logs.html:56 admin/views.py:286 logs/views.py:17
|
||||
#: admin/templates/admin/logs.html:56 admin/views.py:288 logs/views.py:17
|
||||
msgid "Done"
|
||||
msgstr "Tamamlandı"
|
||||
|
||||
#: admin/templates/admin/logs.html:56 admin/views.py:286 logs/views.py:17
|
||||
#: admin/templates/admin/logs.html:56 admin/views.py:288 logs/views.py:17
|
||||
#: operations/templates/operations/job.html:107
|
||||
msgid "Failed"
|
||||
msgstr "Başarısız"
|
||||
|
|
@ -1106,6 +1106,10 @@ msgstr "Engeli Kaldır"
|
|||
msgid "How the user signs in."
|
||||
msgstr "Kullanıcının nasıl oturum açtığı."
|
||||
|
||||
#: admin/templates/admin/user_password_form.html:19
|
||||
msgid "Changing it deletes the user's API tokens."
|
||||
msgstr "Değiştirmek kullanıcının API token'larını siler."
|
||||
|
||||
#: admin/templates/admin/user_password_form.html:22
|
||||
msgid "Signs in with the password set here."
|
||||
msgstr "Burada belirlenen parolayla oturum açar."
|
||||
|
|
@ -1138,27 +1142,27 @@ msgstr "Kullanıcı Oluştur"
|
|||
msgid "Update User"
|
||||
msgstr "Kullanıcı Güncelle"
|
||||
|
||||
#: admin/views.py:213
|
||||
#: admin/views.py:215
|
||||
#, python-format
|
||||
msgid "Password changed for %(user)s"
|
||||
msgstr "%(user)s parolası değiştirildi"
|
||||
|
||||
#: admin/views.py:237
|
||||
#: admin/views.py:239
|
||||
msgid "Their access to VMs, SSH keys and settings go; the VMs themselves stay."
|
||||
msgstr ""
|
||||
"Sanal makinelere erişimi, SSH anahtarları ve ayarları silinir; sanal "
|
||||
"makinelerin kendisi kalır."
|
||||
|
||||
#: admin/views.py:335
|
||||
#: admin/views.py:337
|
||||
msgid "deleted user"
|
||||
msgstr "silinmiş kullanıcı"
|
||||
|
||||
#: admin/views.py:346
|
||||
#: admin/views.py:348
|
||||
#, python-format
|
||||
msgid "Removed VM %(name)s deleted"
|
||||
msgstr "Kaldırılan sanal makine %(name)s silindi"
|
||||
|
||||
#: admin/views.py:353
|
||||
#: admin/views.py:355
|
||||
msgid "Its owners no longer get the VM back if its UUID shows up again."
|
||||
msgstr ""
|
||||
"UUID'si yeniden görünse bile sahipleri sanal makineyi artık geri almaz."
|
||||
|
|
|
|||
Binary file not shown.
|
|
@ -893,16 +893,16 @@ msgstr "结果"
|
|||
msgid "IP"
|
||||
msgstr "IP"
|
||||
|
||||
#: admin/templates/admin/logs.html:56 admin/views.py:286 instances/states.py:19
|
||||
#: admin/templates/admin/logs.html:56 admin/views.py:288 instances/states.py:19
|
||||
#: instances/views.py:419 instances/views.py:421 logs/views.py:17
|
||||
msgid "Unknown"
|
||||
msgstr "未知"
|
||||
|
||||
#: admin/templates/admin/logs.html:56 admin/views.py:286 logs/views.py:17
|
||||
#: admin/templates/admin/logs.html:56 admin/views.py:288 logs/views.py:17
|
||||
msgid "Done"
|
||||
msgstr "完成"
|
||||
|
||||
#: admin/templates/admin/logs.html:56 admin/views.py:286 logs/views.py:17
|
||||
#: admin/templates/admin/logs.html:56 admin/views.py:288 logs/views.py:17
|
||||
#: operations/templates/operations/job.html:107
|
||||
msgid "Failed"
|
||||
msgstr "失败"
|
||||
|
|
@ -1079,6 +1079,10 @@ msgstr "解除禁用"
|
|||
msgid "How the user signs in."
|
||||
msgstr "用户的登录方式。"
|
||||
|
||||
#: admin/templates/admin/user_password_form.html:19
|
||||
msgid "Changing it deletes the user's API tokens."
|
||||
msgstr "更改密码会删除该用户的 API 令牌。"
|
||||
|
||||
#: admin/templates/admin/user_password_form.html:22
|
||||
msgid "Signs in with the password set here."
|
||||
msgstr "使用此处设置的密码登录。"
|
||||
|
|
@ -1111,25 +1115,25 @@ msgstr "创建用户"
|
|||
msgid "Update User"
|
||||
msgstr "更新用户"
|
||||
|
||||
#: admin/views.py:213
|
||||
#: admin/views.py:215
|
||||
#, python-format
|
||||
msgid "Password changed for %(user)s"
|
||||
msgstr "已为 %(user)s 修改密码"
|
||||
|
||||
#: admin/views.py:237
|
||||
#: admin/views.py:239
|
||||
msgid "Their access to VMs, SSH keys and settings go; the VMs themselves stay."
|
||||
msgstr "其对虚拟机、SSH 密钥和设置的访问将被移除;虚拟机本身保留。"
|
||||
|
||||
#: admin/views.py:335
|
||||
#: admin/views.py:337
|
||||
msgid "deleted user"
|
||||
msgstr "已删除的用户"
|
||||
|
||||
#: admin/views.py:346
|
||||
#: admin/views.py:348
|
||||
#, python-format
|
||||
msgid "Removed VM %(name)s deleted"
|
||||
msgstr "已删除已移除的虚拟机 %(name)s"
|
||||
|
||||
#: admin/views.py:353
|
||||
#: admin/views.py:355
|
||||
msgid "Its owners no longer get the VM back if its UUID shows up again."
|
||||
msgstr "若其 UUID 再次出现,其所有者将不再恢复对该虚拟机的所有权。"
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue