From c2fbfd70a942b5da896f646e96fcb029719ba1bf Mon Sep 17 00:00:00 2001 From: mahdavi Date: Thu, 4 Jul 2024 15:12:18 +0330 Subject: [PATCH] client id and client owner in token model --- apps/gooyal_oauth2/migrations/0001_initial.py | 121 ++++++++++++++++++ .../0002_alter_accesstoken_client_owner.py | 21 +++ apps/gooyal_oauth2/migrations/__init__.py | 0 apps/gooyal_oauth2/models.py | 35 ++++- apps/gooyal_oauth2/validators.py | 15 +-- apps/wallet/serializers.py | 1 + apps/wallet/views.py | 2 +- wallet/settings.py | 10 +- 8 files changed, 187 insertions(+), 18 deletions(-) create mode 100644 apps/gooyal_oauth2/migrations/0001_initial.py create mode 100644 apps/gooyal_oauth2/migrations/0002_alter_accesstoken_client_owner.py create mode 100644 apps/gooyal_oauth2/migrations/__init__.py diff --git a/apps/gooyal_oauth2/migrations/0001_initial.py b/apps/gooyal_oauth2/migrations/0001_initial.py new file mode 100644 index 0000000..938a506 --- /dev/null +++ b/apps/gooyal_oauth2/migrations/0001_initial.py @@ -0,0 +1,121 @@ +# Generated by Django 5.0.6 on 2024-07-04 11:18 + +import django.db.models.deletion +import oauth2_provider.generators +import oauth2_provider.models +import uuid +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name='Application', + fields=[ + ('id', models.BigAutoField(primary_key=True, serialize=False)), + ('client_id', models.CharField(db_index=True, default=oauth2_provider.generators.generate_client_id, max_length=100, unique=True)), + ('redirect_uris', models.TextField(blank=True, help_text='Allowed URIs list, space separated')), + ('post_logout_redirect_uris', models.TextField(blank=True, default='', help_text='Allowed Post Logout URIs list, space separated')), + ('client_type', models.CharField(choices=[('confidential', 'Confidential'), ('public', 'Public')], max_length=32)), + ('authorization_grant_type', models.CharField(choices=[('authorization-code', 'Authorization code'), ('implicit', 'Implicit'), ('password', 'Resource owner password-based'), ('client-credentials', 'Client credentials'), ('openid-hybrid', 'OpenID connect hybrid')], max_length=32)), + ('client_secret', oauth2_provider.models.ClientSecretField(blank=True, db_index=True, default=oauth2_provider.generators.generate_client_secret, help_text='Hashed on Save. Copy it now if this is a new secret.', max_length=255)), + ('hash_client_secret', models.BooleanField(default=True)), + ('name', models.CharField(blank=True, max_length=255)), + ('skip_authorization', models.BooleanField(default=False)), + ('created', models.DateTimeField(auto_now_add=True)), + ('updated', models.DateTimeField(auto_now=True)), + ('algorithm', models.CharField(blank=True, choices=[('', 'No OIDC support'), ('RS256', 'RSA with SHA-2 256'), ('HS256', 'HMAC with SHA-2 256')], default='', max_length=5)), + ('allowed_origins', models.TextField(blank=True, default='', help_text='Allowed origins list to enable CORS, space separated')), + ('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(app_label)s_%(class)s', to=settings.AUTH_USER_MODEL)), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='Grant', + fields=[ + ('id', models.BigAutoField(primary_key=True, serialize=False)), + ('code', models.CharField(max_length=255, unique=True)), + ('expires', models.DateTimeField()), + ('redirect_uri', models.TextField()), + ('scope', models.TextField(blank=True)), + ('created', models.DateTimeField(auto_now_add=True)), + ('updated', models.DateTimeField(auto_now=True)), + ('code_challenge', models.CharField(blank=True, default='', max_length=128)), + ('code_challenge_method', models.CharField(blank=True, choices=[('plain', 'plain'), ('S256', 'S256')], default='', max_length=10)), + ('nonce', models.CharField(blank=True, default='', max_length=255)), + ('claims', models.TextField(blank=True)), + ('application', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.OAUTH2_PROVIDER_APPLICATION_MODEL)), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(app_label)s_%(class)s', to=settings.AUTH_USER_MODEL)), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='IDToken', + fields=[ + ('id', models.BigAutoField(primary_key=True, serialize=False)), + ('jti', models.UUIDField(default=uuid.uuid4, editable=False, unique=True, verbose_name='JWT Token ID')), + ('expires', models.DateTimeField()), + ('scope', models.TextField(blank=True)), + ('created', models.DateTimeField(auto_now_add=True)), + ('updated', models.DateTimeField(auto_now=True)), + ('application', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.OAUTH2_PROVIDER_APPLICATION_MODEL)), + ('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(app_label)s_%(class)s', to=settings.AUTH_USER_MODEL)), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='AccessToken', + fields=[ + ('id', models.BigAutoField(primary_key=True, serialize=False)), + ('token', models.CharField(db_index=True, max_length=255, unique=True)), + ('expires', models.DateTimeField()), + ('scope', models.TextField(blank=True)), + ('created', models.DateTimeField(auto_now_add=True)), + ('updated', models.DateTimeField(auto_now=True)), + ('detail', models.JSONField(blank=True, null=True)), + ('client_id', models.CharField(blank=True, max_length=255, null=True)), + ('client_owner', models.UUIDField(blank=True, null=True)), + ('user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='%(app_label)s_%(class)s', to=settings.AUTH_USER_MODEL)), + ('application', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to=settings.OAUTH2_PROVIDER_APPLICATION_MODEL)), + ('id_token', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='access_token', to=settings.OAUTH2_PROVIDER_ID_TOKEN_MODEL)), + ], + options={ + 'abstract': False, + }, + ), + migrations.CreateModel( + name='RefreshToken', + fields=[ + ('id', models.BigAutoField(primary_key=True, serialize=False)), + ('token', models.CharField(max_length=255)), + ('created', models.DateTimeField(auto_now_add=True)), + ('updated', models.DateTimeField(auto_now=True)), + ('revoked', models.DateTimeField(null=True)), + ('access_token', models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='refresh_token', to=settings.OAUTH2_PROVIDER_ACCESS_TOKEN_MODEL)), + ('application', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.OAUTH2_PROVIDER_APPLICATION_MODEL)), + ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='%(app_label)s_%(class)s', to=settings.AUTH_USER_MODEL)), + ], + options={ + 'abstract': False, + }, + ), + migrations.AddField( + model_name='accesstoken', + name='source_refresh_token', + field=models.OneToOneField(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='refreshed_access_token', to=settings.OAUTH2_PROVIDER_REFRESH_TOKEN_MODEL), + ), + ] diff --git a/apps/gooyal_oauth2/migrations/0002_alter_accesstoken_client_owner.py b/apps/gooyal_oauth2/migrations/0002_alter_accesstoken_client_owner.py new file mode 100644 index 0000000..41c0f9d --- /dev/null +++ b/apps/gooyal_oauth2/migrations/0002_alter_accesstoken_client_owner.py @@ -0,0 +1,21 @@ +# Generated by Django 5.0.6 on 2024-07-04 11:38 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('gooyal_oauth2', '0001_initial'), + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.AlterField( + model_name='accesstoken', + name='client_owner', + field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.PROTECT, to=settings.AUTH_USER_MODEL), + ), + ] diff --git a/apps/gooyal_oauth2/migrations/__init__.py b/apps/gooyal_oauth2/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/apps/gooyal_oauth2/models.py b/apps/gooyal_oauth2/models.py index f668dca..34ad8f7 100644 --- a/apps/gooyal_oauth2/models.py +++ b/apps/gooyal_oauth2/models.py @@ -1 +1,34 @@ -# models \ No newline at end of file +# models +from django.db import models +from oauth2_provider.models import AbstractApplication, AbstractAccessToken, AbstractGrant, AbstractRefreshToken, \ + AbstractIDToken + + +class AccessToken(AbstractAccessToken): + detail = models.JSONField(null=True, blank=True) + client_id = models.CharField(max_length=255, null=True, blank=True) + client_owner = models.ForeignKey('users.User', on_delete=models.PROTECT, null=True, blank=True) + + class Meta: + abstract = False + + +class Application(AbstractApplication): + class Meta: + abstract = False + + +class Grant(AbstractGrant): + class Meta: + abstract = False + + +class RefreshToken(AbstractRefreshToken): + class Meta: + abstract = False + + +class IDToken(AbstractIDToken): + class Meta: + abstract = False + diff --git a/apps/gooyal_oauth2/validators.py b/apps/gooyal_oauth2/validators.py index 39f29dc..cf6c474 100755 --- a/apps/gooyal_oauth2/validators.py +++ b/apps/gooyal_oauth2/validators.py @@ -61,8 +61,6 @@ UserModel = get_user_model() log = logging.getLogger("oauth2_provider") -AccessTokenModel = get_access_token_model() -UserModel = get_user_model() class OAuth2Validator(BaseOAuth2Validator): # pylint: disable=w0223 def get_or_create_user_from_content(self, content): @@ -150,19 +148,14 @@ class OAuth2Validator(BaseOAuth2Validator): # pylint: disable=w0223 expires, timezone=get_timezone(oauth2_settings.AUTHENTICATION_SERVER_EXP_TIME_ZONE) ) - # NOTICE: onlu change from orginal method is that we create application here - if 'client_id' in content: - application, _created = Application.objects.get_or_create( - client_id=content["client_id"] - ) - else: - application = None - + # TODO: get application owner and put it here access_token, _created = AccessToken.objects.update_or_create( token=token, defaults={ "user": user, - "application": application, + "client_id": content.get("client_id", ""), + "client_owner": user, + "application": None, "scope": scope, "expires": expires, }, diff --git a/apps/wallet/serializers.py b/apps/wallet/serializers.py index 84e657e..e024e44 100755 --- a/apps/wallet/serializers.py +++ b/apps/wallet/serializers.py @@ -8,6 +8,7 @@ from ..users.models import User class TransactionSerializer(serializers.ModelSerializer): + payee = serializers.UUIDField(read_only=True) class Meta: model = Transaction fields = ('uuid', diff --git a/apps/wallet/views.py b/apps/wallet/views.py index c2f26de..49ccef5 100755 --- a/apps/wallet/views.py +++ b/apps/wallet/views.py @@ -68,7 +68,7 @@ class TransactionList(generics.ListCreateAPIView): def perform_create(self, serializer): payer = self.request.user - payee = self.request.auth.application.user + payee = self.request.auth.client_owner serializer.save(payer=payer, payee=payee, application=get_application(self.request)) diff --git a/wallet/settings.py b/wallet/settings.py index 4098b7e..6ba190d 100644 --- a/wallet/settings.py +++ b/wallet/settings.py @@ -68,11 +68,11 @@ MIDDLEWARE = [ ] -OAUTH2_PROVIDER_APPLICATION_MODEL = "oauth2_provider.Application" -OAUTH2_PROVIDER_ACCESS_TOKEN_MODEL = "oauth2_provider.AccessToken" -OAUTH2_PROVIDER_ID_TOKEN_MODEL = "oauth2_provider.IDToken" -OAUTH2_PROVIDER_GRANT_MODEL = "oauth2_provider.Grant" -OAUTH2_PROVIDER_REFRESH_TOKEN_MODEL = "oauth2_provider.RefreshToken" +OAUTH2_PROVIDER_APPLICATION_MODEL = "gooyal_oauth2.Application" +OAUTH2_PROVIDER_ACCESS_TOKEN_MODEL = "gooyal_oauth2.AccessToken" +OAUTH2_PROVIDER_ID_TOKEN_MODEL = "gooyal_oauth2.IDToken" +OAUTH2_PROVIDER_GRANT_MODEL = "gooyal_oauth2.Grant" +OAUTH2_PROVIDER_REFRESH_TOKEN_MODEL = "gooyal_oauth2.RefreshToken" OAUTH2_PROVIDER = {