new pages for applications

This commit is contained in:
sahama 2024-08-03 16:52:09 +03:30
parent 99ef4287bd
commit 19d5742563
11 changed files with 297 additions and 25 deletions

View file

@ -5,6 +5,7 @@ Django forms for use with the gooyal-restrict-scopes package.
from django import forms
from oauth2_provider.scopes import get_scopes_backend
from .models import Application
class DelimitedListField(forms.MultipleChoiceField):
@ -35,12 +36,12 @@ class ApplicationForm(forms.ModelForm):
"""
Form for creating or updating a restricted application.
"""
#  allowed_scope is a space-delimited list, but we want to present
#  a selection of valid scopes with checkboxes
# allowed_scope is a space-delimited list, but we want to present
# a selection of valid scopes with checkboxes
allowed_scope = DelimitedListField(
label='Allowed scopes',
#  The choices and initial values are callables, because the scopes might
#  not be available at import type, e.g. if coming from the database
# The choices and initial values are callables, because the scopes might
# not be available at import type, e.g. if coming from the database
choices=lambda: get_scopes_backend().get_all_scopes().items(),
initial=lambda: get_scopes_backend().get_default_scopes(),
delimiter=' ',
@ -49,3 +50,30 @@ class ApplicationForm(forms.ModelForm):
class Meta:
exclude = ()
class ApplicationCreateForm(forms.ModelForm):
available_scopes_list = forms.CharField(widget=forms.Textarea(),required=False)
class Meta:
model = Application
fields = ['name',
'client_id',
'client_secret',
# 'available_scopes_list',
# 'authorization_grant_type',
'redirect_uris',
'post_logout_redirect_uris']
readonly_fields = ['client_id', 'client_secret']
class ApplicationUpdateForm(forms.ModelForm):
class Meta:
model = Application
fields = ['name',
'client_id',
'client_secret',
# 'authorization_grant_type',
'redirect_uris',
'post_logout_redirect_uris']
readonly_fields = ['client_id', 'client_secret']

View file

@ -0,0 +1,18 @@
# Generated by Django 5.0.6 on 2024-08-03 12:33
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('gooyal_oauth2', '0001_initial'),
]
operations = [
migrations.AlterField(
model_name='application',
name='name',
field=models.CharField(blank=True, max_length=255, unique=True),
),
]

View file

@ -30,7 +30,7 @@ class Application(AbstractApplication):
"""
id=None
uuid = models.UUIDField(primary_key=True, editable=False, default=uuid.uuid4, unique=True, db_index=True)
name = models.CharField(max_length=255, blank=False, unique=True)
user = models.ForeignKey(
settings.AUTH_USER_MODEL,
related_name="%(app_label)s_%(class)s",
@ -54,6 +54,10 @@ class Application(AbstractApplication):
app_scopes = set(self.allowed_scope.split())
return app_scopes.intersection(all_scopes)
# @property
# def available_scopes(self):
def allows_grant_type(self, *grant_types):
# Assume, for this example, that self.authorization_grant_type is set to self.GRANT_AUTHORIZATION_CODE
@ -72,13 +76,13 @@ class Scope(models.Model):
uuid = models.UUIDField(primary_key=True, editable=False, default=uuid.uuid4, unique=True, db_index=True)
application = models.ForeignKey(
oauth2_settings.APPLICATION_MODEL,
models.CASCADE,
models.PROTECT,
# This field is nullable because it is only set for scopes created by
# external resource servers, which have a corresponding OAuth application
# record on the authorisation server
blank=True, null=True,
help_text='The application to which the scope belongs.',
related_name='scopes'
related_name='available_scopes'
)
resource = models.ForeignKey(
Resource,
@ -132,11 +136,11 @@ class Scope(models.Model):
"""
endpoint = settings.RESOURCE_SERVER_REGISTER_SCOPE_URL
if endpoint:
#  If the endpoint is set, make the callout to the authz server
# If the endpoint is set, make the callout to the authz server
token = "Bearer {}".format(oauth2_settings.RESOURCE_SERVER_AUTH_TOKEN)
#  Let any failures bubble up
# Let any failures bubble up
# The idea is to call this method during deployment as a post-migrate
#  hook, so we want failures to halt the deployment
# hook, so we want failures to halt the deployment
response = requests.post(
endpoint,
json={

View file

@ -1,11 +1,12 @@
from django.urls import re_path
from django.urls import re_path, path
from oauth2_provider import views
from rest_framework import routers
from .views.introspect import IntrospectTokenView, IntrospectApplicationView
from .views import apis as api_views
app_name = "oauth2_provider"
from .views import pages
app_name = "gooyal_oauth2"
base_urlpatterns = [
@ -20,19 +21,19 @@ router = routers.SimpleRouter()
router.register(r'apis/applications', api_views.ApplicationViewSet, basename='application')
management_urlpatterns = [
# Application management views
re_path(r"^applications/$", views.ApplicationList.as_view(), name="list"),
re_path(r"^applications/register/$", views.ApplicationRegistration.as_view(), name="register"),
re_path(r"^applications/(?P<pk>[\w-]+)/$", views.ApplicationDetail.as_view(), name="detail"),
re_path(r"^applications/(?P<pk>[\w-]+)/delete/$", views.ApplicationDelete.as_view(), name="delete"),
re_path(r"^applications/(?P<pk>[\w-]+)/update/$", views.ApplicationUpdate.as_view(), name="update"),
# re_path(r"^applications/$", views.ApplicationList.as_view(), name="application_list"),
path("applications/", pages.ApplicationListView.as_view(), name="application_list"),
path('applications/register/', pages.ApplicationCreateView.as_view(), name='application_create'),
path("applications/<pk>/", pages.ApplicationDetailView.as_view(), name="application_detail"),
# re_path(r"^applications/(?P<pk>[\w-]+)/delete/$", views.ApplicationDelete.as_view(), name="delete"),
path("applications/<pk>/update/", pages.ApplicationUpdateView.as_view(), name="application_update"),
# Token management views
re_path(r"^authorized_tokens/$", views.AuthorizedTokensListView.as_view(), name="authorized-token-list"),
re_path(
r"^authorized_tokens/(?P<pk>[\w-]+)/delete/$",
views.AuthorizedTokenDeleteView.as_view(),
name="authorized-token-delete",
),
# re_path(r"^authorized_tokens/$", views.AuthorizedTokensListView.as_view(), name="authorized-token-list"),
# re_path(
# r"^authorized_tokens/(?P<pk>[\w-]+)/delete/$",
# views.AuthorizedTokenDeleteView.as_view(),
# name="authorized-token-delete",
# ),
]
oidc_urlpatterns = [

View file

@ -0,0 +1,61 @@
from django.contrib.auth.decorators import login_required
from django.urls import resolve, reverse_lazy
from django.utils.decorators import method_decorator
from django.views.generic import CreateView, DetailView, ListView, UpdateView
from oauth2_provider.models import get_application_model
from apps.gooyal_oauth2.forms import ApplicationCreateForm
from apps.gooyal_oauth2.models import Application
@method_decorator(login_required, name='dispatch')
class ApplicationCreateView(CreateView):
template_name = "gooyal_oauth2/application_create.html"
form_class = ApplicationCreateForm
def get_success_url(self):
return reverse_lazy("gooyal_oauth2:application_detail", kwargs={"pk": self.object.pk})
def form_valid(self, form):
form.instance.user = self.request.user
form.instance.client_type = Application.CLIENT_PUBLIC
# form.instance.client_type = Application.CLIENT_PUBLIC
return super().form_valid(form)
@method_decorator(login_required, name='dispatch')
class ApplicationUpdateView(UpdateView):
template_name = "gooyal_oauth2/application_create.html"
form_class = ApplicationCreateForm
def get_queryset(self):
return Application.objects.filter(user=self.request.user)
def get_success_url(self):
return reverse_lazy("gooyal_oauth2:application_detail", kwargs={"pk": self.object.pk})
def form_valid(self, form):
# form.instance.user = self.request.user
# form.instance.client_type = Application.CLIENT_PUBLIC
return super().form_valid(form)
@method_decorator(login_required, name='dispatch')
class ApplicationDetailView(DetailView):
model = Application
template_name = "gooyal_oauth2/application_create.html"
def get_queryset(self):
return Application.objects.filter(user=self.request.user)
@method_decorator(login_required, name='dispatch')
class ApplicationListView(ListView):
model = Application
template_name = "gooyal_oauth2/application_list.html"
paginate_by = 20
def get_queryset(self):
return Application.objects.filter(user=self.request.user)

View file

@ -0,0 +1,23 @@
{% extends "base.html" %}
{% load static %}
{% load crispy_forms_tags %}
{% load jalali_tags %}
{% block title %}Application: create{% endblock %}
{% block page_title %}Application: create{% endblock %}
{% block content %}
<form action="{{ request.get_full_path }}" method="post" enctype="multipart/form-data" novalidate>
{% csrf_token %}
<div class="col-md-12 col-lg-12" id="formset">
{{ form|crispy }}
</div>
<div class="col-12">
<div class="grid">
<div class="d-flex justify-content-end">
<input class="btn btn-success ms-2" type="submit" value="ثبت" name="save"/>
</div>
</div>
</div>
</form>
{% endblock %}

View file

@ -0,0 +1,12 @@
{% extends "base.html" %}
{% load static %}
{% load crispy_forms_tags %}
{% load jalali_tags %}
{% block title %}Application: detail{% endblock %}
{% block page_title %}Application: detail{% endblock %}
{% block content %}
<p>application: {{ object.name }}!</p>
<p>client id: {{ object.client_id }}!</p>
{% endblock %}

View file

@ -0,0 +1,57 @@
{% extends "base.html" %}
{% load static %}
{% load jalali_tags %}
{% load crispy_forms_tags %}
{% block title %}Application: list{% endblock %}
{% block page_title %}Application: list{% endblock %}
{% block page_action %}
{% include "include/filter_action.html" %}
<div class="me-2">
<a href="{% url 'gooyal_oauth2:application_create' %}">
<div class="btn btn-success">
<span class="bi bi-info-circle-fill"></span>
</div>
</a>
</div>
{% endblock %}
{% block content %}
<table class="table">
<tr>
<th>name</th>
<th>client id</th>
<th>actions</th>
</tr>
{% for obj in object_list %}
<tr>
<td>{{ obj.name }}</td>
<td>{{ obj.client_id }}</td>
<td>
<div class="grid">
<div class="d-flex justify-content-end">
<div class="me-2">
<a href="{% url 'gooyal_oauth2:application_detail' pk=obj.pk %}">
<div class="btn btn-success">
<span class="bi bi-info-circle-fill"></span>
</div>
</a>
</div>
<div class="me-2">
<a href="{% url 'gooyal_oauth2:application_update' pk=obj.pk %}">
<div class="btn btn-success">
<span class="bi bi-info-circle-fill"></span>
</div>
</a>
</div>
</div>
</div>
</td>
</tr>
{% endfor %}
</table>
{% include 'include/paginator.html' %}
{% endblock %}

View file

@ -0,0 +1,33 @@
{% load crispy_forms_tags %}
<button type="button" class="btn btn-primary me-2" data-bs-toggle="modal" data-bs-target="#filterModal">
<span class="bi bi-funnel-fill"></span>
</button>
<!-- Modal -->
<div class="modal fade" id="filterModal" tabindex="-1" aria-labelledby="exampleModalLabel"
aria-hidden="true">
<div class="modal-dialog">
<form method="get">
<div class="modal-content">
<div class="modal-header">
<h5 class="modal-title" id="exampleModalLabel">محدود کردن نتایج</h5>
<button type="button" class="btn-close" data-bs-dismiss="modal"
aria-label="بستن"></button>
</div>
<div class="modal-body">
{{ filter.form | crispy }}
</div>
<div class="modal-footer">
{# <button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Close</button>#}
<input type="submit" class="btn btn-primary" value="اعمال">
{# <button type="button" class="btn btn-primary"></button>#}
</div>
</div>
</form>
</div>
</div>

View file

@ -0,0 +1,35 @@
{% load tags %}
<nav class="d-flex justify-content-center" aria-label="Page navigation example">
<ul class="pagination">
{% if is_paginated %}
{% get_proper_elided_page_range paginator page_obj.number as page_range %}
{% for i in page_range %}
{% if page_obj.number == i %}
<li class="page-item"><span class="page-link"><strong>( {{ i }} )</strong></span></li>
{% elif i == '…' %}
<li class="page-item"><span class="page-link">{{ i }}</span></li>
{% else %}
<li class="page-item"><a class="page-link" href="?page={{ i }}{% for fil, fil_value in filter.data.items %}{% if fil != 'page' %}&{{fil}}={{fil_value}}{% endif %}{% endfor %}">{{ i }}</a></li>
{% endif %}
{% endfor %}
{% endif %}
<li class="p-2"><span class="fw-bolder text-decoration-underline">{{ paginator.count }}</span> داده یافت شد.</li>
</ul>
</nav>
{# <nav aria-label="Page navigation example">#}
{# <ul class="pagination">#}
{# {% if is_paginated %}#}
{##}
{# <li class="page-item"><span class="page-link">{{ page_obj.number }} از {{ page_obj.paginator.num_pages }}</span></li>#}
{# <li class="page-item"><span class="page-link">{{ page_obj.number }} از {{ page_obj.paginator.num_pages }}</span></li>#}
{# {% elif i > page_obj.number|add:'-5' and i < page_obj.number|add:'5' %}#}
{# <li class="page-item"><a class="page-link" href="?page={{ i }}{{ query_string }}">{{ i }}</a>#}
{# </li>#}
{# {% endif %}#}
{##}
{# {% endif %}#}
{# </ul>#}
{# </nav>#}

View file

@ -16,7 +16,7 @@
</div>
<div class="col d-flex justify-content-center">
<div class="bi bi-file-spreadsheet p-1"></div>
<div class="p-1"><a class="text-decoration-none" href="{% url 'swagger-ui' %}">API's</a></div>
<div class="p-1"><a class="text-decoration-none" href="{% url 'gooyal_oauth2:application_list' %}">applications</a></div>
</div>
{% endif %}
<div class="col d-flex justify-content-end">