Compare commits

..

25 Commits

Author SHA1 Message Date
6f7b917c38 bump version: 0.1.23-beta -> 0.1.24-beta 2019-03-20 23:00:33 +01:00
1456ee6d3e prepare 0.1.24 2019-03-20 23:00:22 +01:00
5155204283 Merge branch '32-automatically-set-owner-field-when-creating-oauth-provider' into 'master'
Resolve "Automatically set owner field when creating OAuth Provider"

Closes #32

See merge request BeryJu.org/passbook!16
2019-03-20 21:20:54 +00:00
5509ec9b0f Merge branch '29-oauth-provider-add-extra-info-button-to-show-urls' into 'master'
Resolve "OAuth Provider: Add extra info button to show URLs"

Closes #29

See merge request BeryJu.org/passbook!15
2019-03-20 21:17:36 +00:00
d6f9b2e47d remove user field from form. Closes #32 2019-03-20 20:09:27 +01:00
67aa4aef11 add modal for OAuth Providers showing the URLs 2019-03-20 20:03:28 +01:00
9e46c8bfec bump version: 0.1.22-beta -> 0.1.23-beta 2019-03-18 20:54:31 +01:00
1eaa9b9733 prepare 0.1.23 2019-03-18 20:54:23 +01:00
ee05834b69 Merge branch '28-openid-connect-discovery' into 'master'
set issuer to root address instead of well-known path

Closes #28

See merge request BeryJu.org/passbook!13
2019-03-18 19:52:13 +00:00
fccc8f4959 set issuer to root address instead of well-known path 2019-03-18 20:42:32 +01:00
c721620f96 Merge branch '28-openid-connect-discovery' into 'master'
Resolve "OpenID Connect Discovery support"

Closes #28

See merge request BeryJu.org/passbook!12
2019-03-18 19:42:08 +00:00
c9f73d718e start implementing openid connect discovery 2019-03-18 20:35:11 +01:00
bfa58be721 bump version: 0.1.21-beta -> 0.1.22-beta 2019-03-14 21:22:15 +01:00
4bb602149e perapre 0.1.22 2019-03-14 21:20:55 +01:00
81ab9092fc Fix OAuth Client's disconnect view having invalid URL names 2019-03-14 21:19:14 +01:00
29d5962c4c add Azure AD Source 2019-03-14 21:18:55 +01:00
5c75339946 point to correct icons 2019-03-14 21:18:13 +01:00
4774d9a46c fix delete form not working 2019-03-14 21:17:41 +01:00
dbe16ba4fd fix layout when on mobile viewport and scrolling 2019-03-14 21:17:28 +01:00
6972cf00a0 move icons to single folder, cleanup 2019-03-14 21:17:07 +01:00
0445be9712 fix missing debug template 2019-03-14 21:16:27 +01:00
89dbdd9585 bump version: 0.1.20-beta -> 0.1.21-beta 2019-03-14 18:08:02 +01:00
da88ce7150 prepare 0.1.21 2019-03-14 18:02:13 +01:00
5f50fcfcf5 detect HTTPS from reverse proxy 2019-03-14 18:01:41 +01:00
96be087221 add request debug view 2019-03-14 18:01:27 +01:00
73 changed files with 355 additions and 73 deletions

View File

@ -1,5 +1,5 @@
[bumpversion]
current_version = 0.1.20-beta
current_version = 0.1.24-beta
tag = True
commit = True
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)\-(?P<release>.*)

View File

@ -54,7 +54,7 @@ package-docker:
before_script:
- echo "{\"auths\":{\"docker.$NEXUS_URL\":{\"auth\":\"$NEXUS_AUTH\"}}}" > /kaniko/.docker/config.json
script:
- /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile --destination docker.pkg.beryju.org/passbook:latest --destination docker.pkg.beryju.org/passbook:0.1.20-beta
- /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile --destination docker.pkg.beryju.org/passbook:latest --destination docker.pkg.beryju.org/passbook:0.1.24-beta
stage: build
only:
- tags

View File

@ -3,7 +3,7 @@ from setuptools import setup
setup(
name='django-allauth-passbook',
version='0.1.20-beta',
version='0.1.24-beta',
description='passbook support for django-allauth',
# long_description='\n'.join(read_simple('docs/index.md')[2:]),
long_description_content_type='text/markdown',

View File

@ -18,7 +18,7 @@ tests_require = [
setup(
name='sentry-auth-passbook',
version='0.1.20-beta',
version='0.1.24-beta',
author='BeryJu.org',
author_email='support@beryju.org',
url='https://passbook.beryju.org',

37
debian/changelog vendored
View File

@ -1,10 +1,45 @@
passbook (0.1.24) stable; urgency=medium
* bump version: 0.1.22-beta -> 0.1.23-beta
* add modal for OAuth Providers showing the URLs
* remove user field from form. Closes #32
-- Jens Langhammer <jens.langhammer@beryju.org> Wed, 20 Mar 2019 21:59:21 +0000
passbook (0.1.23) stable; urgency=medium
* add support for OpenID-Connect Discovery
-- Jens Langhammer <jens.langhammer@beryju.org> Thu, 18 Mar 2019 20:19:27 +0000
passbook (0.1.22) stable; urgency=medium
* bump version: 0.1.20-beta -> 0.1.21-beta
* fix missing debug template
* move icons to single folder, cleanup
* fix layout when on mobile viewport and scrolling
* fix delete form not working
* point to correct icons
* add Azure AD Source
* Fix OAuth Client's disconnect view having invalid URL names
-- Jens Langhammer <jens.langhammer@beryju.org> Thu, 14 Mar 2019 20:19:27 +0000
passbook (0.1.21) stable; urgency=medium
* bump version: 0.1.19-beta -> 0.1.20-beta
* add request debug view
* detect HTTPS from reverse proxy
-- Jens Langhammer <jens.langhammer@beryju.org> Thu, 14 Mar 2019 17:01:49 +0000
passbook (0.1.20) stable; urgency=medium
* bump version: 0.1.18-beta -> 0.1.19-beta
* fix GitHub Pretend again
* add user settings for Sources
-- Jens Langhammer <jens.langhammer@beryju.org> Wed, 13 Mar 2019 15:49:44 +0000
-- Jens Langhammer <jens.langhammer@beryju.org> Wed, 13 Mar 2019 15:49:44 +0000
passbook (0.1.18) stable; urgency=medium

View File

@ -1,6 +1,6 @@
apiVersion: v1
appVersion: "0.1.20-beta"
appVersion: "0.1.24-beta"
description: A Helm chart for passbook.
name: passbook
version: "0.1.20-beta"
version: "0.1.24-beta"
icon: https://passbook.beryju.org/images/logo.png

View File

@ -123,6 +123,7 @@ data:
- passbook.oauth_client.source_types.reddit
- passbook.oauth_client.source_types.supervisr
- passbook.oauth_client.source_types.twitter
- passbook.oauth_client.source_types.azure_ad
saml_idp:
signing: true
autosubmit: false

View File

@ -5,7 +5,7 @@
replicaCount: 1
image:
tag: 0.1.20-beta
tag: 0.1.24-beta
nameOverride: ""

View File

@ -1,2 +1,2 @@
"""passbook"""
__version__ = '0.1.20-beta'
__version__ = '0.1.24-beta'

View File

@ -1,2 +1,2 @@
"""passbook admin"""
__version__ = '0.1.20-beta'
__version__ = '0.1.24-beta'

View File

@ -0,0 +1,31 @@
{% extends "administration/base.html" %}
{% load i18n %}
{% load utils %}
{% block title %}
{% title %}
{% endblock %}
{% block content %}
<div class="container">
<h1><span class="pficon-applications"></span> {% trans "Request" %}</h1>
<hr>
<table class="table table-striped table-bordered">
<thead>
<tr>
<th>{% trans 'Key' %}</th>
<th>{% trans 'Value' %}</th>
</tr>
</thead>
<tbody>
{% for key, value in request_dict.items %}
<tr>
<td>{{ key }}</td>
<td>{{ value }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% endblock %}

View File

@ -57,6 +57,10 @@
<a class="btn btn-default btn-sm"
href="{{ href }}?back={{ request.get_full_path }}">{% trans name %}</a>
{% endfor %}
{% get_htmls provider as htmls %}
{% for html in htmls %}
{{ html|safe }}
{% endfor %}
</td>
</tr>
{% endfor %}

View File

@ -5,6 +5,8 @@ from logging import getLogger
from django import template
from django.db.models import Model
from passbook.lib.utils.template import render_to_string
register = template.Library()
LOGGER = getLogger(__name__)
@ -29,3 +31,24 @@ def get_links(model_instance):
pass
return links
@register.simple_tag(takes_context=True)
def get_htmls(context, model_instance):
"""Find all html_ methods on an object instance, run them and return as dict"""
prefix = 'html_'
htmls = []
if not isinstance(model_instance, Model):
LOGGER.warning("Model %s is not instance of Model", model_instance)
return htmls
try:
for name, method in inspect.getmembers(model_instance, predicate=inspect.ismethod):
if name.startswith(prefix):
template, _context = method(context.get('request'))
htmls.append(render_to_string(template, _context))
except NotImplementedError:
pass
return htmls

View File

@ -1,7 +1,7 @@
"""passbook URL Configuration"""
from django.urls import include, path
from passbook.admin.views import (applications, audit, factors, groups,
from passbook.admin.views import (applications, audit, debug, factors, groups,
invitations, overview, policy,
property_mapping, providers, sources, users)
@ -77,5 +77,7 @@ urlpatterns = [
# Groups
path('groups/', groups.GroupListView.as_view(), name='groups'),
# API
path('api/', include('passbook.admin.api.urls'))
path('api/', include('passbook.admin.api.urls')),
# Debug
path('debug/request/', debug.DebugRequestView.as_view(), name='debug-request'),
]

View File

@ -0,0 +1,17 @@
"""passbook administration debug views"""
from django.views.generic import TemplateView
from passbook.admin.mixins import AdminRequiredMixin
class DebugRequestView(AdminRequiredMixin, TemplateView):
"""Show debug info about request"""
template_name = 'administration/debug/request.html'
def get_context_data(self, **kwargs):
kwargs['request_dict'] = {}
for key in dir(self.request):
kwargs['request_dict'][key] = getattr(self.request, key)
return super().get_context_data(**kwargs)

View File

@ -1,2 +1,2 @@
"""passbook api"""
__version__ = '0.1.20-beta'
__version__ = '0.1.24-beta'

View File

@ -1,2 +1,2 @@
"""passbook audit Header"""
__version__ = '0.1.20-beta'
__version__ = '0.1.24-beta'

View File

@ -1,2 +1,2 @@
"""passbook captcha_factor Header"""
__version__ = '0.1.20-beta'
__version__ = '0.1.24-beta'

View File

@ -1,2 +1,2 @@
"""passbook core"""
__version__ = '0.1.20-beta'
__version__ = '0.1.24-beta'

View File

@ -35,6 +35,7 @@ SECRET_KEY = CONFIG.get('secret_key')
DEBUG = CONFIG.get('debug')
INTERNAL_IPS = ['127.0.0.1']
ALLOWED_HOSTS = CONFIG.get('domains', [])
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
LOGIN_URL = 'passbook_core:auth-login'
# CSRF_FAILURE_VIEW = 'passbook.core.views.errors.CSRFErrorView.as_view'

View File

@ -1,19 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="20px" height="20px" viewBox="0 0 20 20" style="enable-background:new 0 0 20 20;" xml:space="preserve">
<style type="text/css">
.st0{fill:#FBBB00;}
.st1{fill:#518EF8;}
.st2{fill:#28B446;}
.st3{fill:#F14336;}
</style>
<path class="st0" d="M4.4,12.1l-0.7,2.6l-2.5,0.1C0.4,13.3,0,11.7,0,10c0-1.7,0.4-3.2,1.1-4.6h0l2.3,0.4l1,2.3
C4.2,8.7,4.1,9.3,4.1,10C4.1,10.7,4.2,11.4,4.4,12.1z"/>
<path class="st1" d="M19.8,8.1C19.9,8.7,20,9.4,20,10c0,0.7-0.1,1.4-0.2,2.1c-0.5,2.3-1.8,4.3-3.5,5.7l0,0l-2.9-0.1L13,15.1
c1.2-0.7,2.1-1.8,2.6-3h-5.3v-4h5.4H19.8L19.8,8.1z"/>
<path class="st2" d="M16.3,17.8L16.3,17.8C14.5,19.2,12.4,20,10,20c-3.8,0-7.1-2.1-8.8-5.3l3.2-2.7c0.8,2.3,3,3.9,5.6,3.9
c1.1,0,2.1-0.3,3-0.8L16.3,17.8z"/>
<path class="st3" d="M16.4,2.3L13.1,5c-0.9-0.6-2-0.9-3.1-0.9c-2.6,0-4.8,1.7-5.6,4L1.1,5.4h0C2.8,2.2,6.1,0,10,0
C12.4,0,14.7,0.9,16.4,2.3z"/>
</svg>

Before

Width:  |  Height:  |  Size: 1.1 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 15 KiB

View File

Before

Width:  |  Height:  |  Size: 2.7 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

View File

Before

Width:  |  Height:  |  Size: 6.4 KiB

After

Width:  |  Height:  |  Size: 6.4 KiB

View File

Before

Width:  |  Height:  |  Size: 9.4 KiB

After

Width:  |  Height:  |  Size: 9.4 KiB

View File

Before

Width:  |  Height:  |  Size: 4.4 KiB

After

Width:  |  Height:  |  Size: 4.4 KiB

View File

Before

Width:  |  Height:  |  Size: 7.8 KiB

After

Width:  |  Height:  |  Size: 7.8 KiB

View File

Before

Width:  |  Height:  |  Size: 8.0 KiB

After

Width:  |  Height:  |  Size: 8.0 KiB

View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" width="21" height="21" viewBox="0 0 21 21"><title>MS-SymbolLockup</title><rect x="1" y="1" width="9" height="9" fill="#f25022"/><rect x="1" y="11" width="9" height="9" fill="#00a4ef"/><rect x="11" y="1" width="9" height="9" fill="#7fba00"/><rect x="11" y="11" width="9" height="9" fill="#ffb900"/></svg>

After

Width:  |  Height:  |  Size: 343 B

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

Before

Width:  |  Height:  |  Size: 750 B

After

Width:  |  Height:  |  Size: 750 B

View File

Before

Width:  |  Height:  |  Size: 814 B

After

Width:  |  Height:  |  Size: 814 B

View File

Before

Width:  |  Height:  |  Size: 788 B

After

Width:  |  Height:  |  Size: 788 B

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

Before

Width:  |  Height:  |  Size: 783 B

After

Width:  |  Height:  |  Size: 783 B

View File

Before

Width:  |  Height:  |  Size: 688 B

After

Width:  |  Height:  |  Size: 688 B

View File

Before

Width:  |  Height:  |  Size: 1.9 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

Before

Width:  |  Height:  |  Size: 6.0 KiB

After

Width:  |  Height:  |  Size: 6.0 KiB

View File

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

Before

Width:  |  Height:  |  Size: 889 B

After

Width:  |  Height:  |  Size: 889 B

View File

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB

View File

Before

Width:  |  Height:  |  Size: 8.8 KiB

After

Width:  |  Height:  |  Size: 8.8 KiB

View File

Before

Width:  |  Height:  |  Size: 23 KiB

After

Width:  |  Height:  |  Size: 23 KiB

View File

Before

Width:  |  Height:  |  Size: 4.0 KiB

After

Width:  |  Height:  |  Size: 4.0 KiB

View File

Before

Width:  |  Height:  |  Size: 1.8 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

Before

Width:  |  Height:  |  Size: 1.0 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

View File

@ -3,7 +3,7 @@
{% load utils %}
<!DOCTYPE html>
<html lang="en">
<html lang="en" class="layout-pf layout-pf-fixed transitions">
<head>
<meta charset="UTF-8">

View File

@ -16,6 +16,7 @@
Are you sure you want to delete {{ object_type }} "{{ object }}"?
{% endblocktrans %}
</p>
<input type="hidden" name="confirmdelete" value="yes">
<a href="{% back %}" class="btn btn-default">{% trans 'Back' %}</a>
<input type="submit" class="btn btn-danger" value="{% trans 'Delete' %}" />
</form>

View File

@ -51,7 +51,7 @@
{% for url, icon, name in sources %}
<li class="login-pf-social-link">
<a href="{{ url }}">
<img src="{% static 'img/' %}{{ icon }}.svg" alt="{{ name }}"> {{ name }}
<img src="{% static 'img/logos/' %}{{ icon }}.svg" alt="{{ name }}"> {{ name }}
</a>
</li>
{% endfor %}

View File

@ -172,19 +172,27 @@
</span>
</a>
</li>
<li class="list-group-item {% is_active 'passbook_admin:debug-request' %}">
<a href="{% url 'passbook_admin:debug-request' %}">
<span class="list-group-item-value">
{% trans 'Debug' %}
</span>
</a>
</li>
</ul>
</div>
</li>
{% endif %}
</ul>
</div>
<div class="container-fluid container-cards-pf">
<div class="container-fluid container-cards-pf container-pf-nav-pf-vertical hide-nav-pf">
{% block content %}
{% endblock %}
</div>
{% endblock %}
{% block scripts %}
{{ block.super }}
<script>
$(document).ready(function () {
// initialize tooltips

View File

@ -30,7 +30,7 @@
{% for name, icon, link in us %}
<li class="{% if link == request.get_full_path %} active {% endif %}">
<a href="{{ link }}">
<img src="{% static icon %}" alt=""> {{ name }}
<i class="{{ icon }}"></i> {{ name }}
</a>
</li>
{% endfor %}

View File

@ -1,2 +1,2 @@
"""passbook hibp_policy"""
__version__ = '0.1.20-beta'
__version__ = '0.1.24-beta'

View File

@ -1,2 +1,2 @@
"""Passbook ldap app Header"""
__version__ = '0.1.20-beta'
__version__ = '0.1.24-beta'

View File

@ -1,2 +1,2 @@
"""passbook lib"""
__version__ = '0.1.20-beta'
__version__ = '0.1.24-beta'

View File

@ -85,6 +85,7 @@ oauth_client:
- passbook.oauth_client.source_types.reddit
- passbook.oauth_client.source_types.supervisr
- passbook.oauth_client.source_types.twitter
- passbook.oauth_client.source_types.azure_ad
saml_idp:
# List of python packages with provider types to load.
types:

View File

@ -1,2 +1,2 @@
"""passbook oauth_client Header"""
__version__ = '0.1.20-beta'
__version__ = '0.1.24-beta'

View File

@ -108,3 +108,17 @@ class GoogleOAuthSourceForm(OAuthSourceForm):
'access_token_url': 'https://accounts.google.com/o/oauth2/token',
'profile_url': ' https://www.googleapis.com/oauth2/v1/userinfo',
}
class AzureADOAuthSourceForm(OAuthSourceForm):
"""OAuth Source form with pre-determined URL for AzureAD"""
class Meta(OAuthSourceForm.Meta):
overrides = {
'provider_type': 'azure_ad',
'request_token_url': '',
'authorization_url': 'https://login.microsoftonline.com/common/oauth2/authorize',
'access_token_url': 'https://login.microsoftonline.com/common/oauth2/token',
'profile_url': ' https://graph.windows.net/myorganization/me?api-version=1.6',
}

View File

@ -29,9 +29,9 @@ class OAuthSource(Source):
def get_login_button(self):
url = reverse_lazy('passbook_oauth_client:oauth-client-login',
kwargs={'source_slug': self.slug})
if self.provider_type == 'github':
return url, 'github-logo', _('GitHub')
return url, 'generic', _('Generic')
# if self.provider_type == 'github':
# return url, 'github-logo', _('GitHub')
return url, self.provider_type, self.name
@property
def additional_info(self):
@ -42,9 +42,12 @@ class OAuthSource(Source):
"""Entrypoint to integrate with User settings. Can either return False if no
user settings are available, or a tuple or string, string, string where the first string
is the name the item has, the second string is the icon and the third is the view-name."""
icon = 'img/%s.svg' % self.get_login_button[1]
icon_type = self.provider_type
if icon_type == 'azure ad':
icon_type = 'windows'
icon_class = 'fa fa-%s' % icon_type
view_name = 'passbook_oauth_client:oauth-client-user'
return self.name, icon, reverse((view_name), kwargs={
return self.name, icon_class, reverse((view_name), kwargs={
'source_slug': self.slug
})
@ -113,6 +116,19 @@ class GoogleOAuthSource(OAuthSource):
verbose_name = _('Google OAuth Source')
verbose_name_plural = _('Google OAuth Sources')
class AzureADOAuthSource(OAuthSource):
"""Abstract subclass of OAuthSource to specify AzureAD Form"""
form = 'passbook.oauth_client.forms.AzureADOAuthSourceForm'
class Meta:
abstract = True
verbose_name = _('Azure AD OAuth Source')
verbose_name_plural = _('Azure AD OAuth Sources')
class UserOAuthSourceConnection(UserSourceConnection):
"""Authorized remote OAuth provider."""

View File

@ -0,0 +1,52 @@
"""AzureAD OAuth2 Views"""
import json
import uuid
from logging import getLogger
from requests.exceptions import RequestException
from passbook.oauth_client.clients import OAuth2Client
from passbook.oauth_client.source_types.manager import MANAGER, RequestKind
from passbook.oauth_client.utils import user_get_or_create
from passbook.oauth_client.views.core import OAuthCallback
LOGGER = getLogger(__name__)
class AzureADOAuth2Client(OAuth2Client):
"""AzureAD OAuth2 Client"""
def get_profile_info(self, raw_token):
"Fetch user profile information."
try:
token = json.loads(raw_token)['access_token']
headers = {
'Authorization': 'Bearer %s' % token
}
response = self.request('get', self.source.profile_url,
headers=headers)
response.raise_for_status()
except RequestException as exc:
LOGGER.warning('Unable to fetch user profile: %s', exc)
return None
else:
return response.json() or response.text
@MANAGER.source(kind=RequestKind.callback, name='Azure AD')
class AzureADOAuthCallback(OAuthCallback):
"""AzureAD OAuth2 Callback"""
client_class = AzureADOAuth2Client
def get_user_id(self, source, info):
return uuid.UUID(info.get('objectId')).int
def get_or_create_user(self, source, access, info):
user_data = {
'username': info.get('displayName'),
'email': info.get('mail', None) or info.get('otherMails')[0],
'name': info.get('displayName'),
'password': None,
}
return user_get_or_create(**user_data)

View File

@ -194,7 +194,9 @@ class OAuthCallback(OAuthClientMixin, View):
messages.success(self.request, _("Successfully linked %(source)s!" % {
'source': self.source.name
}))
return redirect(reverse('user_settings'))
return redirect(reverse('passbook_oauth_client:oauth-client-user', kwargs={
'source_slug': self.source.slug
}))
messages.success(self.request, _("Successfully authenticated with %(source)s!" % {
'source': self.source.name
}))
@ -207,26 +209,28 @@ class DisconnectView(LoginRequiredMixin, View):
source = None
aas = None
def dispatch(self, request, source):
self.source = get_object_or_404(OAuthSource, name=source)
def dispatch(self, request, source_slug):
self.source = get_object_or_404(OAuthSource, slug=source_slug)
self.aas = get_object_or_404(UserOAuthSourceConnection,
source=self.source, user=request.user)
return super().dispatch(request, source)
return super().dispatch(request, source_slug)
def post(self, request, source):
def post(self, request, source_slug):
"""Delete connection object"""
if 'confirmdelete' in request.POST:
# User confirmed deletion
self.aas.delete()
messages.success(request, _('Connection successfully deleted'))
return redirect(reverse('user_settings'))
return self.get(request, source)
return redirect(reverse('passbook_oauth_client:oauth-client-user', kwargs={
'source_slug': self.source.slug
}))
return self.get(request, source_slug)
def get(self, request, source):
"""Show delete form"""
return render(request, 'generic/delete.html', {
'object': 'OAuth Connection with %s' % self.source.name,
'delete_url': reverse('oauth-client-disconnect', kwargs={
'source': self.source.name,
'object': self.source,
'delete_url': reverse('passbook_oauth_client:oauth-client-disconnect', kwargs={
'source_slug': self.source.slug,
})
})

View File

@ -1,2 +1,2 @@
"""passbook oauth_provider Header"""
__version__ = '0.1.20-beta'
__version__ = '0.1.24-beta'

View File

@ -11,5 +11,5 @@ class OAuth2ProviderForm(forms.ModelForm):
class Meta:
model = OAuth2Provider
fields = ['name', 'user', 'redirect_uris', 'client_type',
fields = ['name', 'redirect_uris', 'client_type',
'authorization_grant_type', 'client_id', 'client_secret', ]

View File

@ -1,5 +1,6 @@
"""Oauth2 provider product extension"""
from django.shortcuts import reverse
from django.utils.translation import gettext as _
from oauth2_provider.models import AbstractApplication
@ -14,6 +15,20 @@ class OAuth2Provider(Provider, AbstractApplication):
def __str__(self):
return "OAuth2 Provider %s" % self.name
def html_setup_urls(self, request):
"""return template and context modal with URLs for authorize, token, openid-config, etc"""
return "oauth2_provider/setup_url_modal.html", {
'provider': self,
'authorize_url': request.build_absolute_uri(
reverse('passbook_oauth_provider:oauth2-authorize')),
'token_url': request.build_absolute_uri(
reverse('passbook_oauth_provider:token')),
'userinfo_url': request.build_absolute_uri(
reverse('passbook_api:openid')),
'openid_url': request.build_absolute_uri(
reverse('passbook_oauth_provider:openid-discovery'))
}
class Meta:
verbose_name = _('OAuth2 Provider')

View File

@ -0,0 +1,49 @@
{% load i18n %}
<button class="btn btn-default btn-sm" data-toggle="modal" data-target="#{{ provider.pk }}">{% trans 'View Setup URLs' %}</button>
<div class="modal fade" id="{{ provider.pk }}" tabindex="-1" role="dialog" aria-labelledby="{{ provider.pk }}Label" aria-hidden="true">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true" aria-label="Close">
<span class="pficon pficon-close"></span>
</button>
<h4 class="modal-title" id="{{ provider.pk }}Label">{% trans 'Setup URLs' %}</h4>
</div>
<div class="modal-body">
<form class="form-horizontal">
<div class="form-group">
<label class="col-sm-3 control-label">{% trans 'Authroize URL' %}</label>
<div class="col-sm-9">
<input type="text"class="form-control" readonly value="{{ authorize_url }}">
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans 'Token URL' %}</label>
<div class="col-sm-9">
<input type="text" class="form-control" readonly value="{{ token_url }}">
</div>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">{% trans 'Userinfo Endpoint' %}</label>
<div class="col-sm-9">
<input type="text" class="form-control" readonly value="{{ userinfo_url }}">
</div>
</div>
</form>
<hr>
<form class="form-horizontal">
<div class="form-group">
<label class="col-sm-3 control-label">{% trans 'OpenID Configuration URL' %}</label>
<div class="col-sm-9">
<input type="text"class="form-control" readonly value="{{ openid_url }}">
</div>
</div>
</form>
</div>
<div class="modal-footer">
<button type="button" class="btn btn-primary" data-dismiss="modal">{% trans 'Close' %}</button>
</div>
</div>
</div>
</div>

View File

@ -3,7 +3,7 @@
from django.urls import path
from oauth2_provider import views
from passbook.oauth_provider.views import oauth2
from passbook.oauth_provider.views import oauth2, openid
urlpatterns = [
# Custom OAuth 2 Authorize View
@ -14,8 +14,12 @@ urlpatterns = [
path('authorize/permission_denied/', oauth2.OAuthPermissionDenied.as_view(),
name='oauth2-permission-denied'),
# OAuth API
path("authorize/", views.AuthorizationView.as_view(), name="authorize"),
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"),
# OpenID-Connect Discovery
path('.well-known/openid-configuration', openid.OpenIDConfigurationView.as_view(),
name='openid-discovery'),
path('.well-known/jwks.json', openid.JSONWebKeyView.as_view(),
name='openid-jwks'),
]

View File

@ -0,0 +1,30 @@
"""passbook oauth provider OpenID Views"""
from django.http import HttpRequest, JsonResponse
from django.shortcuts import reverse
from django.views.generic import View
class OpenIDConfigurationView(View):
"""Return OpenID Configuration"""
def get(self, request: HttpRequest):
"""Get Response conform to https://openid.net/specs/openid-connect-discovery-1_0.html"""
return JsonResponse({
'issuer': request.build_absolute_uri(reverse('passbook_core:overview')),
'authorization_endpoint': request.build_absolute_uri(
reverse('passbook_oauth_provider:oauth2-authorize')),
'token_endpoint': request.build_absolute_uri(reverse('passbook_oauth_provider:token')),
"jwks_uri": request.build_absolute_uri(reverse('passbook_oauth_provider:openid-jwks')),
"scopes_supported": [
"openid:userinfo",
],
})
class JSONWebKeyView(View):
"""JSON Web Key View"""
def get(self, request: HttpRequest):
"""JSON Webkeys are not implemented yet, hence return an empty object"""
return JsonResponse({})

View File

@ -1,2 +1,2 @@
"""passbook otp Header"""
__version__ = '0.1.20-beta'
__version__ = '0.1.24-beta'

View File

@ -1,2 +1,2 @@
"""passbook password_expiry"""
__version__ = '0.1.20-beta'
__version__ = '0.1.24-beta'

View File

@ -1,2 +1,2 @@
"""passbook saml_idp Header"""
__version__ = '0.1.20-beta'
__version__ = '0.1.24-beta'