From 152603cf9de4cfd643ce3a3ada67cfe81f2dcc86 Mon Sep 17 00:00:00 2001 From: realaravinth Date: Mon, 6 Jun 2022 09:29:36 +0530 Subject: [PATCH] feat: optionally restrict create app to admins only --- dashboard/settings.py | 5 ++ ...ter_oauthintegration_client_secret_text.py | 25 ++++++++++ oauth/integrations/tests.py | 50 ++++++++++++++++--- oauth/integrations/views.py | 10 ++-- ...0009_alter_authorizationgrant_code_text.py | 27 ++++++++++ 5 files changed, 108 insertions(+), 9 deletions(-) create mode 100644 oauth/integrations/migrations/0009_alter_oauthintegration_client_secret_text.py create mode 100644 oauth/migrations/0009_alter_authorizationgrant_code_text.py diff --git a/dashboard/settings.py b/dashboard/settings.py index f1462a4..0a997f1 100644 --- a/dashboard/settings.py +++ b/dashboard/settings.py @@ -126,3 +126,8 @@ STATIC_URL = "static/" # https://docs.djangoproject.com/en/4.0/ref/settings/#default-auto-field DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" + + +### Dashbaord specific configuration options + +RESTRICT_NEW_INTEGRATION_INSTALLATION = True diff --git a/oauth/integrations/migrations/0009_alter_oauthintegration_client_secret_text.py b/oauth/integrations/migrations/0009_alter_oauthintegration_client_secret_text.py new file mode 100644 index 0000000..fd4c237 --- /dev/null +++ b/oauth/integrations/migrations/0009_alter_oauthintegration_client_secret_text.py @@ -0,0 +1,25 @@ +# Generated by Django 4.0.3 on 2022-06-06 03:45 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("integrations", "0008_alter_oauthintegration_client_secret_text_and_more"), + ] + + operations = [ + migrations.AlterField( + model_name="oauthintegration", + name="client_secret_text", + field=models.CharField( + blank=True, + default="driw0y2clz6sqnNFYE62Okl6NPW51XHt", + editable=False, + max_length=32, + unique=True, + verbose_name="client secret", + ), + ), + ] diff --git a/oauth/integrations/tests.py b/oauth/integrations/tests.py index f083d18..93e3b7f 100644 --- a/oauth/integrations/tests.py +++ b/oauth/integrations/tests.py @@ -12,13 +12,11 @@ # # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see . - - from django.contrib.auth import get_user_model from django.urls import reverse -from django.test import TestCase, Client +from django.test import TestCase, Client, override_settings -from .views import CREATE_APP_CTX, new_app +from .views import CREATE_APP_CTX class CreateNewAppTests(TestCase): @@ -34,6 +32,12 @@ class CreateNewAppTests(TestCase): password=self.password, ) + self.superuser = get_user_model().objects.create_superuser( + username="create_new_app_tests_superuser", + email="create_new_app_tests_superuser@example.org", + password=self.password, + ) + def test_create_new_app_unauthenticated_user(self): """ Tests if new_app is accessible only when user is authenticated @@ -41,13 +45,24 @@ class CreateNewAppTests(TestCase): resp = self.client.get(reverse("oauth.integrations.new_app")) self.assertEqual(resp.status_code, 302) + def test_view_is_restricted_to_super_user(self): + """ + Tests if view is only accessible from superuser accounts + """ + c = Client() + c.login(username=self.user.username, password=self.password) + c.session.save() + + resp = c.get(reverse("oauth.integrations.new_app")) + self.assertEqual(resp.status_code, 404) + def test_create_new_app_renders(self): """ Tests new_app template render """ c = Client() - c.login(username=self.user.username, password=self.password) + c.login(username=self.superuser.username, password=self.password) c.session.save() resp = c.get(reverse("oauth.integrations.new_app")) @@ -64,7 +79,30 @@ class CreateNewAppTests(TestCase): "redirect_uri": "https://test_new_app_submission.example.org", } c = Client() - c.login(username=self.user.username, password=self.password) + c.login(username=self.superuser.username, password=self.password) c.session.save() resp = c.post(reverse("oauth.integrations.new_app"), payload) self.assertEqual(resp.status_code, 200) + + def test_method_unavailable(self): + """ + Test new_app using unsupported HTTP method + """ + c = Client() + c.login(username=self.superuser.username, password=self.password) + c.session.save() + + resp = c.head(reverse("oauth.integrations.new_app")) + self.assertEqual(resp.status_code, 404) + + @override_settings(RESTRICT_NEW_INTEGRATION_INSTALLATION=False) + def test_unrestricted_app_creation(self): + """ + Test new_app using unsupported HTTP method + """ + c = Client() + c.login(username=self.user.username, password=self.password) + c.session.save() + + resp = c.get(reverse("oauth.integrations.new_app")) + self.assertEqual(resp.status_code, 200) diff --git a/oauth/integrations/views.py b/oauth/integrations/views.py index 80874a4..bc8204e 100644 --- a/oauth/integrations/views.py +++ b/oauth/integrations/views.py @@ -14,9 +14,10 @@ # along with this program. If not, see . from django.shortcuts import render -from django.http import HttpResponse, Http404 +from django.http import HttpResponse, HttpResponseNotFound from django.contrib.auth import authenticate from django.contrib.auth.decorators import login_required +from django.conf import settings from django.views.decorators.csrf import csrf_protect @@ -36,6 +37,10 @@ def new_app(request): """ Create new OAuth integration APP """ + if settings.RESTRICT_NEW_INTEGRATION_INSTALLATION: + if not request.user.is_superuser: + return HttpResponseNotFound("Page not Found") + if request.method == "GET": return render(request, "integrations/new.html", {"create_app": CREATE_APP_CTX}) @@ -48,8 +53,7 @@ def new_app(request): if "privacy_policy" in request.POST: app.privacy_policy_uri = request.POST["privacy_policy"] - print("OK") app.save() return HttpResponse("OK") - return Http404("Method not supported") + return HttpResponseNotFound("Method not supported") diff --git a/oauth/migrations/0009_alter_authorizationgrant_code_text.py b/oauth/migrations/0009_alter_authorizationgrant_code_text.py new file mode 100644 index 0000000..140a256 --- /dev/null +++ b/oauth/migrations/0009_alter_authorizationgrant_code_text.py @@ -0,0 +1,27 @@ +# Generated by Django 4.0.3 on 2022-06-06 03:45 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("oauth", "0008_alter_authorizationgrant_code_text"), + ] + + operations = [ + migrations.AlterField( + model_name="authorizationgrant", + name="code_text", + field=models.CharField( + blank=True, + default="CwLC9yHqvoQh9pHWQbQyqCqTEZlugZZa", + editable=False, + max_length=32, + primary_key=True, + serialize=False, + unique=True, + verbose_name="Authorization Code", + ), + ), + ]