Compare commits

...

21 Commits

Author SHA1 Message Date
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
a53a269a8c bump version: 0.1.19-beta -> 0.1.20-beta 2019-03-13 16:51:43 +01:00
59565a5286 prepare 0.1.20 2019-03-13 16:51:38 +01:00
ae3c092238 add user settings for Sources 2019-03-13 16:49:30 +01:00
e98e5e4e3e fix GitHub Pretend again 2019-03-13 15:52:05 +01:00
d50c7ec8d4 bump version: 0.1.18-beta -> 0.1.19-beta 2019-03-13 15:16:48 +01:00
c0fdf377d1 prepare 0.1.18 2019-03-13 15:14:36 +01:00
70c11c8988 fix GitHub Pretend throwing a 500 error 2019-03-13 15:12:13 +01:00
67b19becc1 fix API Call for sentry-client, add missing template 2019-03-13 14:27:34 +01:00
83 changed files with 334 additions and 187 deletions

View File

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

View File

@ -54,7 +54,7 @@ package-docker:
before_script: before_script:
- echo "{\"auths\":{\"docker.$NEXUS_URL\":{\"auth\":\"$NEXUS_AUTH\"}}}" > /kaniko/.docker/config.json - echo "{\"auths\":{\"docker.$NEXUS_URL\":{\"auth\":\"$NEXUS_AUTH\"}}}" > /kaniko/.docker/config.json
script: 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.18-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.22-beta
stage: build stage: build
only: only:
- tags - tags

View File

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

View File

@ -8,13 +8,13 @@ from sentry.utils import json
from .constants import BASE_DOMAIN from .constants import BASE_DOMAIN
class SupervisrApiError(Exception): class PassbookApiError(Exception):
def __init__(self, message='', status=0): def __init__(self, message='', status=0):
super(SupervisrApiError, self).__init__(message) super(PassbookApiError, self).__init__(message)
self.status = status self.status = status
class SupervisrClient(object): class PassbookClient(object):
def __init__(self, client_id, client_secret): def __init__(self, client_id, client_secret):
self.client_id = client_id self.client_id = client_id
self.client_secret = client_secret self.client_secret = client_secret
@ -36,10 +36,10 @@ class SupervisrClient(object):
headers=headers, headers=headers,
) )
except RequestException as e: except RequestException as e:
raise SupervisrApiError(unicode(e), status=getattr(e, 'status_code', 0)) raise PassbookApiError(unicode(e), status=getattr(e, 'status_code', 0))
if req.status_code < 200 or req.status_code >= 300: if req.status_code < 200 or req.status_code >= 300:
raise SupervisrApiError(req.content, status=req.status_code) raise PassbookApiError(req.content, status=req.status_code)
return json.loads(req.content) return json.loads(req.content)
def get_user(self, access_token): def get_user(self, access_token):
return self._request('/api/core/v1/accounts/me/?format=openid', access_token) return self._request('/api/v1/openid/', access_token)

View File

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

42
debian/changelog vendored
View File

@ -1,3 +1,45 @@
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
passbook (0.1.18) stable; urgency=medium
* bump version: 0.1.16-beta -> 0.1.17-beta
* fix Server Error when downloading metadata
* add sentry client
* fix included yaml file
* adjust versions for client packages, auto build client-packages
* bump version: 0.1.17-beta -> 0.1.18-beta
* fix API Call for sentry-client, add missing template
* fix GitHub Pretend throwing a 500 error
-- Jens Langhammer <jens.langhammer@beryju.org> Wed, 13 Mar 2019 14:14:10 +0000
passbook (0.1.17) stable; urgency=medium passbook (0.1.17) stable; urgency=medium
* bump version: 0.1.15-beta -> 0.1.16-beta * bump version: 0.1.15-beta -> 0.1.16-beta

View File

@ -1,6 +1,6 @@
apiVersion: v1 apiVersion: v1
appVersion: "0.1.18-beta" appVersion: "0.1.22-beta"
description: A Helm chart for passbook. description: A Helm chart for passbook.
name: passbook name: passbook
version: "0.1.18-beta" version: "0.1.22-beta"
icon: https://passbook.beryju.org/images/logo.png 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.reddit
- passbook.oauth_client.source_types.supervisr - passbook.oauth_client.source_types.supervisr
- passbook.oauth_client.source_types.twitter - passbook.oauth_client.source_types.twitter
- passbook.oauth_client.source_types.azure_ad
saml_idp: saml_idp:
signing: true signing: true
autosubmit: false autosubmit: false

View File

@ -5,7 +5,7 @@
replicaCount: 1 replicaCount: 1
image: image:
tag: 0.1.18-beta tag: 0.1.22-beta
nameOverride: "" nameOverride: ""

View File

@ -1,2 +1,2 @@
"""passbook""" """passbook"""
__version__ = '0.1.18-beta' __version__ = '0.1.22-beta'

View File

@ -1,2 +1,2 @@
"""passbook admin""" """passbook admin"""
__version__ = '0.1.18-beta' __version__ = '0.1.22-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

@ -1,7 +1,7 @@
"""passbook URL Configuration""" """passbook URL Configuration"""
from django.urls import include, path 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, invitations, overview, policy,
property_mapping, providers, sources, users) property_mapping, providers, sources, users)
@ -77,5 +77,7 @@ urlpatterns = [
# Groups # Groups
path('groups/', groups.GroupListView.as_view(), name='groups'), path('groups/', groups.GroupListView.as_view(), name='groups'),
# API # 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""" """passbook api"""
__version__ = '0.1.18-beta' __version__ = '0.1.22-beta'

View File

@ -1,2 +1,2 @@
"""passbook audit Header""" """passbook audit Header"""
__version__ = '0.1.18-beta' __version__ = '0.1.22-beta'

View File

@ -1,2 +1,2 @@
"""passbook captcha_factor Header""" """passbook captcha_factor Header"""
__version__ = '0.1.18-beta' __version__ = '0.1.22-beta'

View File

@ -1,2 +1,2 @@
"""passbook core""" """passbook core"""
__version__ = '0.1.18-beta' __version__ = '0.1.22-beta'

View File

@ -186,6 +186,12 @@ class Source(PolicyModel):
"""Return additional Info, such as a callback URL. Show in the administration interface.""" """Return additional Info, such as a callback URL. Show in the administration interface."""
return None return None
def has_user_settings(self):
"""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."""
return False
def __str__(self): def __str__(self):
return self.name return self.name

View File

@ -35,6 +35,7 @@ SECRET_KEY = CONFIG.get('secret_key')
DEBUG = CONFIG.get('debug') DEBUG = CONFIG.get('debug')
INTERNAL_IPS = ['127.0.0.1'] INTERNAL_IPS = ['127.0.0.1']
ALLOWED_HOSTS = CONFIG.get('domains', []) ALLOWED_HOSTS = CONFIG.get('domains', [])
SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')
LOGIN_URL = 'passbook_core:auth-login' LOGIN_URL = 'passbook_core:auth-login'
# CSRF_FAILURE_VIEW = 'passbook.core.views.errors.CSRFErrorView.as_view' # 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 %} {% load utils %}
<!DOCTYPE html> <!DOCTYPE html>
<html lang="en"> <html lang="en" class="layout-pf layout-pf-fixed transitions">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">

View File

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

View File

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

View File

@ -26,7 +26,7 @@
<li class="dropdown"> <li class="dropdown">
<button class="btn btn-link dropdown-toggle nav-item-iconic" id="dropdownMenu1" data-toggle="dropdown" <button class="btn btn-link dropdown-toggle nav-item-iconic" id="dropdownMenu1" data-toggle="dropdown"
aria-haspopup="true" aria-expanded="true"> aria-haspopup="true" aria-expanded="true">
<span title="Help" class="fa pficon-help dropdown-title"></span> <span title="Help" class="fa pficon-help"></span>
</button> </button>
<ul class="dropdown-menu" aria-labelledby="dropdownMenu1"> <ul class="dropdown-menu" aria-labelledby="dropdownMenu1">
{% comment %} <li><a href="#0">Help</a></li> {% endcomment %} {% comment %} <li><a href="#0">Help</a></li> {% endcomment %}
@ -172,19 +172,27 @@
</span> </span>
</a> </a>
</li> </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> </ul>
</div> </div>
</li> </li>
{% endif %} {% endif %}
</ul> </ul>
</div> </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 %} {% block content %}
{% endblock %} {% endblock %}
</div> </div>
{% endblock %} {% endblock %}
{% block scripts %} {% block scripts %}
{{ block.super }}
<script> <script>
$(document).ready(function () { $(document).ready(function () {
// initialize tooltips // initialize tooltips

View File

@ -2,6 +2,7 @@
{% load i18n %} {% load i18n %}
{% load is_active %} {% load is_active %}
{% load static %}
{% load passbook_user_settings %} {% load passbook_user_settings %}
{% block content %} {% block content %}
@ -24,6 +25,15 @@
</a> </a>
</li> </li>
{% endfor %} {% endfor %}
<li class="nav-divider"></li>
{% user_sources as us %}
{% for name, icon, link in us %}
<li class="{% if link == request.get_full_path %} active {% endif %}">
<a href="{{ link }}">
<i class="{{ icon }}"></i> {{ name }}
</a>
</li>
{% endfor %}
</ul> </ul>
</div> </div>
</div> </div>

View File

@ -2,7 +2,7 @@
from django import template from django import template
from passbook.core.models import Factor from passbook.core.models import Factor, Source
from passbook.core.policies import PolicyEngine from passbook.core.policies import PolicyEngine
register = template.Library() register = template.Library()
@ -20,3 +20,17 @@ def user_factors(context):
if policy_engine.passing and _link: if policy_engine.passing and _link:
matching_factors.append(_link) matching_factors.append(_link)
return matching_factors return matching_factors
@register.simple_tag(takes_context=True)
def user_sources(context):
"""Return a list of all sources which are enabled for the user"""
user = context.get('request').user
_all_sources = Source.objects.filter(enabled=True).select_subclasses()
matching_sources = []
for factor in _all_sources:
_link = factor.has_user_settings()
policy_engine = PolicyEngine(factor.policies.all())
policy_engine.for_user(user).with_request(context.get('request')).build()
if policy_engine.passing and _link:
matching_sources.append(_link)
return matching_sources

View File

@ -1,2 +1,2 @@
"""passbook hibp_policy""" """passbook hibp_policy"""
__version__ = '0.1.18-beta' __version__ = '0.1.22-beta'

View File

@ -1,2 +1,2 @@
"""Passbook ldap app Header""" """Passbook ldap app Header"""
__version__ = '0.1.18-beta' __version__ = '0.1.22-beta'

View File

@ -1,2 +1,2 @@
"""passbook lib""" """passbook lib"""
__version__ = '0.1.18-beta' __version__ = '0.1.22-beta'

View File

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

View File

@ -1,2 +1,2 @@
"""passbook oauth_client Header""" """passbook oauth_client Header"""
__version__ = '0.1.18-beta' __version__ = '0.1.22-beta'

View File

@ -108,3 +108,17 @@ class GoogleOAuthSourceForm(OAuthSourceForm):
'access_token_url': 'https://accounts.google.com/o/oauth2/token', 'access_token_url': 'https://accounts.google.com/o/oauth2/token',
'profile_url': ' https://www.googleapis.com/oauth2/v1/userinfo', '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://login.microsoftonline.com/common/openid/userinfo',
}

View File

@ -1,7 +1,7 @@
"""OAuth Client models""" """OAuth Client models"""
from django.db import models from django.db import models
from django.urls import reverse_lazy from django.urls import reverse, reverse_lazy
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from passbook.core.models import Source, UserSourceConnection from passbook.core.models import Source, UserSourceConnection
@ -29,15 +29,28 @@ class OAuthSource(Source):
def get_login_button(self): def get_login_button(self):
url = reverse_lazy('passbook_oauth_client:oauth-client-login', url = reverse_lazy('passbook_oauth_client:oauth-client-login',
kwargs={'source_slug': self.slug}) kwargs={'source_slug': self.slug})
if self.provider_type == 'github': # if self.provider_type == 'github':
return url, 'github-logo', _('GitHub') # return url, 'github-logo', _('GitHub')
return url, 'generic', _('Generic') return url, self.provider_type, self.name
@property @property
def additional_info(self): def additional_info(self):
return "Callback URL: '%s'" % reverse_lazy('passbook_oauth_client:oauth-client-callback', return "Callback URL: '%s'" % reverse_lazy('passbook_oauth_client:oauth-client-callback',
kwargs={'source_slug': self.slug}) kwargs={'source_slug': self.slug})
def has_user_settings(self):
"""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_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_class, reverse((view_name), kwargs={
'source_slug': self.slug
})
class Meta: class Meta:
verbose_name = _('Generic OAuth Source') verbose_name = _('Generic OAuth Source')
@ -103,6 +116,19 @@ class GoogleOAuthSource(OAuthSource):
verbose_name = _('Google OAuth Source') verbose_name = _('Google OAuth Source')
verbose_name_plural = _('Google OAuth Sources') 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): class UserOAuthSourceConnection(UserSourceConnection):
"""Authorized remote OAuth provider.""" """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

@ -1,6 +0,0 @@
{% load passbook_oauth_client %}
{% any_provider as enabled %}
{% if enabled %}
<div class="btn-group btn-primary btn-block">
{% endif %}

View File

@ -1,6 +0,0 @@
{% load passbook_oauth_client %}
{% provider_exists 'facebook' as facebook_enabled %}
{% if facebook_enabled %}
<a href="{% url 'passbook_oauth_client:oauth-client-login' provider='facebook' %}" class="btn" style="background-color:#4267b2;color:white;margin-top:10px;width:100%;"><i class="fa fa-facebook-official" aria-hidden="true"></i></a>
{% endif %}

View File

@ -1,6 +0,0 @@
{% load passbook_oauth_client %}
{% provider_exists 'twitter' as twitter_enabled %}
{% if twitter_enabled %}
<a href="{% url 'passbook_oauth_client:oauth-client-login' provider='twitter' %}" class="btn" style="background-color:#55ACEE;color:white;margin-top:10px;width:100%;"><i class="fa fa-twitter" aria-hidden="true"></i></a>
{% endif %}

View File

@ -1,7 +0,0 @@
{% load passbook_oauth_client %}
{% load static %}
{% provider_exists 'google' as google_enabled %}
{% if google_enabled %}
<a href="{% url 'passbook_oauth_client:oauth-client-login' provider='google' %}" class="btn" style="background-color:white;color:black;margin-top:10px;width:100%;"><img src="{% static 'img/google.svg' %}" style="height:12px"></a>
{% endif %}

View File

@ -1,6 +0,0 @@
{% load passbook_oauth_client %}
{% provider_exists 'github' as github_enabled %}
{% if github_enabled %}
<a href="{% url 'passbook_oauth_client:oauth-client-login' provider='github' %}" class="btn" style="background-color:#444444;color:white;margin-top:10px;width:100%;"><i class="fa fa-github" aria-hidden="true"></i></a>
{% endif %}

View File

@ -1,7 +0,0 @@
{% load passbook_oauth_client %}
{% load static %}
{% provider_exists 'discord' as discord_enabled %}
{% if discord_enabled %}
<a href="{% url 'passbook_oauth_client:oauth-client-login' provider='discord' %}" class="btn" style="background-color:#2C2F33;color:white;margin-top:10px;width:100%;"><img src="{% static 'img/discord.svg' %}" style="height:12px"></a>
{% endif %}

View File

@ -1,7 +0,0 @@
{% load passbook_oauth_client %}
{% load static %}
{% provider_exists 'reddit' as reddit_enabled %}
{% if reddit_enabled %}
<a href="{% url 'passbook_oauth_client:oauth-client-login' provider='reddit' %}" class="btn" style="background-color:#ff4500;color:white;margin-top:10px;width:100%;"><img src="{% static 'img/reddit.svg' %}" style="height:20px;margin-top:-5px;"></a>
{% endif %}

View File

@ -1,6 +0,0 @@
{% load passbook_oauth_client %}
{% any_provider as enabled %}
{% if enabled %}
</div>
{% endif %}

View File

@ -1,54 +0,0 @@
{% extends "user/base.html" %}
{% load utils %}
{% load i18n %}
{% block title %}
{% title "Overview" %}
{% endblock %}
{% block content %}
<h1><clr-icon shape="connect" size="48"></clr-icon>{% trans "OAuth2" %}</h1>
<div class="row">
<div class="col-md-12">
<div class="card">
<div class="card-header">
{% trans "Connected Accounts" %}
</div>
<div class="card-footer">
{% if provider_state %}
<table class="table">
<thead>
<th>
<th>{% trans 'Provider' %}</th>
<th>{% trans 'Status' %}</th>
<th>{% trans 'Action' %}</th>
<th>{% trans 'ID' %}</th>
</th>
</thead>
<tbody>
{% for data in provider_state %}
<tr>
<td></td>
<td>{% trans data.provider.ui_name %}</td>
<td>{{ data.state|yesno:"Connected,Not Connected" }}</td>
<td>
{% if data.state == False %}
<a href="{% url 'passbook_oauth_client:oauth-client-login' provider=data.provider.name %}">Connect</a>
{% else %}
<a href="{% url 'passbook_oauth_client:oauth-client-disconnect' provider=data.provider.name %}">Disconnect</a>
{% endif %}
</td>
<td>{{ data.aas.first.identifier }}</td>
</tr>
{% endfor %}
</tbody>
</table>
{% else %}
<p>{% trans "No Providers configured!" %}</p>
{% endif %}
</div>
</div>
</div>
</div>
{% endblock %}

View File

@ -0,0 +1,18 @@
{% extends "user/base.html" %}
{% load i18n %}
{% block page %}
<h1>{{ source.name }}</h1>
{% if connections.exists %}
<p>{% trans 'Connected.' %}</p>
<a class="btn btn-danger" href="{% url 'passbook_oauth_client:oauth-client-disconnect' source_slug=source.slug %}">
{% trans 'Disconnect' %}
</a>
{% else %}
<p>Not connected.</p>
<a class="btn btn-primary" href="{% url 'passbook_oauth_client:oauth-client-login' source_slug=source.slug %}">
{% trans 'Connect' %}
</a>
{% endif %}
{% endblock %}

View File

@ -3,7 +3,7 @@
from django.urls import path from django.urls import path
from passbook.oauth_client.source_types.manager import RequestKind from passbook.oauth_client.source_types.manager import RequestKind
from passbook.oauth_client.views import core, dispatcher from passbook.oauth_client.views import core, dispatcher, user
urlpatterns = [ urlpatterns = [
path('login/<slug:source_slug>/', dispatcher.DispatcherView.as_view( path('login/<slug:source_slug>/', dispatcher.DispatcherView.as_view(
@ -12,4 +12,6 @@ urlpatterns = [
kind=RequestKind.callback), name='oauth-client-callback'), kind=RequestKind.callback), name='oauth-client-callback'),
path('disconnect/<slug:source_slug>/', core.DisconnectView.as_view(), path('disconnect/<slug:source_slug>/', core.DisconnectView.as_view(),
name='oauth-client-disconnect'), name='oauth-client-disconnect'),
path('user/<slug:source_slug>/', user.UserSettingsView.as_view(),
name='oauth-client-user'),
] ]

View File

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

View File

@ -0,0 +1,20 @@
"""passbook oauth_client user views"""
from django.contrib.auth.mixins import LoginRequiredMixin
from django.shortcuts import get_object_or_404
from django.views.generic import TemplateView
from passbook.oauth_client.models import OAuthSource, UserOAuthSourceConnection
class UserSettingsView(LoginRequiredMixin, TemplateView):
"""Show user current connection state"""
template_name = 'oauth_client/user.html'
def get_context_data(self, **kwargs):
source = get_object_or_404(OAuthSource, slug=self.kwargs.get('source_slug'))
connections = UserOAuthSourceConnection.objects.filter(user=self.request.user,
source=source)
kwargs['source'] = source
kwargs['connections'] = connections
return super().get_context_data(**kwargs)

View File

@ -1,2 +1,2 @@
"""passbook oauth_provider Header""" """passbook oauth_provider Header"""
__version__ = '0.1.18-beta' __version__ = '0.1.22-beta'

View File

@ -1,2 +1,2 @@
"""passbook otp Header""" """passbook otp Header"""
__version__ = '0.1.18-beta' __version__ = '0.1.22-beta'

View File

@ -1,2 +1,2 @@
"""passbook password_expiry""" """passbook password_expiry"""
__version__ = '0.1.18-beta' __version__ = '0.1.22-beta'

View File

@ -1,16 +1,24 @@
"""passbook pretend GitHub Views""" """passbook pretend GitHub Views"""
from django.http import JsonResponse from django.http import JsonResponse
from django.shortcuts import get_object_or_404
from django.views import View from django.views import View
from oauth2_provider.models import AccessToken
class GitHubUserView(View): class GitHubUserView(View):
"""Emulate GitHub's /user API Endpoint""" """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): def get(self, request):
"""Emulate GitHub's /user API Endpoint""" """Emulate GitHub's /user API Endpoint"""
user = self.verify_access_token()
return JsonResponse({ return JsonResponse({
"login": request.user.username, "login": user.username,
"id": request.user.pk, "id": user.pk,
"node_id": "", "node_id": "",
"avatar_url": "", "avatar_url": "",
"gravatar_id": "", "gravatar_id": "",
@ -27,19 +35,19 @@ class GitHubUserView(View):
"received_events_url": "", "received_events_url": "",
"type": "User", "type": "User",
"site_admin": False, "site_admin": False,
"name": request.user.name, "name": user.name,
"company": "", "company": "",
"blog": "", "blog": "",
"location": "", "location": "",
"email": request.user.email, "email": user.email,
"hireable": False, "hireable": False,
"bio": "", "bio": "",
"public_repos": 0, "public_repos": 0,
"public_gists": 0, "public_gists": 0,
"followers": 0, "followers": 0,
"following": 0, "following": 0,
"created_at": request.user.date_joined, "created_at": user.date_joined,
"updated_at": request.user.date_joined, "updated_at": user.date_joined,
"private_gists": 0, "private_gists": 0,
"total_private_repos": 0, "total_private_repos": 0,
"owned_private_repos": 0, "owned_private_repos": 0,

View File

@ -1,2 +1,2 @@
"""passbook saml_idp Header""" """passbook saml_idp Header"""
__version__ = '0.1.18-beta' __version__ = '0.1.22-beta'