Compare commits

...

10 commits

Author SHA1 Message Date
Sayyid Hamid Mahdavi
709ee8c454 add application Preferences 2025-06-10 15:51:09 +03:30
Sayyid Hamid Mahdavi
53786a7a4a add application Preferences 2025-06-10 15:38:56 +03:30
Sayyid Hamid Mahdavi
4dca5cea43 add application Preferences 2025-06-10 15:38:45 +03:30
sahama
2e8f58968b "rest_framework.parsers.JSONParser", 2024-08-12 14:11:07 +03:30
sahama
0a735c1ebd get_filter_link 2024-08-08 00:32:26 +03:30
sahama
b14482e383 get_filter_link 2024-08-07 19:01:26 +03:30
sahama
957b2705f3 color 2024-08-07 18:42:04 +03:30
sahama
579e959b1b color 2024-08-07 18:34:49 +03:30
sahama
623d98de5b new oauth urls 2024-08-07 17:24:05 +03:30
mahdavi
2ba7a2e1e9 add application to data json 2024-07-29 17:22:56 +03:30
12 changed files with 98 additions and 23 deletions

View file

@ -27,4 +27,12 @@ def get_proper_elided_page_range(p, number, on_each_side=2, on_ends=2):
@register.filter
def get_item(dictionary, key):
return dictionary.get(key)
return dictionary.get(key)
@register.simple_tag
def get_filter_link(request, key, value):
qs: str = request.GET.urlencode()
qs_dict = request.GET.dict()
qs_dict.update({key: value})
return '&'.join(f'{k}={v}' for k, v in qs_dict.items())
# return 'page={{ i }}{% for fil, fil_value in filter.data.items %}{% if fil != 'page' %}&{{fil}}={{fil_value}}{% endif %}{% endfor %}">{{ i }}</a></li>

View file

@ -0,0 +1,26 @@
# Generated by Django 5.1.4 on 2025-06-10 12:07
import django.db.models.deletion
from django.conf import settings
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('crud', '0003_alter_plan_owner'),
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.AlterField(
model_name='data',
name='plan',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to='crud.plan'),
),
migrations.AlterField(
model_name='data',
name='user',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL),
),
]

View file

@ -12,9 +12,15 @@ class Plan(models.Model):
class Data(models.Model):
user = models.ForeignKey(User, on_delete=models.PROTECT)
plan = models.ForeignKey(Plan, on_delete=models.PROTECT)
user = models.ForeignKey(User, on_delete=models.PROTECT, null=True, blank=True)
plan = models.ForeignKey(Plan, on_delete=models.PROTECT, null=True, blank=True)
data = models.JSONField(default=dict)
@property
def application(self):
return str(self.plan.application.uuid)
class Meta:
unique_together = ('user', 'plan')

View file

@ -5,8 +5,7 @@ from .models import Data, Plan
class DataSerializer(serializers.ModelSerializer):
class Meta:
model = Data
fields = ('user',
'data',
)
fields = (
'data',
)
read_only_fields = ['user']

View file

@ -26,7 +26,7 @@ class CRUDRouter(DefaultRouter):
),
# Detail route.
Route(
url=r'^{prefix}/item{trailing_slash}$',
url=r'^{prefix}{trailing_slash}$',
mapping={
'get': 'retrieve',
'put': 'update',
@ -40,7 +40,7 @@ class CRUDRouter(DefaultRouter):
# Dynamically generated detail routes. Generated using
# @action(detail=True) decorator on methods of the viewset.
DynamicRoute(
url=r'^{prefix}/item/{url_path}{trailing_slash}$',
url=r'^{prefix}/{url_path}{trailing_slash}$',
name='{basename}-{url_name}',
detail=True,
initkwargs={}
@ -52,6 +52,7 @@ router = CRUDRouter()
app_name = 'crud'
router.register('', views.DataViewSet, basename='crud')
router.register('user', views.UserDataViewSet, basename='user-data')
router.register('application', views.ApplicationDataViewSet, basename='application-data')
urlpatterns = router.urls

View file

@ -7,13 +7,15 @@ from rest_framework.viewsets import GenericViewSet
from apps.crud.models import Data, Plan
from apps.crud.serializers import DataSerializer
from apps.gooyal_oauth2.models import Application
from apps.gooyal_oauth2.rest_framework import IsAuthenticatedOrTokenMatchesOASRequirements
from apps.gooyal_oauth2.utils import get_application
class DataViewSet(mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
GenericViewSet):
class UserDataViewSet(mixins.RetrieveModelMixin,
mixins.UpdateModelMixin,
mixins.DestroyModelMixin,
GenericViewSet):
queryset = Data.objects.all()
serializer_class = DataSerializer
@ -28,7 +30,7 @@ class DataViewSet(mixins.RetrieveModelMixin,
def get_object(self):
user = self.request.user
auth = self.request.auth
if auth:
if auth and user:
# plan = Plan.objects.filter(application_id=auth.application_id).first()
plan, created = Plan.objects.get_or_create(application_id=auth.application_id)
# data, created = Data.objects.get_or_create(plan_id=plan.pk, user=user)
@ -36,3 +38,25 @@ class DataViewSet(mixins.RetrieveModelMixin,
return data
raise Http404
class ApplicationDataViewSet(mixins.RetrieveModelMixin,
GenericViewSet):
queryset = Application.objects.all()
serializer_class = DataSerializer
permission_classes = [IsAuthenticatedOrTokenMatchesOASRequirements]
required_alternate_scopes = {
"GET": [["data_crud.data:retrieve"]],
}
def get_object(self):
application = get_application(self.request)
if application:
# plan = Plan.objects.filter(application_id=auth.application_id).first()
plan, created = Plan.objects.get_or_create(application=application)
# data, created = Data.objects.get_or_create(plan_id=plan.pk, user=user)
data, created = Data.objects.get_or_create(plan=plan, user=None)
return data
raise Http404

View file

@ -0,0 +1,6 @@
def get_application(request):
try:
application = request.auth.application
except:
application = None
return application

View file

@ -91,7 +91,7 @@ class OAuthCode(models.Model):
return code_challenge
def generate_login_url(self):
url = f'''{settings.BASE_OAUTH2_PROVIDER_URL}/oauth2/authorize/?response_type=code&code_challenge={self.generate_code_challenge()}&code_challenge_method=S256&client_id={settings.CLIENT_ID}&scope={settings.SCOPES}&state={self.uuid}'''
url = f'''{settings.BASE_OAUTH2_PROVIDER_PUBLIC_URL}/authorize/?response_type=code&code_challenge={self.generate_code_challenge()}&code_challenge_method=S256&client_id={settings.CLIENT_ID}&scope={settings.SCOPES}&state={self.uuid}'''
return url
def validate_code(self):
@ -107,6 +107,6 @@ class OAuthCode(models.Model):
"grant_type": "authorization_code"
}
response = requests.post(f'{settings.BASE_OAUTH2_PROVIDER_URL}/oauth2/token/', data=data, headers=headers)
response = requests.post(f'{settings.BASE_OAUTH2_PROVIDER_PRIVATE_URL}/token/', data=data, headers=headers)
return response.json()

View file

@ -76,7 +76,9 @@ OAUTH2_PROVIDER_ID_TOKEN_MODEL = "gooyal_oauth2.IDToken"
OAUTH2_PROVIDER_GRANT_MODEL = "gooyal_oauth2.Grant"
OAUTH2_PROVIDER_REFRESH_TOKEN_MODEL = "gooyal_oauth2.RefreshToken"
BASE_OAUTH2_PROVIDER_URL = "https://accounts.gooyal.com"
BASE_OAUTH2_PROVIDER_PUBLIC_URL = config("BASE_OAUTH2_PROVIDER_PUBLIC_URL")
BASE_OAUTH2_PROVIDER_PRIVATE_URL = config("BASE_OAUTH2_PROVIDER_PRIVATE_URL")
CLIENT_ID = config('CLIENT_ID')
CLIENT_SECRET = config('CLIENT_SECRET')
SCOPES = config('SCOPES', default='')
@ -88,7 +90,7 @@ OAUTH2_PROVIDER = {
# 'groups': 'Access to your groups',
# 'introspection': 'Introspect token scope'},
'OAUTH2_VALIDATOR_CLASS': 'apps.gooyal_oauth2.validators.OAuth2Validator',
'RESOURCE_SERVER_INTROSPECTION_URL': BASE_OAUTH2_PROVIDER_URL + '/oauth2/introspect/',
'RESOURCE_SERVER_INTROSPECTION_URL': BASE_OAUTH2_PROVIDER_PRIVATE_URL + '/introspect/',
# 'RESOURCE_SERVER_AUTH_TOKEN': '3yUqsWtwKYKHnfivFcJu', # OR this but not both:
'RESOURCE_SERVER_INTROSPECTION_CREDENTIALS': (CLIENT_ID, CLIENT_SECRET),
}
@ -220,6 +222,8 @@ SPECTACULAR_SETTINGS = {
'VERSION': '1.0.0',
'SERVE_INCLUDE_SCHEMA': False,
# OTHER SETTINGS
"PARSER_WHITELIST": ["rest_framework.parsers.JSONParser"],
}
AUTH_USER_MODEL = 'users.User'

View file

@ -37,7 +37,7 @@ urlpatterns = [
path('', include('apps.core.urls')),
path('users/', include('apps.users.urls')),
path('oauth2/', include('oauth2_provider.urls', namespace='oauth2_provider')),
path('crud/', include('apps.crud.urls', namespace='crud')),
path('data/', include('apps.crud.urls', namespace='crud')),
]
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

View file

@ -42,8 +42,9 @@
<div class="row vh-100 overflow-auto">
{# {% include "include/sidebar.html" %}#}
<div class="col d-flex flex-column h-sm-100">
<div class="m-0 p-0 w-100 " style="background-color: yellow; height: 3px;"></div>
<main class="row overflow-auto pb-3">
<div class="col pt-3">
<div class="col pt-2">
{% include "include/top_bar.html" %}
{% include "include/messages.html" %}
{# {% block breadcrumbs %}#}

View file

@ -3,8 +3,8 @@
{% load crispy_forms_tags %}
{% load jalali_tags %}
{% block title %}wallet: Home{% endblock %}
{% block page_title %}wallet: Home{% endblock %}
{% block title %}CRUD: Home{% endblock %}
{% block page_title %}CRUD: Home{% endblock %}
{% block content %}