new pages for applications
This commit is contained in:
parent
99ef4287bd
commit
19d5742563
11 changed files with 297 additions and 25 deletions
|
|
@ -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']
|
||||
|
|
|
|||
18
apps/gooyal_oauth2/migrations/0002_alter_application_name.py
Normal file
18
apps/gooyal_oauth2/migrations/0002_alter_application_name.py
Normal 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),
|
||||
),
|
||||
]
|
||||
|
|
@ -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={
|
||||
|
|
|
|||
|
|
@ -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 = [
|
||||
|
|
|
|||
61
apps/gooyal_oauth2/views/pages.py
Normal file
61
apps/gooyal_oauth2/views/pages.py
Normal 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)
|
||||
23
templates/gooyal_oauth2/application_create.html
Normal file
23
templates/gooyal_oauth2/application_create.html
Normal 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 %}
|
||||
12
templates/gooyal_oauth2/application_detail.html
Normal file
12
templates/gooyal_oauth2/application_detail.html
Normal 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 %}
|
||||
57
templates/gooyal_oauth2/application_list.html
Normal file
57
templates/gooyal_oauth2/application_list.html
Normal 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 %}
|
||||
33
templates/include/filter_action.html
Normal file
33
templates/include/filter_action.html
Normal 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>
|
||||
35
templates/include/paginator.html
Normal file
35
templates/include/paginator.html
Normal 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>#}
|
||||
|
|
@ -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">
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue