Revert "*: providers and sources -> channels, PolicyModel to PolicyBindingModel that uses custom M2M through"
This reverts commit 7ed3ceb960.
			
			
This commit is contained in:
		
							
								
								
									
										0
									
								
								passbook/providers/oauth/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								passbook/providers/oauth/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										29
									
								
								passbook/providers/oauth/api.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								passbook/providers/oauth/api.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,29 @@
 | 
			
		||||
"""OAuth2Provider API Views"""
 | 
			
		||||
from rest_framework.serializers import ModelSerializer
 | 
			
		||||
from rest_framework.viewsets import ModelViewSet
 | 
			
		||||
 | 
			
		||||
from passbook.providers.oauth.models import OAuth2Provider
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class OAuth2ProviderSerializer(ModelSerializer):
 | 
			
		||||
    """OAuth2Provider Serializer"""
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
 | 
			
		||||
        model = OAuth2Provider
 | 
			
		||||
        fields = [
 | 
			
		||||
            "pk",
 | 
			
		||||
            "name",
 | 
			
		||||
            "redirect_uris",
 | 
			
		||||
            "client_type",
 | 
			
		||||
            "authorization_grant_type",
 | 
			
		||||
            "client_id",
 | 
			
		||||
            "client_secret",
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class OAuth2ProviderViewSet(ModelViewSet):
 | 
			
		||||
    """OAuth2Provider Viewset"""
 | 
			
		||||
 | 
			
		||||
    queryset = OAuth2Provider.objects.all()
 | 
			
		||||
    serializer_class = OAuth2ProviderSerializer
 | 
			
		||||
							
								
								
									
										12
									
								
								passbook/providers/oauth/apps.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										12
									
								
								passbook/providers/oauth/apps.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,12 @@
 | 
			
		||||
"""passbook auth oauth provider app config"""
 | 
			
		||||
 | 
			
		||||
from django.apps import AppConfig
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PassbookProviderOAuthConfig(AppConfig):
 | 
			
		||||
    """passbook auth oauth provider app config"""
 | 
			
		||||
 | 
			
		||||
    name = "passbook.providers.oauth"
 | 
			
		||||
    label = "passbook_providers_oauth"
 | 
			
		||||
    verbose_name = "passbook Providers.OAuth"
 | 
			
		||||
    mountpoint = ""
 | 
			
		||||
							
								
								
									
										21
									
								
								passbook/providers/oauth/forms.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										21
									
								
								passbook/providers/oauth/forms.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,21 @@
 | 
			
		||||
"""passbook OAuth2 Provider Forms"""
 | 
			
		||||
 | 
			
		||||
from django import forms
 | 
			
		||||
 | 
			
		||||
from passbook.providers.oauth.models import OAuth2Provider
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class OAuth2ProviderForm(forms.ModelForm):
 | 
			
		||||
    """OAuth2 Provider form"""
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
 | 
			
		||||
        model = OAuth2Provider
 | 
			
		||||
        fields = [
 | 
			
		||||
            "name",
 | 
			
		||||
            "redirect_uris",
 | 
			
		||||
            "client_type",
 | 
			
		||||
            "authorization_grant_type",
 | 
			
		||||
            "client_id",
 | 
			
		||||
            "client_secret",
 | 
			
		||||
        ]
 | 
			
		||||
							
								
								
									
										104
									
								
								passbook/providers/oauth/migrations/0001_initial.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										104
									
								
								passbook/providers/oauth/migrations/0001_initial.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,104 @@
 | 
			
		||||
# Generated by Django 2.2.6 on 2019-10-07 14:07
 | 
			
		||||
 | 
			
		||||
import django.db.models.deletion
 | 
			
		||||
import oauth2_provider.generators
 | 
			
		||||
from django.conf import settings
 | 
			
		||||
from django.db import migrations, models
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Migration(migrations.Migration):
 | 
			
		||||
 | 
			
		||||
    initial = True
 | 
			
		||||
 | 
			
		||||
    run_before = [
 | 
			
		||||
        ("oauth2_provider", "0001_initial"),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    dependencies = [
 | 
			
		||||
        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
 | 
			
		||||
        ("passbook_core", "0001_initial"),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    operations = [
 | 
			
		||||
        migrations.CreateModel(
 | 
			
		||||
            name="OAuth2Provider",
 | 
			
		||||
            fields=[
 | 
			
		||||
                (
 | 
			
		||||
                    "provider_ptr",
 | 
			
		||||
                    models.OneToOneField(
 | 
			
		||||
                        auto_created=True,
 | 
			
		||||
                        on_delete=django.db.models.deletion.CASCADE,
 | 
			
		||||
                        parent_link=True,
 | 
			
		||||
                        primary_key=True,
 | 
			
		||||
                        serialize=False,
 | 
			
		||||
                        to="passbook_core.Provider",
 | 
			
		||||
                    ),
 | 
			
		||||
                ),
 | 
			
		||||
                (
 | 
			
		||||
                    "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"
 | 
			
		||||
                    ),
 | 
			
		||||
                ),
 | 
			
		||||
                (
 | 
			
		||||
                    "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"),
 | 
			
		||||
                        ],
 | 
			
		||||
                        max_length=32,
 | 
			
		||||
                    ),
 | 
			
		||||
                ),
 | 
			
		||||
                (
 | 
			
		||||
                    "client_secret",
 | 
			
		||||
                    models.CharField(
 | 
			
		||||
                        blank=True,
 | 
			
		||||
                        db_index=True,
 | 
			
		||||
                        default=oauth2_provider.generators.generate_client_secret,
 | 
			
		||||
                        max_length=255,
 | 
			
		||||
                    ),
 | 
			
		||||
                ),
 | 
			
		||||
                ("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)),
 | 
			
		||||
                (
 | 
			
		||||
                    "user",
 | 
			
		||||
                    models.ForeignKey(
 | 
			
		||||
                        blank=True,
 | 
			
		||||
                        null=True,
 | 
			
		||||
                        on_delete=django.db.models.deletion.CASCADE,
 | 
			
		||||
                        related_name="passbook_providers_oauth_oauth2provider",
 | 
			
		||||
                        to=settings.AUTH_USER_MODEL,
 | 
			
		||||
                    ),
 | 
			
		||||
                ),
 | 
			
		||||
            ],
 | 
			
		||||
            options={
 | 
			
		||||
                "verbose_name": "OAuth2 Provider",
 | 
			
		||||
                "verbose_name_plural": "OAuth2 Providers",
 | 
			
		||||
            },
 | 
			
		||||
            bases=("passbook_core.provider", models.Model),
 | 
			
		||||
        ),
 | 
			
		||||
    ]
 | 
			
		||||
							
								
								
									
										0
									
								
								passbook/providers/oauth/migrations/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								passbook/providers/oauth/migrations/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										43
									
								
								passbook/providers/oauth/models.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										43
									
								
								passbook/providers/oauth/models.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,43 @@
 | 
			
		||||
"""Oauth2 provider product extension"""
 | 
			
		||||
 | 
			
		||||
from typing import Optional
 | 
			
		||||
 | 
			
		||||
from django.http import HttpRequest
 | 
			
		||||
from django.shortcuts import reverse
 | 
			
		||||
from django.utils.translation import gettext as _
 | 
			
		||||
from oauth2_provider.models import AbstractApplication
 | 
			
		||||
 | 
			
		||||
from passbook.core.models import Provider
 | 
			
		||||
from passbook.lib.utils.template import render_to_string
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class OAuth2Provider(Provider, AbstractApplication):
 | 
			
		||||
    """Associate an OAuth2 Application with a Product"""
 | 
			
		||||
 | 
			
		||||
    form = "passbook.providers.oauth.forms.OAuth2ProviderForm"
 | 
			
		||||
 | 
			
		||||
    def __str__(self):
 | 
			
		||||
        return f"OAuth2 Provider {self.name}"
 | 
			
		||||
 | 
			
		||||
    def html_setup_urls(self, request: HttpRequest) -> Optional[str]:
 | 
			
		||||
        """return template and context modal with URLs for authorize, token, openid-config, etc"""
 | 
			
		||||
        return render_to_string(
 | 
			
		||||
            "oauth2_provider/setup_url_modal.html",
 | 
			
		||||
            {
 | 
			
		||||
                "provider": self,
 | 
			
		||||
                "authorize_url": request.build_absolute_uri(
 | 
			
		||||
                    reverse("passbook_providers_oauth:oauth2-authorize")
 | 
			
		||||
                ),
 | 
			
		||||
                "token_url": request.build_absolute_uri(
 | 
			
		||||
                    reverse("passbook_providers_oauth:token")
 | 
			
		||||
                ),
 | 
			
		||||
                "userinfo_url": request.build_absolute_uri(
 | 
			
		||||
                    reverse("passbook_api:openid")
 | 
			
		||||
                ),
 | 
			
		||||
            },
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
 | 
			
		||||
        verbose_name = _("OAuth2 Provider")
 | 
			
		||||
        verbose_name_plural = _("OAuth2 Providers")
 | 
			
		||||
							
								
								
									
										32
									
								
								passbook/providers/oauth/settings.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								passbook/providers/oauth/settings.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,32 @@
 | 
			
		||||
"""passbook OAuth_Provider"""
 | 
			
		||||
from django.conf import settings
 | 
			
		||||
 | 
			
		||||
CORS_ORIGIN_ALLOW_ALL = settings.DEBUG
 | 
			
		||||
 | 
			
		||||
REQUEST_APPROVAL_PROMPT = "auto"
 | 
			
		||||
 | 
			
		||||
INSTALLED_APPS = [
 | 
			
		||||
    "oauth2_provider",
 | 
			
		||||
    "corsheaders",
 | 
			
		||||
]
 | 
			
		||||
MIDDLEWARE = [
 | 
			
		||||
    "oauth2_provider.middleware.OAuth2TokenMiddleware",
 | 
			
		||||
    "corsheaders.middleware.CorsMiddleware",
 | 
			
		||||
]
 | 
			
		||||
AUTHENTICATION_BACKENDS = [
 | 
			
		||||
    "oauth2_provider.backends.OAuth2Backend",
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
OAUTH2_PROVIDER_APPLICATION_MODEL = "passbook_providers_oauth.OAuth2Provider"
 | 
			
		||||
 | 
			
		||||
OAUTH2_PROVIDER = {
 | 
			
		||||
    # this is the list of available scopes
 | 
			
		||||
    "SCOPES": {
 | 
			
		||||
        "openid": "Access OpenID Userinfo",
 | 
			
		||||
        "openid:userinfo": "Access OpenID Userinfo",
 | 
			
		||||
        # 'write': 'Write scope',
 | 
			
		||||
        # 'groups': 'Access to your groups',
 | 
			
		||||
        "user:email": "GitHub Compatibility: User E-Mail",
 | 
			
		||||
        "read:org": "GitHub Compatibility: User Groups",
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,73 @@
 | 
			
		||||
{% extends "login/base.html" %}
 | 
			
		||||
 | 
			
		||||
{% load passbook_utils %}
 | 
			
		||||
{% load i18n %}
 | 
			
		||||
 | 
			
		||||
{% block card_title %}
 | 
			
		||||
{% trans 'Authorize Application' %}
 | 
			
		||||
{% endblock %}
 | 
			
		||||
 | 
			
		||||
{% block card %}
 | 
			
		||||
<form method="POST" class="pf-c-form">
 | 
			
		||||
    {% csrf_token %}
 | 
			
		||||
    {% if not error %}
 | 
			
		||||
        {% csrf_token %}
 | 
			
		||||
        {% for field in form %}
 | 
			
		||||
            {% if field.is_hidden %}
 | 
			
		||||
                {{ field }}
 | 
			
		||||
            {% endif %}
 | 
			
		||||
        {% endfor %}
 | 
			
		||||
        <div class="pf-c-form__group">
 | 
			
		||||
            <p class="subtitle">
 | 
			
		||||
                {% blocktrans with remote=application.name %}
 | 
			
		||||
                You're about to sign into {{ remote }}.
 | 
			
		||||
                {% endblocktrans %}
 | 
			
		||||
            </p>
 | 
			
		||||
            <p>{% trans "Application requires following permissions" %}</p>
 | 
			
		||||
            <ul class="pf-c-list">
 | 
			
		||||
                {% for scope in scopes_descriptions %}
 | 
			
		||||
                <li>{{ scope }}</li>
 | 
			
		||||
                {% endfor %}
 | 
			
		||||
            </ul>
 | 
			
		||||
            {{ form.errors }}
 | 
			
		||||
            {{ form.non_field_errors }}
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="pf-c-form__group">
 | 
			
		||||
            <p>
 | 
			
		||||
                {% blocktrans with user=user %}
 | 
			
		||||
                You are logged in as {{ user }}. Not you?
 | 
			
		||||
                {% endblocktrans %}
 | 
			
		||||
                <a href="{% url 'passbook_flows:default-invalidation' %}">{% trans 'Logout' %}</a>
 | 
			
		||||
            </p>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="pf-c-form__group pf-m-action">
 | 
			
		||||
            <input type="submit" class="pf-c-button pf-m-primary" name="allow" value="{% trans 'Continue' %}">
 | 
			
		||||
            <a href="{% back %}" class="pf-c-button pf-m-secondary">{% trans "Cancel" %}</a>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="pf-c-form__group" style="display: none;" id="loading">
 | 
			
		||||
            <div class="pf-c-form__horizontal-group">
 | 
			
		||||
                <span class="pf-c-spinner" role="progressbar" aria-valuetext="Loading...">
 | 
			
		||||
                    <span class="pf-c-spinner__clipper"></span>
 | 
			
		||||
                    <span class="pf-c-spinner__lead-ball"></span>
 | 
			
		||||
                    <span class="pf-c-spinner__tail-ball"></span>
 | 
			
		||||
                </span>
 | 
			
		||||
            </div>
 | 
			
		||||
        </div>
 | 
			
		||||
    {% else %}
 | 
			
		||||
    <div class="login-group">
 | 
			
		||||
        <p class="subtitle">
 | 
			
		||||
            {% blocktrans with err=error.error %}Error: {{ err }}{% endblocktrans %}
 | 
			
		||||
        </p>
 | 
			
		||||
        <p>{{ error.description }}</p>
 | 
			
		||||
    </div>
 | 
			
		||||
    {% endif %}
 | 
			
		||||
</form>
 | 
			
		||||
{% endblock %}
 | 
			
		||||
 | 
			
		||||
{% block scripts %}
 | 
			
		||||
<script>
 | 
			
		||||
document.querySelector("form").addEventListener("submit", (e) => {
 | 
			
		||||
    document.getElementById("loading").removeAttribute("style");
 | 
			
		||||
});
 | 
			
		||||
</script>
 | 
			
		||||
{% endblock %}
 | 
			
		||||
@ -0,0 +1 @@
 | 
			
		||||
{% extends "base/skeleton.html" %}
 | 
			
		||||
@ -0,0 +1,38 @@
 | 
			
		||||
{% load i18n %}
 | 
			
		||||
<button class="pf-c-button pf-m-tertiary" data-target="modal" data-modal="oauth-{{ provider.pk }}">{% trans 'View Setup URLs' %}</button>
 | 
			
		||||
 | 
			
		||||
<div class="pf-c-backdrop" id="oauth-{{ provider.pk }}" hidden>
 | 
			
		||||
    <div class="pf-l-bullseye">
 | 
			
		||||
        <div class="pf-c-modal-box pf-m-lg" role="dialog">
 | 
			
		||||
            <button data-modal-close class="pf-c-button pf-m-plain" type="button" aria-label="Close dialog">
 | 
			
		||||
                <i class="fas fa-times" aria-hidden="true"></i>
 | 
			
		||||
            </button>
 | 
			
		||||
            <h1 class="pf-c-title pf-m-2xl" id="modal-title">{% trans 'Setup URLs' %}</h1>
 | 
			
		||||
            <div class="pf-c-modal-box__body" id="modal-description">
 | 
			
		||||
                <form class="pf-c-form">
 | 
			
		||||
                    <div class="pf-c-form__group">
 | 
			
		||||
                        <label class="pf-c-form__label" for="help-text-simple-form-name">
 | 
			
		||||
                            <span class="pf-c-form__label-text">{% trans 'Authorize URL' %}</span>
 | 
			
		||||
                        </label>
 | 
			
		||||
                        <input class="pf-c-form-control" readonly type="text" value="{{ authorize_url }}" />
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <div class="pf-c-form__group">
 | 
			
		||||
                        <label class="pf-c-form__label" for="help-text-simple-form-name">
 | 
			
		||||
                            <span class="pf-c-form__label-text">{% trans 'Token URL' %}</span>
 | 
			
		||||
                        </label>
 | 
			
		||||
                        <input class="pf-c-form-control" readonly type="text" value="{{ token_url }}" />
 | 
			
		||||
                    </div>
 | 
			
		||||
                    <div class="pf-c-form__group">
 | 
			
		||||
                        <label class="pf-c-form__label" for="help-text-simple-form-name">
 | 
			
		||||
                            <span class="pf-c-form__label-text">{% trans 'Userinfo Endpoint' %}</span>
 | 
			
		||||
                        </label>
 | 
			
		||||
                        <input class="pf-c-form-control" readonly type="text" value="{{ userinfo_url }}" />
 | 
			
		||||
                    </div>
 | 
			
		||||
                </form>
 | 
			
		||||
            </div>
 | 
			
		||||
            <footer class="pf-c-modal-box__footer pf-m-align-left">
 | 
			
		||||
                <button data-modal-close class="pf-c-button pf-m-primary" type="button">{% trans 'Close' %}</button>
 | 
			
		||||
            </footer>
 | 
			
		||||
        </div>
 | 
			
		||||
    </div>
 | 
			
		||||
</div>
 | 
			
		||||
							
								
								
									
										48
									
								
								passbook/providers/oauth/urls.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								passbook/providers/oauth/urls.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,48 @@
 | 
			
		||||
"""passbook oauth_provider urls"""
 | 
			
		||||
 | 
			
		||||
from django.urls import include, path
 | 
			
		||||
from oauth2_provider import views
 | 
			
		||||
 | 
			
		||||
from passbook.providers.oauth.views import github, oauth2
 | 
			
		||||
 | 
			
		||||
oauth_urlpatterns = [
 | 
			
		||||
    # Custom OAuth 2 Authorize View
 | 
			
		||||
    path(
 | 
			
		||||
        "authorize/",
 | 
			
		||||
        oauth2.PassbookAuthorizationLoadingView.as_view(),
 | 
			
		||||
        name="oauth2-authorize",
 | 
			
		||||
    ),
 | 
			
		||||
    path(
 | 
			
		||||
        "authorize/permission_ok/",
 | 
			
		||||
        oauth2.PassbookAuthorizationView.as_view(),
 | 
			
		||||
        name="oauth2-ok-authorize",
 | 
			
		||||
    ),
 | 
			
		||||
    path(
 | 
			
		||||
        "authorize/permission_denied/",
 | 
			
		||||
        oauth2.OAuthPermissionDenied.as_view(),
 | 
			
		||||
        name="oauth2-permission-denied",
 | 
			
		||||
    ),
 | 
			
		||||
    # OAuth API
 | 
			
		||||
    path("token/", views.TokenView.as_view(), name="token"),
 | 
			
		||||
    path("revoke_token/", views.RevokeTokenView.as_view(), name="revoke-token"),
 | 
			
		||||
    path("introspect/", views.IntrospectTokenView.as_view(), name="introspect"),
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
github_urlpatterns = [
 | 
			
		||||
    path(
 | 
			
		||||
        "login/oauth/authorize",
 | 
			
		||||
        oauth2.PassbookAuthorizationView.as_view(),
 | 
			
		||||
        name="github-authorize",
 | 
			
		||||
    ),
 | 
			
		||||
    path(
 | 
			
		||||
        "login/oauth/access_token",
 | 
			
		||||
        views.TokenView.as_view(),
 | 
			
		||||
        name="github-access-token",
 | 
			
		||||
    ),
 | 
			
		||||
    path("user", github.GitHubUserView.as_view(), name="github-user"),
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
urlpatterns = [
 | 
			
		||||
    path("", include(github_urlpatterns)),
 | 
			
		||||
    path("application/oauth/", include(oauth_urlpatterns)),
 | 
			
		||||
]
 | 
			
		||||
							
								
								
									
										0
									
								
								passbook/providers/oauth/views/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								passbook/providers/oauth/views/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										67
									
								
								passbook/providers/oauth/views/github.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								passbook/providers/oauth/views/github.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,67 @@
 | 
			
		||||
"""passbook pretend GitHub Views"""
 | 
			
		||||
from django.http import JsonResponse
 | 
			
		||||
from django.shortcuts import get_object_or_404
 | 
			
		||||
from django.views import View
 | 
			
		||||
from oauth2_provider.models import AccessToken
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class GitHubUserView(View):
 | 
			
		||||
    """Emulate GitHub's /user API Endpoint"""
 | 
			
		||||
 | 
			
		||||
    def verify_access_token(self):
 | 
			
		||||
        """Verify access token manually since github uses /user?access_token=..."""
 | 
			
		||||
        token = get_object_or_404(
 | 
			
		||||
            AccessToken, token=self.request.GET.get("access_token", "")
 | 
			
		||||
        )
 | 
			
		||||
        return token.user
 | 
			
		||||
 | 
			
		||||
    def get(self, request):
 | 
			
		||||
        """Emulate GitHub's /user API Endpoint"""
 | 
			
		||||
        user = self.verify_access_token()
 | 
			
		||||
        return JsonResponse(
 | 
			
		||||
            {
 | 
			
		||||
                "login": user.username,
 | 
			
		||||
                "id": user.pk,
 | 
			
		||||
                "node_id": "",
 | 
			
		||||
                "avatar_url": "",
 | 
			
		||||
                "gravatar_id": "",
 | 
			
		||||
                "url": "",
 | 
			
		||||
                "html_url": "",
 | 
			
		||||
                "followers_url": "",
 | 
			
		||||
                "following_url": "",
 | 
			
		||||
                "gists_url": "",
 | 
			
		||||
                "starred_url": "",
 | 
			
		||||
                "subscriptions_url": "",
 | 
			
		||||
                "organizations_url": "",
 | 
			
		||||
                "repos_url": "",
 | 
			
		||||
                "events_url": "",
 | 
			
		||||
                "received_events_url": "",
 | 
			
		||||
                "type": "User",
 | 
			
		||||
                "site_admin": False,
 | 
			
		||||
                "name": user.name,
 | 
			
		||||
                "company": "",
 | 
			
		||||
                "blog": "",
 | 
			
		||||
                "location": "",
 | 
			
		||||
                "email": user.email,
 | 
			
		||||
                "hireable": False,
 | 
			
		||||
                "bio": "",
 | 
			
		||||
                "public_repos": 0,
 | 
			
		||||
                "public_gists": 0,
 | 
			
		||||
                "followers": 0,
 | 
			
		||||
                "following": 0,
 | 
			
		||||
                "created_at": user.date_joined,
 | 
			
		||||
                "updated_at": user.date_joined,
 | 
			
		||||
                "private_gists": 0,
 | 
			
		||||
                "total_private_repos": 0,
 | 
			
		||||
                "owned_private_repos": 0,
 | 
			
		||||
                "disk_usage": 0,
 | 
			
		||||
                "collaborators": 0,
 | 
			
		||||
                "two_factor_authentication": True,
 | 
			
		||||
                "plan": {
 | 
			
		||||
                    "name": "None",
 | 
			
		||||
                    "space": 0,
 | 
			
		||||
                    "private_repos": 0,
 | 
			
		||||
                    "collaborators": 0,
 | 
			
		||||
                },
 | 
			
		||||
            }
 | 
			
		||||
        )
 | 
			
		||||
							
								
								
									
										90
									
								
								passbook/providers/oauth/views/oauth2.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										90
									
								
								passbook/providers/oauth/views/oauth2.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,90 @@
 | 
			
		||||
"""passbook OAuth2 Views"""
 | 
			
		||||
from typing import Optional
 | 
			
		||||
from urllib.parse import urlencode
 | 
			
		||||
 | 
			
		||||
from django.contrib import messages
 | 
			
		||||
from django.contrib.auth.mixins import LoginRequiredMixin
 | 
			
		||||
from django.forms import Form
 | 
			
		||||
from django.http import HttpRequest, HttpResponse
 | 
			
		||||
from django.shortcuts import get_object_or_404, redirect, reverse
 | 
			
		||||
from django.utils.translation import ugettext as _
 | 
			
		||||
from oauth2_provider.views.base import AuthorizationView
 | 
			
		||||
from structlog import get_logger
 | 
			
		||||
 | 
			
		||||
from passbook.audit.models import Event, EventAction
 | 
			
		||||
from passbook.core.models import Application
 | 
			
		||||
from passbook.core.views.access import AccessMixin
 | 
			
		||||
from passbook.core.views.utils import LoadingView, PermissionDeniedView
 | 
			
		||||
from passbook.providers.oauth.models import OAuth2Provider
 | 
			
		||||
 | 
			
		||||
LOGGER = get_logger()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PassbookAuthorizationLoadingView(LoginRequiredMixin, LoadingView):
 | 
			
		||||
    """Show loading view for permission checks"""
 | 
			
		||||
 | 
			
		||||
    title = _("Checking permissions...")
 | 
			
		||||
 | 
			
		||||
    def get_url(self):
 | 
			
		||||
        querystring = urlencode(self.request.GET)
 | 
			
		||||
        return (
 | 
			
		||||
            reverse("passbook_providers_oauth:oauth2-ok-authorize") + "?" + querystring
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class OAuthPermissionDenied(PermissionDeniedView):
 | 
			
		||||
    """Show permission denied view"""
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class PassbookAuthorizationView(AccessMixin, AuthorizationView):
 | 
			
		||||
    """Custom OAuth2 Authorization View which checks policies, etc"""
 | 
			
		||||
 | 
			
		||||
    _application: Optional[Application] = None
 | 
			
		||||
 | 
			
		||||
    def _inject_response_type(self):
 | 
			
		||||
        """Inject response_type into querystring if not set"""
 | 
			
		||||
        LOGGER.debug("response_type not set, defaulting to 'code'")
 | 
			
		||||
        querystring = urlencode(self.request.GET)
 | 
			
		||||
        querystring += "&response_type=code"
 | 
			
		||||
        return redirect(
 | 
			
		||||
            reverse("passbook_providers_oauth:oauth2-ok-authorize") + "?" + querystring
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def dispatch(self, request: HttpRequest, *args, **kwargs) -> HttpResponse:
 | 
			
		||||
        """Update OAuth2Provider's skip_authorization state"""
 | 
			
		||||
        # Get client_id to get provider, so we can update skip_authorization field
 | 
			
		||||
        client_id = request.GET.get("client_id")
 | 
			
		||||
        provider = get_object_or_404(OAuth2Provider, client_id=client_id)
 | 
			
		||||
        try:
 | 
			
		||||
            application = self.provider_to_application(provider)
 | 
			
		||||
        except Application.DoesNotExist:
 | 
			
		||||
            return redirect("passbook_providers_oauth:oauth2-permission-denied")
 | 
			
		||||
        # Update field here so oauth-toolkit does work for us
 | 
			
		||||
        provider.skip_authorization = application.skip_authorization
 | 
			
		||||
        provider.save()
 | 
			
		||||
        self._application = application
 | 
			
		||||
        # Check permissions
 | 
			
		||||
        passing, policy_messages = self.user_has_access(self._application, request.user)
 | 
			
		||||
        if not passing:
 | 
			
		||||
            for policy_message in policy_messages:
 | 
			
		||||
                messages.error(request, policy_message)
 | 
			
		||||
            return redirect("passbook_providers_oauth:oauth2-permission-denied")
 | 
			
		||||
        # Some clients don't pass response_type, so we default to code
 | 
			
		||||
        if "response_type" not in request.GET:
 | 
			
		||||
            return self._inject_response_type()
 | 
			
		||||
        actual_response = AuthorizationView.dispatch(self, request, *args, **kwargs)
 | 
			
		||||
        if actual_response.status_code == 400:
 | 
			
		||||
            LOGGER.debug("Bad request", redirect_uri=request.GET.get("redirect_uri"))
 | 
			
		||||
        return actual_response
 | 
			
		||||
 | 
			
		||||
    def form_valid(self, form: Form):
 | 
			
		||||
        # User has clicked on "Authorize"
 | 
			
		||||
        Event.new(
 | 
			
		||||
            EventAction.AUTHORIZE_APPLICATION, authorized_application=self._application,
 | 
			
		||||
        ).from_http(self.request)
 | 
			
		||||
        LOGGER.debug(
 | 
			
		||||
            "User authorized Application",
 | 
			
		||||
            user=self.request.user,
 | 
			
		||||
            application=self._application,
 | 
			
		||||
        )
 | 
			
		||||
        return AuthorizationView.form_valid(self, form)
 | 
			
		||||
		Reference in New Issue
	
	Block a user