diff --git a/shynet/api/admin.py b/shynet/api/admin.py index 4b08f5a..d3c1eff 100644 --- a/shynet/api/admin.py +++ b/shynet/api/admin.py @@ -1,9 +1 @@ -from django.contrib import admin -from api.models import ApiToken - - -class ApiTokenAdmin(admin.ModelAdmin): - list_display = ("name", "user", "value") - - -admin.site.register(ApiToken, ApiTokenAdmin) +# from django.contrib import admin diff --git a/shynet/api/migrations/0001_initial.py b/shynet/api/migrations/0001_initial.py deleted file mode 100644 index b6ebdc0..0000000 --- a/shynet/api/migrations/0001_initial.py +++ /dev/null @@ -1,30 +0,0 @@ -# Generated by Django 3.2.5 on 2021-10-11 09:31 - -import api.models -from django.conf import settings -from django.db import migrations, models -import django.db.models.deletion - - -class Migration(migrations.Migration): - - initial = True - - dependencies = [ - migrations.swappable_dependency(settings.AUTH_USER_MODEL), - ] - - operations = [ - migrations.CreateModel( - name='ApiToken', - fields=[ - ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), - ('name', models.CharField(max_length=64)), - ('value', models.TextField(default=api.models._default_token_value, unique=True)), - ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='api_tokens', to=settings.AUTH_USER_MODEL)), - ], - options={ - 'ordering': ['name', 'value'], - }, - ), - ] diff --git a/shynet/api/mixins.py b/shynet/api/mixins.py index 380e009..47dc9a8 100644 --- a/shynet/api/mixins.py +++ b/shynet/api/mixins.py @@ -1,6 +1,7 @@ from django.http import JsonResponse from django.contrib.auth.models import AnonymousUser -from .models import ApiToken + +from core.models import User class ApiTokenRequiredMixin: @@ -10,11 +11,9 @@ class ApiTokenRequiredMixin: return AnonymousUser() token = token.split(' ')[1] - api_token = ApiToken.objects.filter(value=token).first() - if not api_token: - return AnonymousUser() + user = User.objects.filter(api_token=token).first() - return api_token.user + return user if user else AnonymousUser() def dispatch(self, request, *args, **kwargs): request.user = self._get_user_by_token(request) diff --git a/shynet/api/models.py b/shynet/api/models.py index 800f726..24e1689 100644 --- a/shynet/api/models.py +++ b/shynet/api/models.py @@ -1,19 +1 @@ -from django.db import models -from core.models import User -from secrets import token_urlsafe - - -def _default_token_value(): - return token_urlsafe(32) - - -class ApiToken(models.Model): - name = models.CharField(max_length=64) - value = models.TextField(default=_default_token_value, unique=True) - user = models.ForeignKey(User, on_delete=models.CASCADE, related_name="api_tokens") - - class Meta: - ordering = ["name", "value"] - - def __str__(self): - return self.name +# from django.db import models diff --git a/shynet/core/migrations/0009_auto_20211117_0217.py b/shynet/core/migrations/0009_auto_20211117_0217.py new file mode 100644 index 0000000..461cd1e --- /dev/null +++ b/shynet/core/migrations/0009_auto_20211117_0217.py @@ -0,0 +1,24 @@ +# Generated by Django 3.2.5 on 2021-11-17 07:17 + +import core.models +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('core', '0008_auto_20200628_1403'), + ] + + operations = [ + migrations.AddField( + model_name='user', + name='api_token', + field=models.TextField(default=core.models._default_api_token, unique=True), + ), + migrations.AlterField( + model_name='user', + name='id', + field=models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID'), + ), + ] diff --git a/shynet/core/models.py b/shynet/core/models.py index 9f78b00..875faba 100644 --- a/shynet/core/models.py +++ b/shynet/core/models.py @@ -1,8 +1,9 @@ import ipaddress -import json import re import uuid +from secrets import token_urlsafe + from django.apps import apps from django.conf import settings from django.contrib.auth.models import AbstractUser @@ -43,9 +44,14 @@ def _parse_network_list(networks: str): return [ipaddress.ip_network(network.strip()) for network in networks.split(",")] +def _default_api_token(): + return token_urlsafe(32) + + class User(AbstractUser): username = models.TextField(default=_default_uuid, unique=True) email = models.EmailField(unique=True) + api_token = models.TextField(default=_default_api_token, unique=True) def __str__(self): return self.email diff --git a/shynet/dashboard/templates/base.html b/shynet/dashboard/templates/base.html index f4d2f67..113d602 100644 --- a/shynet/dashboard/templates/base.html +++ b/shynet/dashboard/templates/base.html @@ -88,6 +88,9 @@ {% url 'account_set_password' as url %} {% include 'dashboard/includes/sidebar_portal.html' with label="Security" url=url %} + {% url 'dashboard:api_settings' as url %} + {% include 'dashboard/includes/sidebar_portal.html' with label="API" url=url %} + {% url 'account_logout' as url %} {% include 'dashboard/includes/sidebar_portal.html' with label="Sign Out" url=url %} diff --git a/shynet/dashboard/templates/dashboard/pages/api_settings.html b/shynet/dashboard/templates/dashboard/pages/api_settings.html new file mode 100644 index 0000000..fed6882 --- /dev/null +++ b/shynet/dashboard/templates/dashboard/pages/api_settings.html @@ -0,0 +1,22 @@ +{% extends "base.html" %} + +{% block content %} +
+

API

+
+
+
+

Token

+
+ + {{request.user.api_token}} + + + + +
+
+
+
+
+{% endblock %} diff --git a/shynet/dashboard/templates/dashboard/pages/service_update.html b/shynet/dashboard/templates/dashboard/pages/service_update.html index 097335f..90c56eb 100644 --- a/shynet/dashboard/templates/dashboard/pages/service_update.html +++ b/shynet/dashboard/templates/dashboard/pages/service_update.html @@ -30,5 +30,12 @@ +
+
+

API Token

+ + {{request.user.api_token}} + +
{% endblock %} diff --git a/shynet/dashboard/urls.py b/shynet/dashboard/urls.py index 328ce58..e107a13 100644 --- a/shynet/dashboard/urls.py +++ b/shynet/dashboard/urls.py @@ -1,6 +1,4 @@ -from django.contrib import admin -from django.urls import include, path -from django.views.generic import RedirectView +from django.urls import path from . import views @@ -28,4 +26,14 @@ urlpatterns = [ views.ServiceSessionView.as_view(), name="service_session", ), + path( + "api-settings/", + views.ApiSettingsView.as_view(), + name="api_settings", + ), + path( + "api-token-refresh/", + views.RefreshApiTokenView.as_view(), + name="api_token_refresh", + ), ] diff --git a/shynet/dashboard/views.py b/shynet/dashboard/views.py index 9ce02ce..96a59a8 100644 --- a/shynet/dashboard/views.py +++ b/shynet/dashboard/views.py @@ -3,8 +3,7 @@ from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.messages.views import SuccessMessageMixin from django.core.cache import cache from django.db.models import Q -from django.shortcuts import get_object_or_404, reverse -from django.utils import timezone +from django.shortcuts import get_object_or_404, reverse, redirect from django.views.generic import ( CreateView, DeleteView, @@ -12,11 +11,12 @@ from django.views.generic import ( ListView, TemplateView, UpdateView, + View, ) from rules.contrib.views import PermissionRequiredMixin from analytics.models import Session -from core.models import Service +from core.models import Service, _default_api_token from .forms import ServiceForm from .mixins import DateRangeMixin @@ -155,3 +155,14 @@ class ServiceSessionView(LoginRequiredMixin, PermissionRequiredMixin, DetailView data = super().get_context_data(**kwargs) data["object"] = get_object_or_404(Service, pk=self.kwargs.get("pk")) return data + + +class ApiSettingsView(LoginRequiredMixin, TemplateView): + template_name = "dashboard/pages/api_settings.html" + + +class RefreshApiTokenView(LoginRequiredMixin, View): + def get(self, request): + request.user.api_token = _default_api_token() + request.user.save() + return redirect('dashboard:api_settings')