Implement OAuth2 provider (#927)

- [x] Application management
  - [x] Link
  - [ ] Tests
- [x] Authorize flow
  - [x] Tests
- [x] Refresh token handling
  - [x] Tests
- [x] Revocation endpoint
  - [x] Tests
  - [x] Mitigate: https://github.com/jazzband/django-oauth-toolkit/issues/585
- [x] API authenticator / permission driver
  - [x] Test
- [x] Enforce organizer restriction
  - [x] Tests
- [x] Enforce scope restriction
  - [x] Tests
- [x] Show current applications to user
  - [x] Revoke
  - [x] Tests
- [x] Log new authorizations
  - [x] notify user
- [x] Ensure other grant types are not available
- [x] Documentation
- [x] check if revoking access toking, then refreshing gets rid of organizer constraint
- [x] Show logentry foo
This commit is contained in:
Raphael Michel
2018-06-05 12:58:04 +02:00
committed by GitHub
parent df031b2222
commit 69d10489b8
53 changed files with 1786 additions and 116 deletions

View File

@@ -0,0 +1,51 @@
{% extends "pretixcontrol/auth/base.html" %}
{% load bootstrap3 %}
{% load staticfiles %}
{% load i18n %}
{% block content %}
{% if not error %}
<form class="form-signin" action="" method="post">
<h3>{% trans "Authorize an application" %}</h3>
{% csrf_token %}
{% for field in form %}
{% if field.is_hidden %}
{{ field }}
{% endif %}
{% endfor %}
<p>
{% blocktrans trimmed with application=application.name %}
Do you really want to grant the application <strong>{{ application }}</strong> access to your
pretix account?
{% endblocktrans %}
</p>
<p>{% trans "The application requires the following permissions:" %}</p>
<ul>
{% for scope in scopes_descriptions %}
<li>{{ scope }}</li>
{% endfor %}
</ul>
<p>{% trans "Please select the organizer accounts this application should get access to:" %}</p>
{% bootstrap_field form.organizers layout="inline" %}
{% bootstrap_form_errors form layout="control" %}
<p class="text-danger">
{% blocktrans trimmed %}
This application has <strong>not</strong> been reviewed by the pretix team. Granting access to your
pretix account happens at your own risk.
{% endblocktrans %}
</p>
<div class="form-group buttons">
<input type="submit" class="btn btn-large btn-default" value="Cancel"/>
<input type="submit" class="btn btn-large btn-primary" name="allow" value="Authorize"/>
</div>
</form>
{% else %}
<form class="form-signin" action="" method="post">
<h3>{% trans "Error:" %} {{ error.error }}</h3>
<p>{{ error.description }}</p>
</form>
{% endif %}
{% endblock %}

View File

@@ -1,4 +1,4 @@
{% load i18n %}{% blocktrans with url=url|safe %}Hello,
{% load i18n %}{% blocktrans with url=url|safe messages=messages|safe %}Hello,
this is to inform you that the account information of your pretix account has been
changed. In particular, the following changes have been performed:

View File

@@ -124,6 +124,13 @@
<span class="fa fa-user fa-fw"></span>
{% endif %}
{{ log.user.get_full_name }}
{% if log.oauth_application %}
<br><span class="fa fa-plug fa-fw"></span>
{{ log.oauth_application.name }}
{% endif %}
{% elif log.api_token %}
<span class="fa fa-key fa-fw"></span>
{{ log.api_token.name }}
{% endif %}
</div>
<div class="col-lg-2 col-sm-12 col-xs-12">

View File

@@ -16,7 +16,8 @@
</option>
{% for up in userlist %}
{% if up.user__id %}
<option value="{{ up.user__id }}" {% if request.GET.user == up.user__id %}selected="selected"{% endif %}>
<option value="{{ up.user__id }}"
{% if request.GET.user == up.user__id %}selected="selected"{% endif %}>
{{ up.user__email }}
</option>
{% endif %}
@@ -42,13 +43,20 @@
{% if log.user %}
{% if log.user.is_staff %}
<span class="fa fa-id-card fa-danger fa-fw"
data-toggle="tooltip"
title="{% trans "This change was performed by a pretix administrator." %}">
data-toggle="tooltip"
title="{% trans "This change was performed by a pretix administrator." %}">
</span>
{% else %}
<span class="fa fa-user fa-fw"></span>
{% endif %}
{{ log.user.get_full_name }}
{% if log.oauth_application %}
<br><span class="fa fa-plug fa-fw"></span>
{{ log.oauth_application.name }}
{% endif %}
{% elif log.api_token %}
<span class="fa fa-key fa-fw"></span>
{{ log.api_token.name }}
{% endif %}
</div>
<div class="col-lg-2 col-sm-12 col-xs-12">
@@ -61,7 +69,7 @@
</div>
</div>
</li>
{% empty %}
{% empty %}
<div class="list-group-item">
<em>{% trans "No results" %}</em>
</div>

View File

@@ -15,6 +15,13 @@
<span class="fa fa-user fa-fw"></span>
{% endif %}
{{ log.user.get_full_name }}
{% if log.oauth_application %}
<span class="fa fa-plug fa-fw"></span>
{{ log.oauth_application.name }}
{% endif %}
{% elif log.api_token %}
<span class="fa fa-key fa-fw"></span>
{{ log.api_token.name }}
{% endif %}
{% if log.shredded %}
<span class="fa fa-eraser fa-danger fa-fw"

View File

@@ -0,0 +1,19 @@
{% extends "pretixcontrol/base.html" %}
{% load i18n %}
{% load bootstrap3 %}
{% block title %}{% trans "Disable application" %}{% endblock %}
{% block content %}
<h1>{% trans "Disable application" %}</h1>
<form action="" method="post" class="form-horizontal">
{% csrf_token %}
<p>{% blocktrans %}Are you sure you want to disable the application <strong>{{ application }}</strong> permanently?{% endblocktrans %}</p>
<div class="form-group submit-group">
<a href="{% url "control:user.settings.oauth.apps" %}" class="btn btn-default btn-cancel">
{% trans "Cancel" %}
</a>
<button type="submit" class="btn btn-danger btn-save">
{% trans "Disable" %}
</button>
</div>
</form>
{% endblock %}

View File

@@ -0,0 +1,50 @@
{% extends "pretixcontrol/base.html" %}
{% load i18n %}
{% load bootstrap3 %}
{% block title %}{% trans "Your applications" %}{% endblock %}
{% block content %}
<h1>{% trans "Your applications" %}</h1>
{% if applications %}
<div class="table-responsive">
<table class="table table-condensed table-hover">
<thead>
<tr>
<th>{% trans "Name" %}</th>
<th class="action-col-2"></th>
</tr>
</thead>
<tbody>
{% for application in applications %}
<tr>
<td><strong><a href="{% url "control:user.settings.oauth.app" pk=application.pk %}">{{ application.name }}</a></strong></td>
<td class="text-right">
<a href="{% url "control:user.settings.oauth.app" pk=application.pk %}" class="btn btn-default btn-sm"><i class="fa fa-edit"></i></a>
<a href="{% url "control:user.settings.oauth.app.roll" pk=application.pk %}" class="btn btn-default btn-sm"><i class="fa fa-repeat"></i></a>
<a href="{% url "control:user.settings.oauth.app.disable" pk=application.pk %}" class="btn btn-danger btn-sm"><i class="fa fa-trash"></i></a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<p>
<a class="btn btn-primary" href="{% url "control:user.settings.oauth.apps.register" %}">
<span class="fa fa-plus"></span>
{% trans "Create new application" %}
</a>
</p>
{% else %}
<div class="empty-collection">
<p>
{% blocktrans trimmed %}
No applications registered yet.
{% endblocktrans %}
</p>
<a href="{% url "control:user.settings.oauth.apps.register" %}"
class="btn btn-primary btn-lg">
{% trans "Register a new application" %}
</a>
</div>
{% endif %}
{% endblock %}

View File

@@ -0,0 +1,16 @@
{% extends "pretixcontrol/base.html" %}
{% load i18n %}
{% load bootstrap3 %}
{% block title %}{% trans "Register a new application" %}{% endblock %}
{% block content %}
<h1>{% trans "Register a new application" %}</h1>
<form class="form-horizontal" method="post" action="">
{% csrf_token %}
{% bootstrap_form form layout='control' %}
<div class="form-group submit-group">
<button type="submit" class="btn btn-primary btn-save">
{% trans "Save" %}
</button>
</div>
</form>
{% endblock %}

View File

@@ -0,0 +1,19 @@
{% extends "pretixcontrol/base.html" %}
{% load i18n %}
{% load bootstrap3 %}
{% block title %}{% trans "Generate new application secret" %}{% endblock %}
{% block content %}
<h1>{% trans "Generate new application secret" %}</h1>
<form action="" method="post" class="form-horizontal">
{% csrf_token %}
<p>{% blocktrans %}Are you sure you want to generate a new client secret for the application <strong>{{ application }}</strong>?{% endblocktrans %}</p>
<div class="form-group submit-group">
<a href="{% url "control:user.settings.oauth.apps" %}" class="btn btn-default btn-cancel">
{% trans "Cancel" %}
</a>
<button type="submit" class="btn btn-danger btn-save">
{% trans "Roll secret" %}
</button>
</div>
</form>
{% endblock %}

View File

@@ -0,0 +1,16 @@
{% extends "pretixcontrol/base.html" %}
{% load i18n %}
{% load bootstrap3 %}
{% block title %}{% trans "Update an application" %}{% endblock %}
{% block content %}
<h1>{% trans "Update an application" %}</h1>
<form class="form-horizontal" method="post" action="">
{% csrf_token %}
{% bootstrap_form form layout='control' %}
<div class="form-group submit-group">
<button type="submit" class="btn btn-primary btn-save">
{% trans "Save" %}
</button>
</div>
</form>
{% endblock %}

View File

@@ -0,0 +1,19 @@
{% extends "pretixcontrol/base.html" %}
{% load i18n %}
{% load bootstrap3 %}
{% block title %}{% trans "Revoke access" %}{% endblock %}
{% block content %}
<h1>{% trans "Revoke access" %}</h1>
<form action="" method="post" class="form-horizontal">
{% csrf_token %}
<p>{% blocktrans %}Are you sure you want to revoke access to your account for the application <strong>{{ application }}</strong>?{% endblocktrans %}</p>
<div class="form-group submit-group">
<a href="{% url "control:user.settings.oauth.list" %}" class="btn btn-default btn-cancel">
{% trans "Cancel" %}
</a>
<button type="submit" class="btn btn-danger btn-save">
{% trans "Revoke" %}
</button>
</div>
</form>
{% endblock %}

View File

@@ -0,0 +1,65 @@
{% extends "pretixcontrol/base.html" %}
{% load i18n %}
{% load bootstrap3 %}
{% block title %}{% trans "Authorized applications" %}{% endblock %}
{% block content %}
<h1>{% trans "Authorized applications" %}</h1>
<p>
<a href="{% url "control:user.settings.oauth.apps" %}" class="btn btn-default">
{% trans "Manage your own apps" %}
</a>
</p>
{% if tokens %}
<div class="table-responsive">
<table class="table table-condensed table-hover table-quotas">
<thead>
<tr>
<th>{% trans "Name" %}</th>
<th>{% trans "Permissions" %}</th>
<th>{% trans "Organizers" %}</th>
<th class="action-col-2"></th>
</tr>
</thead>
<tbody>
{% for token in tokens %}
<tr>
<td><strong>{{ token.application.name }}</strong></td>
<td>
<ul>
{% for scope in token.scopes_descriptions %}
<li>
{{ scope }}
</li>
{% endfor %}
</ul>
</td>
<td>
<ul>
{% for o in token.organizers.all %}
<li>
<a href="{% url "control:organizer" organizer=o.slug %}">
{{ o.name }}
</a>
</li>
{% endfor %}
</ul>
</td>
<td class="text-right">
<a href="{% url "control:user.settings.oauth.revoke" pk=token.pk %}"
class="btn btn-danger btn-sm">{% trans "Revoke access" %}</a>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
{% else %}
<div class="empty-collection">
<p>
{% blocktrans trimmed %}
No applications have access to your pretix account.
{% endblocktrans %}
</p>
</div>
{% endif %}
{% endblock %}

View File

@@ -54,7 +54,16 @@
</div>
</div>
<div class="form-group">
<label class="col-md-3 control-label" for="id_new_pw_repeat">{% trans "Account history" %}</label>
<label class="col-md-3 control-label" for="">{% trans "Authorized applications" %}</label>
<div class="col-md-9 static-form-row">
<a href="{% url "control:user.settings.oauth.list" %}">
<span class="fa fa-plug"></span>
{% trans "Show applications" %}
</a>
</div>
</div>
<div class="form-group">
<label class="col-md-3 control-label" for="">{% trans "Account history" %}</label>
<div class="col-md-9 static-form-row">
<a href="{% url "control:user.settings.history" %}">
<span class="fa fa-history"></span>