mirror of
https://github.com/retspen/webvirtcloud
synced 2026-11-11 12:46:49 +00:00
feat(accounts): API tokens for scripts and tools
The REST API took only the login session, so a script could not use it without a browser. A user now makes API tokens on the profile page (API Tokens tab): a name, and an expiry of 30, 90 or 365 days or none. The token is shown once, on a page that is not cached, and then kept only as a SHA-512 digest; the list shows its name, first characters, creation and expiry, and a token is deleted there. Tools send it as "Authorization: Token wvc_...", and act as its user, with that user's permissions. Making and deleting a token is logged, without the token. A password alone (HTTP Basic) still does not reach the API, as it would skip the OTP second factor: a token is made in a session that passed the login and its OTP. The token class comes first, so a refused request gets 401 with its challenge instead of 403. django-rest-knox 5.1.0 provides the tokens (accounts.ApiToken adds the name); settings.py takes "knox", KNOX_TOKEN_MODEL and REST_KNOX from the template.
This commit is contained in:
parent
ba51b3e4d6
commit
1c60696ea1
14 changed files with 290 additions and 13 deletions
|
|
@ -2,7 +2,7 @@ from appsettings.settings import app_settings
|
|||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.auth.forms import AuthenticationForm
|
||||
from django.core.validators import MaxLengthValidator
|
||||
from django.forms import EmailField, Form, ModelForm, ValidationError
|
||||
from django.forms import CharField, EmailField, Form, ModelForm, TypedChoiceField, ValidationError
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from .models import UserInstance, UserSSHKey
|
||||
|
|
@ -74,6 +74,14 @@ class UserSSHKeyForm(ModelForm):
|
|||
fields = ("keyname", "keypublic")
|
||||
|
||||
|
||||
class ApiTokenForm(Form):
|
||||
name = CharField(label=_("Name"), max_length=64, help_text=_("What uses it, such as a script or a host"))
|
||||
days = TypedChoiceField(
|
||||
label=_("Expires"), coerce=int, empty_value=None, required=False, initial=90,
|
||||
choices=[(30, _("in 30 days")), (90, _("in 90 days")), (365, _("in a year")), ("", _("never"))],
|
||||
)
|
||||
|
||||
|
||||
class EmailOTPForm(Form):
|
||||
email = EmailField(label=_("Email"))
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue