授权 {{ application.name }} 访问你的帐户吗?
+Error: {{ error.error }}
+{{ error.description }}
+ {% endif %} +diff --git a/boofilsic/settings.py b/boofilsic/settings.py index 1b36bbb8..036357e8 100644 --- a/boofilsic/settings.py +++ b/boofilsic/settings.py @@ -305,6 +305,7 @@ INSTALLED_APPS += [ "catalog.apps.CatalogConfig", "journal.apps.JournalConfig", "social.apps.SocialConfig", + "developer.apps.DeveloperConfig", "takahe.apps.TakaheConfig", "legacy.apps.LegacyConfig", ] @@ -312,6 +313,9 @@ INSTALLED_APPS += [ for app in env("NEODB_EXTRA_APPS"): INSTALLED_APPS.append(app) +INSTALLED_APPS += [ # we may override templates in these 3rd party apps + "oauth2_provider", +] MIDDLEWARE = [ "django.middleware.security.SecurityMiddleware", diff --git a/developer/__init__.py b/developer/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/developer/admin.py b/developer/admin.py new file mode 100644 index 00000000..8c38f3f3 --- /dev/null +++ b/developer/admin.py @@ -0,0 +1,3 @@ +from django.contrib import admin + +# Register your models here. diff --git a/developer/apps.py b/developer/apps.py new file mode 100644 index 00000000..2ee12960 --- /dev/null +++ b/developer/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class DeveloperConfig(AppConfig): + default_auto_field = "django.db.models.BigAutoField" + name = "developer" diff --git a/developer/migrations/0001_initial.py b/developer/migrations/0001_initial.py new file mode 100644 index 00000000..aa05f04b --- /dev/null +++ b/developer/migrations/0001_initial.py @@ -0,0 +1,128 @@ +# Generated by Django 3.2.19 on 2023-06-28 05:09 + +import django.core.validators +import django.db.models.deletion +import markdownx.models +import oauth2_provider.generators +import oauth2_provider.models +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, + 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, + ), + ), + ("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, + ), + ), + ( + "name", + models.CharField( + max_length=255, + validators=[ + django.core.validators.RegexValidator( + message="minimum two characters, words and -_. only, no special characters", + regex="^\\w[\\w_\\-. ]*\\w$", + ) + ], + ), + ), + ( + "description", + markdownx.models.MarkdownxField(blank=True, default=""), + ), + ("url", models.URLField(blank=True, null=True)), + ("is_official", models.BooleanField(default=False)), + ( + "user", + models.ForeignKey( + blank=True, + null=True, + on_delete=django.db.models.deletion.CASCADE, + related_name="developer_application", + to=settings.AUTH_USER_MODEL, + ), + ), + ], + options={ + "abstract": False, + }, + ), + ] diff --git a/developer/migrations/0002_alter_application_user.py b/developer/migrations/0002_alter_application_user.py new file mode 100644 index 00000000..0a8c33f0 --- /dev/null +++ b/developer/migrations/0002_alter_application_user.py @@ -0,0 +1,26 @@ +# Generated by Django 4.2.3 on 2023-07-06 22:53 + +import django.db.models.deletion +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ("developer", "0001_initial"), + ] + + operations = [ + migrations.AlterField( + model_name="application", + name="user", + field=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, + ), + ), + ] diff --git a/developer/migrations/0003_alter_application_redirect_uris.py b/developer/migrations/0003_alter_application_redirect_uris.py new file mode 100644 index 00000000..079c613c --- /dev/null +++ b/developer/migrations/0003_alter_application_redirect_uris.py @@ -0,0 +1,19 @@ +# Generated by Django 4.2.3 on 2023-07-13 04:02 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + dependencies = [ + ("developer", "0002_alter_application_user"), + ] + + operations = [ + migrations.AlterField( + model_name="application", + name="redirect_uris", + field=models.TextField( + help_text="Allowed URIs list, space separated, at least one URI is required" + ), + ), + ] diff --git a/developer/migrations/__init__.py b/developer/migrations/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/developer/models.py b/developer/models.py new file mode 100644 index 00000000..1ca80821 --- /dev/null +++ b/developer/models.py @@ -0,0 +1,33 @@ +from django.core.validators import RegexValidator +from django.db import models +from django.utils.translation import gettext_lazy as _ +from markdownx.models import MarkdownxField +from oauth2_provider.models import AbstractApplication + +from journal.models.renderers import render_md + + +class Application(AbstractApplication): + name = models.CharField( + max_length=255, + blank=False, + validators=[ + RegexValidator( + regex=r"^\w[\w_\-. ]*\w$", + message=_( + "minimum two characters, words and -_. only, no special characters" + ), + ), + ], + ) + description = MarkdownxField(default="", blank=True) + url = models.URLField(null=True, blank=True) + is_official = models.BooleanField(default=False) + unique_together = [["user", "name"]] + redirect_uris = models.TextField( + blank=False, + help_text=_("Allowed URIs list, space separated, at least one URI is required"), + ) + + def description_html(self): + return render_md(self.description) diff --git a/developer/templates/console.html b/developer/templates/console.html new file mode 100644 index 00000000..605c58ca --- /dev/null +++ b/developer/templates/console.html @@ -0,0 +1,112 @@ +{% load i18n %} + + +
+ ++ By using our APIs, you agree to our Terms of Service. +
+
+ Click Authorize
button below, input your token there to invoke APIs with your account, which is required for APIs like /api/me
+
+ Or use it in command line, like
+ curl -H "Authorization: Bearer YOUR_TOKEN" {{ site_url }}/api/me
+
https://example.org/callback
)
+ https://example.org/callback
with a code
parameter:
+
+ 3. Obtain access token with the following POST request:
+
+ and access token will be returned in the response:
+
+ 4. Use the access token to access protected endpoints like /api/me
+
+ and response will be returned accordingly:
+
+ more endpoints can be found in API Documentation below.
+ + {% trans "Client ID" %} +
+ ++ {% trans "URL" %} +
+{{ application.url | default:"" | urlize }}
++ {% trans "Description" %} +
+{{ application.description_html|safe }}
++ {% trans "Redirect Uris" %} + {% if not application.redirect_uris %}WARNING: no redirect uris have been set, authorization may not work.{% endif %} +
+ ++ Please connect to a Fediverse identity before creating an application. +
+ {% elif applications %} ++ {% trans "No applications defined" %}. {% trans "Click here" %} {% trans "if you want to register a new one" %} +
+ {% endif %} +{{ error.description }}
+ {% endif %} +