app mgmt
This commit is contained in:
parent
8ba8c4490e
commit
d27cf87496
8 changed files with 222 additions and 30 deletions
|
@ -18,7 +18,7 @@ class OAuthAccessTokenAuth(HttpBearer):
|
|||
_logger.debug("API auth: no access token or user not authenticated")
|
||||
return False
|
||||
request_scopes = []
|
||||
if request.method.upper() in ["GET", "HEAD", "OPTIONS"]:
|
||||
if request.method in ["GET", "HEAD", "OPTIONS"]:
|
||||
request_scopes = ["read"]
|
||||
else:
|
||||
request_scopes = ["write"]
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
# Generated by Django 3.2.19 on 2023-06-28 02:44
|
||||
# Generated by Django 3.2.19 on 2023-06-28 05:09
|
||||
|
||||
from django.conf import settings
|
||||
import django.core.validators
|
||||
|
@ -10,7 +10,6 @@ import oauth2_provider.models
|
|||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
|
@ -19,27 +18,111 @@ class Migration(migrations.Migration):
|
|||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Application',
|
||||
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, unique=True, validators=[django.core.validators.RegexValidator(message='至少两个字,不可包含普通文字和-_.以外的字符', regex='^\\w[\\w_\\-. ]*\\w$')])),
|
||||
('descrpition', 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)),
|
||||
("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,
|
||||
"abstract": False,
|
||||
},
|
||||
),
|
||||
]
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
from django.db import models
|
||||
from django.core.validators import RegexValidator
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from oauth2_provider.models import AbstractApplication
|
||||
from markdownx.models import MarkdownxField
|
||||
from journal.renderers import render_md
|
||||
|
||||
|
||||
class Application(AbstractApplication):
|
||||
|
@ -11,11 +13,16 @@ class Application(AbstractApplication):
|
|||
validators=[
|
||||
RegexValidator(
|
||||
regex=r"^\w[\w_\-. ]*\w$",
|
||||
message="至少两个字,不可包含普通文字和-_.以外的字符",
|
||||
message=_(
|
||||
"minimum two characters, words and -_. only, no special characters"
|
||||
),
|
||||
),
|
||||
],
|
||||
unique=True,
|
||||
)
|
||||
descrpition = MarkdownxField(default="", blank=True)
|
||||
description = MarkdownxField(default="", blank=True)
|
||||
url = models.URLField(null=True, blank=True)
|
||||
is_official = models.BooleanField(default=False)
|
||||
unique_together = [["user", "name"]]
|
||||
|
||||
def description_html(self):
|
||||
return render_md(self.description)
|
||||
|
|
52
developer/templates/oauth2_provider/application_detail.html
Normal file
52
developer/templates/oauth2_provider/application_detail.html
Normal file
|
@ -0,0 +1,52 @@
|
|||
{% extends "oauth2_provider/base.html" %}
|
||||
{% load i18n %}
|
||||
{% block content %}
|
||||
<div class="block-center">
|
||||
<h3 class="block-center-heading">{{ application.name }}</h3>
|
||||
<ul class="unstyled">
|
||||
<li>
|
||||
<p>
|
||||
<b>{% trans "Client ID" %}</b>
|
||||
</p>
|
||||
<input class="input-block-level"
|
||||
type="text"
|
||||
value="{{ application.client_id }}"
|
||||
readonly>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
<b>{% trans "Client Secret" %}</b>
|
||||
</p>
|
||||
<input class="input-block-level"
|
||||
type="text"
|
||||
value="{{ application.client_secret }}"
|
||||
readonly>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
<b>{% trans "URL" %}</b>
|
||||
</p>
|
||||
<p>{{ application.url | default:"" | urlize }}</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
<b>{% trans "Description" %}</b>
|
||||
</p>
|
||||
<p>{{ application.description_html|safe }}</p>
|
||||
</li>
|
||||
<li>
|
||||
<p>
|
||||
<b>{% trans "Redirect Uris" %}</b>
|
||||
</p>
|
||||
<textarea class="input-block-level" readonly>{{ application.redirect_uris }}</textarea>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="btn-toolbar">
|
||||
<a class="btn" href="{% url "oauth2_provider:list" %}">{% trans "Go Back" %}</a>
|
||||
<a class="btn btn-primary"
|
||||
href="{% url "oauth2_provider:update" application.id %}">{% trans "Edit" %}</a>
|
||||
<a class="btn btn-danger"
|
||||
href="{% url "oauth2_provider:delete" application.id %}">{% trans "Delete" %}</a>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock content %}
|
|
@ -10,10 +10,12 @@
|
|||
{% csrf_token %}
|
||||
{% if not application.is_official %}
|
||||
<p>
|
||||
<b>{{ application.name }}</b> 是 <a href="{% url 'journal:user_profile' application.user.mastodon_username %}">{{ application.user.mastodon_username }}</a> 创建和维护的应用程序, 并非来自{{ site_name }}官方。
|
||||
<b>{{ application.name }}</b> 是由 <a href="{% url 'journal:user_profile' application.user.mastodon_username %}">{{ application.user.mastodon_username }}</a> 创建和维护的应用程序。
|
||||
{{ site_name }}无法保证其安全性和有效性,请自行验证确认后再授权。
|
||||
</p>
|
||||
{% endif %}
|
||||
{% if application.url %}应用网址: {{ application.url | urlize }}{% endif %}
|
||||
<p>{{ application.description_html|safe }}</p>
|
||||
{% for field in form %}
|
||||
{% if field.is_hidden %}{{ field }}{% endif %}
|
||||
{% endfor %}
|
||||
|
|
|
@ -44,7 +44,7 @@ _urlpatterns += [
|
|||
),
|
||||
re_path(
|
||||
r"^developer/applications/register/$",
|
||||
oauth2_views.ApplicationRegistration.as_view(),
|
||||
ApplicationRegistration.as_view(),
|
||||
name="register",
|
||||
),
|
||||
re_path(
|
||||
|
@ -59,7 +59,7 @@ _urlpatterns += [
|
|||
),
|
||||
re_path(
|
||||
r"^developer/applications/(?P<pk>[\w-]+)/update/$",
|
||||
oauth2_views.ApplicationUpdate.as_view(),
|
||||
ApplicationUpdate.as_view(),
|
||||
name="update",
|
||||
),
|
||||
]
|
||||
|
|
|
@ -4,9 +4,11 @@ from django.contrib.auth.decorators import login_required
|
|||
from django.urls import reverse
|
||||
from oauth2_provider.forms import AllowForm
|
||||
from oauth2_provider.models import get_application_model
|
||||
from oauth2_provider.views import ProtectedResourceView
|
||||
from oauth2_provider.views import ApplicationRegistration as BaseApplicationRegistration
|
||||
from oauth2_provider.views import ApplicationUpdate as BaseApplicationUpdate
|
||||
from oauth2_provider.views.base import AuthorizationView as BaseAuthorizationView
|
||||
from oauth2_provider.settings import oauth2_settings
|
||||
from oauth2_provider.generators import generate_client_id, generate_client_secret
|
||||
from common.api import api
|
||||
from oauthlib.common import generate_token
|
||||
from oauth2_provider.models import AccessToken
|
||||
|
@ -15,10 +17,47 @@ from dateutil.relativedelta import relativedelta
|
|||
from oauth2_provider.models import RefreshToken
|
||||
from django.conf import settings
|
||||
from .models import Application
|
||||
from django.forms.models import modelform_factory
|
||||
from .models import Application
|
||||
|
||||
|
||||
class AuthorizationView(BaseAuthorizationView):
|
||||
pass
|
||||
class ApplicationRegistration(BaseApplicationRegistration):
|
||||
def get_form_class(self):
|
||||
return modelform_factory(
|
||||
Application,
|
||||
fields=(
|
||||
"name",
|
||||
"url",
|
||||
"description",
|
||||
"redirect_uris",
|
||||
# "post_logout_redirect_uris",
|
||||
),
|
||||
)
|
||||
|
||||
def form_valid(self, form):
|
||||
form.instance.user = self.request.user
|
||||
if not form.instance.id:
|
||||
form.instance.client_id = generate_client_id()
|
||||
form.instance.client_secret = generate_client_secret()
|
||||
form.instance.client_type = Application.CLIENT_CONFIDENTIAL
|
||||
form.instance.authorization_grant_type = (
|
||||
Application.GRANT_AUTHORIZATION_CODE
|
||||
)
|
||||
return super().form_valid(form)
|
||||
|
||||
|
||||
class ApplicationUpdate(BaseApplicationUpdate):
|
||||
def get_form_class(self):
|
||||
return modelform_factory(
|
||||
get_application_model(),
|
||||
fields=(
|
||||
"name",
|
||||
"url",
|
||||
"description",
|
||||
"redirect_uris",
|
||||
# "post_logout_redirect_uris",
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
@login_required
|
||||
|
|
|
@ -144,6 +144,15 @@
|
|||
}
|
||||
</script>
|
||||
</section>
|
||||
<hr>
|
||||
<section>
|
||||
<details>
|
||||
<summary>{% trans '应用管理' %}</summary>
|
||||
<p>
|
||||
<a href="{% url 'oauth2_provider:authorized-token-list' %}">查看已授权的应用程序</a>
|
||||
</p>
|
||||
</details>
|
||||
</section>
|
||||
</article>
|
||||
</div>
|
||||
{% include "_sidebar.html" with show_profile=1 %}
|
||||
|
|
Loading…
Add table
Reference in a new issue