Compare commits
14 Commits
version/0.
...
version/0.
| Author | SHA1 | Date | |
|---|---|---|---|
| 7e9154a0ea | |||
| e0ef061771 | |||
| b8694a7ade | |||
| 10d6a30f2c | |||
| 8c94aef6d0 | |||
| 19bd3bfffb | |||
| 8611ac624c | |||
| fa93b59a8c | |||
| 8b66b40f0d | |||
| c2756f15fc | |||
| 408e205c5f | |||
| 5f3ab49535 | |||
| 33431ae013 | |||
| b40ac6dc5d |
@ -1,5 +1,5 @@
|
||||
[bumpversion]
|
||||
current_version = 0.0.7-alpha
|
||||
current_version = 0.0.8-alpha
|
||||
tag = True
|
||||
commit = True
|
||||
parse = (?P<major>\d+)\.(?P<minor>\d+)\.(?P<patch>\d+)\-(?P<release>.*)
|
||||
|
||||
@ -54,7 +54,7 @@ package-docker:
|
||||
before_script:
|
||||
- echo "{\"auths\":{\"https://docker.$NEXUS_URL/\":{\"username\":\"$NEXUS_USER\",\"password\":\"$NEXUS_PASS\"}}}" > /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.0.7-alpha
|
||||
- /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.0.8-alpha
|
||||
stage: build
|
||||
only:
|
||||
- tags
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
"""passbook provider"""
|
||||
from allauth.socialaccount.providers.oauth2.urls import default_urlpatterns
|
||||
|
||||
from allauth_passbook.provider import PassbookProvider
|
||||
|
||||
urlpatterns = default_urlpatterns(PassbookProvider)
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
"""passbook adapter"""
|
||||
import requests
|
||||
|
||||
from allauth.socialaccount import app_settings
|
||||
from allauth.socialaccount.providers.oauth2.views import (OAuth2Adapter,
|
||||
OAuth2CallbackView,
|
||||
OAuth2LoginView)
|
||||
|
||||
from allauth_passbook.provider import PassbookProvider
|
||||
|
||||
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
apiVersion: v1
|
||||
appVersion: "0.0.7-alpha"
|
||||
appVersion: "0.0.8-alpha"
|
||||
description: A Helm chart for passbook.
|
||||
name: passbook
|
||||
version: 1.0.0
|
||||
|
||||
@ -1,2 +1,2 @@
|
||||
"""passbook"""
|
||||
__version__ = '0.0.7-alpha'
|
||||
__version__ = '0.0.8-alpha'
|
||||
|
||||
@ -1,2 +1,2 @@
|
||||
"""passbook admin"""
|
||||
__version__ = '0.0.7-alpha'
|
||||
__version__ = '0.0.8-alpha'
|
||||
|
||||
@ -9,33 +9,35 @@
|
||||
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
<h1>{% trans "Applications" %}</h1>
|
||||
<span>{% trans "External Applications which use passbook as Identity-Provider, utilizing protocols like OAuth2 and SAML." %}</span>
|
||||
<hr>
|
||||
<a href="{% url 'passbook_admin:application-create' %}" class="btn btn-primary">
|
||||
{% trans 'Create...' %}
|
||||
</a>
|
||||
<hr>
|
||||
<table class="table table-striped table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans 'Name' %}</th>
|
||||
<th>{% trans 'Provider' %}</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for application in object_list %}
|
||||
<tr>
|
||||
<td>{{ application.name }}</td>
|
||||
<td>{{ application.provider }}</td>
|
||||
<td>
|
||||
<a class="btn btn-default btn-sm" href="{% url 'passbook_admin:application-update' pk=application.uuid %}?back={{ request.get_full_path }}">{% trans 'Edit' %}</a>
|
||||
<a class="btn btn-default btn-sm" href="{% url 'passbook_admin:application-delete' pk=application.uuid %}?back={{ request.get_full_path }}">{% trans 'Delete' %}</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<h1><span class="pficon-applications"></span> {% trans "Applications" %}</h1>
|
||||
<span>{% trans "External Applications which use passbook as Identity-Provider, utilizing protocols like OAuth2 and SAML." %}</span>
|
||||
<hr>
|
||||
<a href="{% url 'passbook_admin:application-create' %}" class="btn btn-primary">
|
||||
{% trans 'Create...' %}
|
||||
</a>
|
||||
<hr>
|
||||
<table class="table table-striped table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans 'Name' %}</th>
|
||||
<th>{% trans 'Provider' %}</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for application in object_list %}
|
||||
<tr>
|
||||
<td>{{ application.name }}</td>
|
||||
<td>{{ application.provider }}</td>
|
||||
<td>
|
||||
<a class="btn btn-default btn-sm"
|
||||
href="{% url 'passbook_admin:application-update' pk=application.uuid %}?back={{ request.get_full_path }}">{% trans 'Edit' %}</a>
|
||||
<a class="btn btn-default btn-sm"
|
||||
href="{% url 'passbook_admin:application-delete' pk=application.uuid %}?back={{ request.get_full_path }}">{% trans 'Delete' %}</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
@ -8,80 +8,80 @@
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<h1>{% trans "Audit Log" %}</h1>
|
||||
<div id="pf-list-standard" class="list-group list-view-pf list-view-pf-view">
|
||||
<h1><span class="pficon-catalog"></span> {% trans "Audit Log" %}</h1>
|
||||
<div id="pf-list-standard" class="list-group list-view-pf list-view-pf-view">
|
||||
{% for entry in object_list %}
|
||||
<div class="list-group-item">
|
||||
<div class="list-view-pf-main-info">
|
||||
<div class="list-view-pf-left">
|
||||
<span class="fa fa-plane list-view-pf-icon-sm"></span>
|
||||
<div class="list-view-pf-main-info">
|
||||
<div class="list-view-pf-left">
|
||||
<span class="fa fa-plane list-view-pf-icon-sm"></span>
|
||||
</div>
|
||||
<div class="list-view-pf-body">
|
||||
<div class="list-view-pf-description">
|
||||
<div class="list-group-item-heading">
|
||||
{{ entry.action }}
|
||||
</div>
|
||||
<div class="list-group-item-text">
|
||||
{{ entry.context }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="list-view-pf-additional-info">
|
||||
<div class="list-view-pf-additional-info-item">
|
||||
<span class="pficon pficon-user"></span>
|
||||
<strong>{{ entry.user }}</strong>
|
||||
</div>
|
||||
<div class="list-view-pf-additional-info-item">
|
||||
<span class="pficon pficon-cluster"></span>
|
||||
<strong>{{ entry.app|default:'-' }}</strong>
|
||||
</div>
|
||||
<div class="list-view-pf-additional-info-item">
|
||||
<span class="fa fa-clock-o"></span>
|
||||
<strong>{{ entry.created }}</strong>
|
||||
</div>
|
||||
<div class="list-view-pf-additional-info-item">
|
||||
<span class="pficon pficon-screen"></span>
|
||||
<strong>{{ entry.request_ip }}</strong>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="list-view-pf-body">
|
||||
<div class="list-view-pf-description">
|
||||
<div class="list-group-item-heading">
|
||||
{{ entry.action }}
|
||||
</div>
|
||||
<div class="list-group-item-text">
|
||||
{{ entry.context }}
|
||||
</div>
|
||||
</div>
|
||||
<div class="list-view-pf-additional-info">
|
||||
<div class="list-view-pf-additional-info-item">
|
||||
<span class="pficon pficon-user"></span>
|
||||
<strong>{{ entry.user }}</strong>
|
||||
</div>
|
||||
<div class="list-view-pf-additional-info-item">
|
||||
<span class="pficon pficon-cluster"></span>
|
||||
<strong>{{ entry.app|default:'-' }}</strong>
|
||||
</div>
|
||||
<div class="list-view-pf-additional-info-item">
|
||||
<span class="fa fa-clock-o"></span>
|
||||
<strong>{{ entry.created }}</strong>
|
||||
</div>
|
||||
<div class="list-view-pf-additional-info-item">
|
||||
<span class="pficon pficon-screen"></span>
|
||||
<strong>{{ entry.request_ip }}</strong>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endfor %}
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
// Row Checkbox Selection
|
||||
$("#pf-list-standard input[type='checkbox']").change(function (e) {
|
||||
if ($(this).is(":checked")) {
|
||||
$(this).closest('.list-group-item').addClass("active");
|
||||
} else {
|
||||
$(this).closest('.list-group-item').removeClass("active");
|
||||
}
|
||||
});
|
||||
// toggle dropdown menu
|
||||
$('#pf-list-standard .list-view-pf-actions').on('show.bs.dropdown', function () {
|
||||
var $this = $(this);
|
||||
var $dropdown = $this.find('.dropdown');
|
||||
var space = $(window).height() - $dropdown[0].getBoundingClientRect().top - $this.find('.dropdown-menu').outerHeight(true);
|
||||
$dropdown.toggleClass('dropup', space < 10);
|
||||
});
|
||||
// allow users to select multiple list items with shift key
|
||||
$('#pf-list-standard .list-group').on('click', '.list-view-pf-checkbox>input', function (event) {
|
||||
var $list = $('.list-group');
|
||||
var prevIndex = $list.data('preIndex');
|
||||
var $listItems = $list.children('.list-group-item');
|
||||
var $currentItem = $(this).closest('.list-group-item');
|
||||
if (event.shiftKey && prevIndex > -1 && this.checked) {
|
||||
var currentIndex = $listItems.index($currentItem);
|
||||
var $selectScope = currentIndex - prevIndex > 0
|
||||
? $currentItem.prevAll().not($listItems.eq(prevIndex).prevAll().addBack())
|
||||
: $listItems.eq(prevIndex).prevAll().not($currentItem.prevAll().addBack());
|
||||
$selectScope.addClass('active').find('.list-view-pf-checkbox').children('input').prop('checked', true);
|
||||
}
|
||||
$list.data('preIndex', this.checked ? $listItems.index($currentItem) : -1);
|
||||
});
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
// Row Checkbox Selection
|
||||
$("#pf-list-standard input[type='checkbox']").change(function (e) {
|
||||
if ($(this).is(":checked")) {
|
||||
$(this).closest('.list-group-item').addClass("active");
|
||||
} else {
|
||||
$(this).closest('.list-group-item').removeClass("active");
|
||||
}
|
||||
});
|
||||
// toggle dropdown menu
|
||||
$('#pf-list-standard .list-view-pf-actions').on('show.bs.dropdown', function () {
|
||||
var $this = $(this);
|
||||
var $dropdown = $this.find('.dropdown');
|
||||
var space = $(window).height() - $dropdown[0].getBoundingClientRect().top - $this.find('.dropdown-menu').outerHeight(true);
|
||||
$dropdown.toggleClass('dropup', space < 10);
|
||||
});
|
||||
// allow users to select multiple list items with shift key
|
||||
$('#pf-list-standard .list-group').on('click', '.list-view-pf-checkbox>input', function (event) {
|
||||
var $list = $('.list-group');
|
||||
var prevIndex = $list.data('preIndex');
|
||||
var $listItems = $list.children('.list-group-item');
|
||||
var $currentItem = $(this).closest('.list-group-item');
|
||||
if (event.shiftKey && prevIndex > -1 && this.checked) {
|
||||
var currentIndex = $listItems.index($currentItem);
|
||||
var $selectScope = currentIndex - prevIndex > 0
|
||||
? $currentItem.prevAll().not($listItems.eq(prevIndex).prevAll().addBack())
|
||||
: $listItems.eq(prevIndex).prevAll().not($currentItem.prevAll().addBack());
|
||||
$selectScope.addClass('active').find('.list-view-pf-checkbox').children('input').prop('checked', true);
|
||||
}
|
||||
$list.data('preIndex', this.checked ? $listItems.index($currentItem) : -1);
|
||||
});
|
||||
|
||||
});
|
||||
</script>
|
||||
{% include 'partials/pagination.html' %}
|
||||
});
|
||||
</script>
|
||||
{% include 'partials/pagination.html' %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
@ -10,7 +10,7 @@
|
||||
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
<h1>{% trans "Factors" %}</h1>
|
||||
<h1><span class="pficon-plugged"></span> {% trans "Factors" %}</h1>
|
||||
<span>{% trans "Factors required for a user to successfully authenticate." %}</span>
|
||||
<hr>
|
||||
<div class="dropdown">
|
||||
@ -20,7 +20,8 @@
|
||||
</button>
|
||||
<ul class="dropdown-menu" role="menu" aria-labelledby="createDropdown">
|
||||
{% for type, name in types.items %}
|
||||
<li role="presentation"><a role="menuitem" tabindex="-1" href="{% url 'passbook_admin:factor-create' %}?type={{ type }}">{{ name }}</a></li>
|
||||
<li role="presentation"><a role="menuitem" tabindex="-1"
|
||||
href="{% url 'passbook_admin:factor-create' %}?type={{ type }}">{{ name }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
@ -9,32 +9,35 @@
|
||||
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
<h1>{% trans "Invitations" %}</h1>
|
||||
<span>{% trans "Create Invitation Links which optionally force a username or expire on a set date." %}</span>
|
||||
<hr>
|
||||
<a href="{% url 'passbook_admin:invitation-create' %}" class="btn btn-primary">
|
||||
{% trans 'Create...' %}
|
||||
</a>
|
||||
<hr>
|
||||
<table class="table table-striped table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans 'Expiry' %}</th>
|
||||
<th>{% trans 'Link' %}</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for invitation in object_list %}
|
||||
<tr>
|
||||
<td>{{ invitation.expires|default:"Never" }}</td>
|
||||
<td><pre>{{ invitation.link }}</pre></td>
|
||||
<td>
|
||||
<a class="btn btn-default btn-sm" href="{% url 'passbook_admin:invitation-delete' pk=invitation.uuid %}?back={{ request.get_full_path }}">{% trans 'Delete' %}</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<h1><span class="pficon-migration"></span> {% trans "Invitations" %}</h1>
|
||||
<span>{% trans "Create Invitation Links which optionally force a username or expire on a set date." %}</span>
|
||||
<hr>
|
||||
<a href="{% url 'passbook_admin:invitation-create' %}" class="btn btn-primary">
|
||||
{% trans 'Create...' %}
|
||||
</a>
|
||||
<hr>
|
||||
<table class="table table-striped table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans 'Expiry' %}</th>
|
||||
<th>{% trans 'Link' %}</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for invitation in object_list %}
|
||||
<tr>
|
||||
<td>{{ invitation.expires|default:"Never" }}</td>
|
||||
<td>
|
||||
<pre>{{ invitation.link }}</pre>
|
||||
</td>
|
||||
<td>
|
||||
<a class="btn btn-default btn-sm"
|
||||
href="{% url 'passbook_admin:invitation-delete' pk=invitation.uuid %}?back={{ request.get_full_path }}">{% trans 'Delete' %}</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
@ -7,11 +7,18 @@
|
||||
<div class="col-xs-6 col-sm-2 col-md-2">
|
||||
<div class="card-pf card-pf-accented card-pf-aggregate-status">
|
||||
<h2 class="card-pf-title">
|
||||
<a href="#"><span class="fa fa-shield"></span><span class="card-pf-aggregate-status-count"></span> {% trans 'Applications' %}</a>
|
||||
<a href="{% url 'passbook_admin:applications' %}">
|
||||
<span class="pficon-applications"></span>
|
||||
<span class="card-pf-aggregate-status-count"></span> {% trans 'Applications' %}
|
||||
</a>
|
||||
</h2>
|
||||
<div class="card-pf-body">
|
||||
<p class="card-pf-aggregate-status-notifications">
|
||||
<span class="card-pf-aggregate-status-notification"><a href="#"><span class="pficon pficon-ok"></span>{{ application_count }}</a></span>
|
||||
<span class="card-pf-aggregate-status-notification">
|
||||
<a href="{% url 'passbook_admin:applications' %}">
|
||||
<span class="pficon pficon-ok"></span>{{ application_count }}
|
||||
</a>
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@ -19,11 +26,18 @@
|
||||
<div class="col-xs-6 col-sm-2 col-md-2">
|
||||
<div class="card-pf card-pf-accented card-pf-aggregate-status">
|
||||
<h2 class="card-pf-title">
|
||||
<a href="#"><span class="fa fa-shield"></span><span class="card-pf-aggregate-status-count"></span> {% trans 'Sources' %}</a>
|
||||
<a href="{% url 'passbook_admin:sources' %}">
|
||||
<span class="pficon-resource-pool"></span>
|
||||
<span class="card-pf-aggregate-status-count"></span> {% trans 'Sources' %}
|
||||
</a>
|
||||
</h2>
|
||||
<div class="card-pf-body">
|
||||
<p class="card-pf-aggregate-status-notifications">
|
||||
<span class="card-pf-aggregate-status-notification"><a href="#"><span class="pficon pficon-ok"></span>{{ source_count }}</a></span>
|
||||
<span class="card-pf-aggregate-status-notification">
|
||||
<a href="{% url 'passbook_admin:sources' %}">
|
||||
<span class="pficon pficon-ok"></span>{{ source_count }}
|
||||
</a>
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@ -31,11 +45,18 @@
|
||||
<div class="col-xs-6 col-sm-2 col-md-2">
|
||||
<div class="card-pf card-pf-accented card-pf-aggregate-status">
|
||||
<h2 class="card-pf-title">
|
||||
<a href="#"><span class="fa fa-shield"></span><span class="card-pf-aggregate-status-count"></span> {% trans 'Providers' %}</a>
|
||||
<a href="{% url 'passbook_admin:providers' %}">
|
||||
<span class="pficon-integration"></span>
|
||||
<span class="card-pf-aggregate-status-count"></span> {% trans 'Providers' %}
|
||||
</a>
|
||||
</h2>
|
||||
<div class="card-pf-body">
|
||||
<p class="card-pf-aggregate-status-notifications">
|
||||
<span class="card-pf-aggregate-status-notification"><a href="#"><span class="pficon pficon-ok"></span>{{ provider_count }}</a></span>
|
||||
<span class="card-pf-aggregate-status-notification">
|
||||
<a href="{% url 'passbook_admin:providers' %}">
|
||||
<span class="pficon pficon-ok"></span>{{ provider_count }}
|
||||
</a>
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@ -43,11 +64,18 @@
|
||||
<div class="col-xs-6 col-sm-2 col-md-2">
|
||||
<div class="card-pf card-pf-accented card-pf-aggregate-status">
|
||||
<h2 class="card-pf-title">
|
||||
<a href="#"><span class="fa fa-shield"></span><span class="card-pf-aggregate-status-count"></span> {% trans 'Factors' %}</a>
|
||||
<a href="{% url 'passbook_admin:factors' %}">
|
||||
<span class="pficon-plugged"></span>
|
||||
<span class="card-pf-aggregate-status-count"></span> {% trans 'Factors' %}
|
||||
</a>
|
||||
</h2>
|
||||
<div class="card-pf-body">
|
||||
<p class="card-pf-aggregate-status-notifications">
|
||||
<span class="card-pf-aggregate-status-notification"><a href="#"><span class="pficon pficon-ok"></span>{{ factor_count }}</a></span>
|
||||
<span class="card-pf-aggregate-status-notification">
|
||||
<a href="{% url 'passbook_admin:factors' %}">
|
||||
<span class="pficon pficon-ok"></span>{{ factor_count }}
|
||||
</a>
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@ -55,11 +83,18 @@
|
||||
<div class="col-xs-6 col-sm-2 col-md-2">
|
||||
<div class="card-pf card-pf-accented card-pf-aggregate-status">
|
||||
<h2 class="card-pf-title">
|
||||
<a href="#"><span class="fa fa-shield"></span><span class="card-pf-aggregate-status-count"></span> {% trans 'Invitation' %}</a>
|
||||
<a href="{% url 'passbook_admin:policies' %}">
|
||||
<span class="pficon-infrastructure"></span>
|
||||
<span class="card-pf-aggregate-status-count"></span> {% trans 'Policies' %}
|
||||
</a>
|
||||
</h2>
|
||||
<div class="card-pf-body">
|
||||
<p class="card-pf-aggregate-status-notifications">
|
||||
<span class="card-pf-aggregate-status-notification"><a href="#"><span class="pficon pficon-ok"></span>{{ invitation_count }}</a></span>
|
||||
<span class="card-pf-aggregate-status-notification">
|
||||
<a href="{% url 'passbook_admin:policies' %}">
|
||||
<span class="pficon pficon-ok"></span>{{ policy_count }}
|
||||
</a>
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@ -67,11 +102,18 @@
|
||||
<div class="col-xs-6 col-sm-2 col-md-2">
|
||||
<div class="card-pf card-pf-accented card-pf-aggregate-status">
|
||||
<h2 class="card-pf-title">
|
||||
<a href="#"><span class="fa fa-shield"></span><span class="card-pf-aggregate-status-count"></span> {% trans 'Policies' %}</a>
|
||||
<a href="{% url 'passbook_admin:invitations' %}">
|
||||
<span class="pficon-migration"></span>
|
||||
<span class="card-pf-aggregate-status-count"></span> {% trans 'Invitation' %}
|
||||
</a>
|
||||
</h2>
|
||||
<div class="card-pf-body">
|
||||
<p class="card-pf-aggregate-status-notifications">
|
||||
<span class="card-pf-aggregate-status-notification"><a href="#"><span class="pficon pficon-ok"></span>{{ policy_count }}</a></span>
|
||||
<span class="card-pf-aggregate-status-notification">
|
||||
<a href="{% url 'passbook_admin:invitations' %}">
|
||||
<span class="pficon pficon-ok"></span>{{ invitation_count }}
|
||||
</a>
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
@ -79,11 +121,18 @@
|
||||
<div class="col-xs-6 col-sm-2 col-md-2">
|
||||
<div class="card-pf card-pf-accented card-pf-aggregate-status">
|
||||
<h2 class="card-pf-title">
|
||||
<a href="#"><span class="fa fa-shield"></span><span class="card-pf-aggregate-status-count"></span> {% trans 'Users' %}</a>
|
||||
<a href="{% url 'passbook_admin:users' %}">
|
||||
<span class="pficon-users"></span>
|
||||
<span class="card-pf-aggregate-status-count"></span> {% trans 'Users' %}
|
||||
</a>
|
||||
</h2>
|
||||
<div class="card-pf-body">
|
||||
<p class="card-pf-aggregate-status-notifications">
|
||||
<span class="card-pf-aggregate-status-notification"><a href="#"><span class="pficon pficon-ok"></span>{{ user_count }}</a></span>
|
||||
<span class="card-pf-aggregate-status-notification">
|
||||
<a href="{% url 'passbook_admin:users' %}">
|
||||
<span class="pficon pficon-ok"></span>{{ user_count }}
|
||||
</a>
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -9,42 +9,46 @@
|
||||
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
<h1>{% trans "Policies" %}</h1>
|
||||
<span>{% trans "Allow users to use Applications based on properties, enforce Password Criteria and selectively apply Factors." %}</span>
|
||||
<hr>
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-primary dropdown-toggle" type="button" id="createDropdown" data-toggle="dropdown">
|
||||
{% trans 'Create...' %}
|
||||
<span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu" role="menu" aria-labelledby="createDropdown">
|
||||
{% for type, name in types.items %}
|
||||
<li role="presentation"><a role="menuitem" tabindex="-1" href="{% url 'passbook_admin:policy-create' %}?type={{ type }}">{{ name }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
<hr>
|
||||
<table class="table table-striped table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans 'Name' %}</th>
|
||||
<th>{% trans 'Class' %}</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for policy in object_list %}
|
||||
<tr>
|
||||
<td>{{ policy.name }}</td>
|
||||
<td>{{ policy|fieldtype }}</td>
|
||||
<td>
|
||||
<a class="btn btn-default btn-sm" href="{% url 'passbook_admin:policy-update' pk=policy.uuid %}?back={{ request.get_full_path }}">{% trans 'Edit' %}</a>
|
||||
<a class="btn btn-default btn-sm" href="{% url 'passbook_admin:policy-test' pk=policy.uuid %}?back={{ request.get_full_path }}">{% trans 'Test' %}</a>
|
||||
<a class="btn btn-default btn-sm" href="{% url 'passbook_admin:policy-delete' pk=policy.uuid %}?back={{ request.get_full_path }}">{% trans 'Delete' %}</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<h1><span class="pficon-infrastructure"></span> {% trans "Policies" %}</h1>
|
||||
<span>{% trans "Allow users to use Applications based on properties, enforce Password Criteria and selectively apply Factors." %}</span>
|
||||
<hr>
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-primary dropdown-toggle" type="button" id="createDropdown" data-toggle="dropdown">
|
||||
{% trans 'Create...' %}
|
||||
<span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu" role="menu" aria-labelledby="createDropdown">
|
||||
{% for type, name in types.items %}
|
||||
<li role="presentation"><a role="menuitem" tabindex="-1"
|
||||
href="{% url 'passbook_admin:policy-create' %}?type={{ type }}">{{ name }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
<hr>
|
||||
<table class="table table-striped table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans 'Name' %}</th>
|
||||
<th>{% trans 'Class' %}</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for policy in object_list %}
|
||||
<tr>
|
||||
<td>{{ policy.name }}</td>
|
||||
<td>{{ policy|fieldtype }}</td>
|
||||
<td>
|
||||
<a class="btn btn-default btn-sm"
|
||||
href="{% url 'passbook_admin:policy-update' pk=policy.uuid %}?back={{ request.get_full_path }}">{% trans 'Edit' %}</a>
|
||||
<a class="btn btn-default btn-sm"
|
||||
href="{% url 'passbook_admin:policy-test' pk=policy.uuid %}?back={{ request.get_full_path }}">{% trans 'Test' %}</a>
|
||||
<a class="btn btn-default btn-sm"
|
||||
href="{% url 'passbook_admin:policy-delete' pk=policy.uuid %}?back={{ request.get_full_path }}">{% trans 'Delete' %}</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
@ -10,45 +10,49 @@
|
||||
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
<h1>{% trans "Providers" %}</h1>
|
||||
<span>{% trans "Authentication Protocol Provider, used as Protocol behind an Application." %}</span>
|
||||
<hr>
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-primary dropdown-toggle" type="button" id="createDropdown" data-toggle="dropdown">
|
||||
{% trans 'Create...' %}
|
||||
<span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu" role="menu" aria-labelledby="createDropdown">
|
||||
{% for type, name in types.items %}
|
||||
<li role="presentation"><a role="menuitem" tabindex="-1" href="{% url 'passbook_admin:provider-create' %}?type={{ type }}">{{ name }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
<hr>
|
||||
<table class="table table-striped table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans 'Name' %}</th>
|
||||
<th>{% trans 'Class' %}</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for provider in object_list %}
|
||||
<tr>
|
||||
<td>{{ provider.name }}</td>
|
||||
<td>{{ provider|fieldtype }}</td>
|
||||
<td>
|
||||
<a class="btn btn-default btn-sm" href="{% url 'passbook_admin:provider-update' pk=provider.pk %}?back={{ request.get_full_path }}">{% trans 'Edit' %}</a>
|
||||
<a class="btn btn-default btn-sm" href="{% url 'passbook_admin:provider-delete' pk=provider.pk %}?back={{ request.get_full_path }}">{% trans 'Delete' %}</a>
|
||||
{% get_links provider as links %}
|
||||
{% for name, href in links.items %}
|
||||
<a class="btn btn-default btn-sm" href="{{ href }}?back={{ request.get_full_path }}">{% trans name %}</a>
|
||||
<h1><span class="pficon-integration"></span> {% trans "Providers" %}</h1>
|
||||
<span>{% trans "Authentication Protocol Provider, used as Protocol behind an Application." %}</span>
|
||||
<hr>
|
||||
<div class="dropdown">
|
||||
<button class="btn btn-primary dropdown-toggle" type="button" id="createDropdown" data-toggle="dropdown">
|
||||
{% trans 'Create...' %}
|
||||
<span class="caret"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu" role="menu" aria-labelledby="createDropdown">
|
||||
{% for type, name in types.items %}
|
||||
<li role="presentation"><a role="menuitem" tabindex="-1"
|
||||
href="{% url 'passbook_admin:provider-create' %}?type={{ type }}">{{ name }}</a></li>
|
||||
{% endfor %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</ul>
|
||||
</div>
|
||||
<hr>
|
||||
<table class="table table-striped table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans 'Name' %}</th>
|
||||
<th>{% trans 'Class' %}</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for provider in object_list %}
|
||||
<tr>
|
||||
<td>{{ provider.name }}</td>
|
||||
<td>{{ provider|fieldtype }}</td>
|
||||
<td>
|
||||
<a class="btn btn-default btn-sm"
|
||||
href="{% url 'passbook_admin:provider-update' pk=provider.pk %}?back={{ request.get_full_path }}">{% trans 'Edit' %}</a>
|
||||
<a class="btn btn-default btn-sm"
|
||||
href="{% url 'passbook_admin:provider-delete' pk=provider.pk %}?back={{ request.get_full_path }}">{% trans 'Delete' %}</a>
|
||||
{% get_links provider as links %}
|
||||
{% for name, href in links.items %}
|
||||
<a class="btn btn-default btn-sm"
|
||||
href="{{ href }}?back={{ request.get_full_path }}">{% trans name %}</a>
|
||||
{% endfor %}
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
@ -6,7 +6,7 @@
|
||||
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
<h1>{% trans "Sources" %}</h1>
|
||||
<h1><span class="pficon-resource-pool"></span> {% trans "Sources" %}</h1>
|
||||
<span>{% trans "External Sources which can be used to get Identities into passbook, for example Social Providers like Twiter and GitHub or Enterprise Providers like ADFS and LDAP." %}</span>
|
||||
<hr>
|
||||
<div class="dropdown">
|
||||
@ -27,6 +27,7 @@
|
||||
<tr>
|
||||
<th>{% trans 'Name' %}</th>
|
||||
<th>{% trans 'Class' %}</th>
|
||||
<th>{% trans 'Additional Info' %}</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
@ -35,6 +36,7 @@
|
||||
<tr>
|
||||
<td>{{ source.name }}</td>
|
||||
<td>{{ source|fieldtype }}</td>
|
||||
<td>{{ source.additional_info }}</td>
|
||||
<td>
|
||||
<a class="btn btn-default btn-sm"
|
||||
href="{% url 'passbook_admin:source-update' pk=source.uuid %}?back={{ request.get_full_path }}">{% trans 'Edit' %}</a>
|
||||
|
||||
@ -5,34 +5,36 @@
|
||||
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
<h1>{% trans "Users" %}</h1>
|
||||
<hr>
|
||||
<table class="table table-striped table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans 'Username' %}</th>
|
||||
<th>{% trans 'First Name' %}</th>
|
||||
<th>{% trans 'Last Name' %}</th>
|
||||
<th>{% trans 'Active' %}</th>
|
||||
<th>{% trans 'Last Login' %}</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for user in object_list %}
|
||||
<tr>
|
||||
<td>{{ user.username }}</td>
|
||||
<td>{{ user.first_name|default:'-' }}</td>
|
||||
<td>{{ user.last_name|default:'-' }}</td>
|
||||
<td>{{ user.is_active }}</td>
|
||||
<td>{{ user.last_login }}</td>
|
||||
<td>
|
||||
<a class="btn btn-default btn-sm" href="{% url 'passbook_admin:user-update' pk=user.pk %}?back={{ request.get_full_path }}">{% trans 'Edit' %}</a>
|
||||
<a class="btn btn-default btn-sm" href="{% url 'passbook_admin:user-delete' pk=user.pk %}?back={{ request.get_full_path }}">{% trans 'Delete' %}</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
<h1><span class="pficon-users"></span> {% trans "Users" %}</h1>
|
||||
<hr>
|
||||
<table class="table table-striped table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans 'Username' %}</th>
|
||||
<th>{% trans 'First Name' %}</th>
|
||||
<th>{% trans 'Last Name' %}</th>
|
||||
<th>{% trans 'Active' %}</th>
|
||||
<th>{% trans 'Last Login' %}</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for user in object_list %}
|
||||
<tr>
|
||||
<td>{{ user.username }}</td>
|
||||
<td>{{ user.first_name|default:'-' }}</td>
|
||||
<td>{{ user.last_name|default:'-' }}</td>
|
||||
<td>{{ user.is_active }}</td>
|
||||
<td>{{ user.last_login }}</td>
|
||||
<td>
|
||||
<a class="btn btn-default btn-sm"
|
||||
href="{% url 'passbook_admin:user-update' pk=user.pk %}?back={{ request.get_full_path }}">{% trans 'Edit' %}</a>
|
||||
<a class="btn btn-default btn-sm"
|
||||
href="{% url 'passbook_admin:user-delete' pk=user.pk %}?back={{ request.get_full_path }}">{% trans 'Delete' %}</a>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
@ -4,4 +4,8 @@
|
||||
|
||||
{% block above_form %}
|
||||
<h1>{% trans 'Create' %}</h1>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
||||
{% block action %}
|
||||
{% trans 'Create' %}
|
||||
{% endblock %}
|
||||
|
||||
@ -11,7 +11,7 @@
|
||||
<form action="" method="post" class="form-horizontal">
|
||||
{% include 'partials/form.html' with form=form %}
|
||||
<a class="btn btn-default" href="{% back %}">{% trans "Cancel" %}</a>
|
||||
<input type="submit" class="btn btn-primary" value="{% trans 'Create' %}" />
|
||||
<input type="submit" class="btn btn-primary" value="{% block action %}{% endblock %}" />
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -4,4 +4,8 @@
|
||||
|
||||
{% block above_form %}
|
||||
<h1>{% trans 'Update' %}</h1>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
||||
{% block action %}
|
||||
{% trans 'Update' %}
|
||||
{% endblock %}
|
||||
|
||||
@ -39,19 +39,18 @@ class FactorCreateView(SuccessMessageMixin, AdminRequiredMixin, CreateView):
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
kwargs = super().get_context_data(**kwargs)
|
||||
source_type = self.request.GET.get('type')
|
||||
model = next(x for x in all_subclasses(Factor) if x.__name__ == source_type)
|
||||
factor_type = self.request.GET.get('type')
|
||||
model = next(x for x in all_subclasses(Factor) if x.__name__ == factor_type)
|
||||
kwargs['type'] = model._meta.verbose_name
|
||||
return kwargs
|
||||
|
||||
def get_form_class(self):
|
||||
source_type = self.request.GET.get('type')
|
||||
model = next(x for x in all_subclasses(Factor) if x.__name__ == source_type)
|
||||
factor_type = self.request.GET.get('type')
|
||||
model = next(x for x in all_subclasses(Factor) if x.__name__ == factor_type)
|
||||
if not model:
|
||||
raise Http404
|
||||
return path_to_class(model.form)
|
||||
|
||||
|
||||
class FactorUpdateView(SuccessMessageMixin, AdminRequiredMixin, UpdateView):
|
||||
"""Update factor"""
|
||||
|
||||
@ -61,11 +60,12 @@ class FactorUpdateView(SuccessMessageMixin, AdminRequiredMixin, UpdateView):
|
||||
success_message = _('Successfully updated Factor')
|
||||
|
||||
def get_form_class(self):
|
||||
source_type = self.request.GET.get('type')
|
||||
model = next(x for x in all_subclasses(Factor) if x.__name__ == source_type)
|
||||
if not model:
|
||||
raise Http404
|
||||
return path_to_class(model.form)
|
||||
form_class_path = self.get_object().form
|
||||
form_class = path_to_class(form_class_path)
|
||||
return form_class
|
||||
|
||||
def get_object(self, queryset=None):
|
||||
return Factor.objects.filter(pk=self.kwargs.get('pk')).select_subclasses().first()
|
||||
|
||||
class FactorDeleteView(SuccessMessageMixin, AdminRequiredMixin, DeleteView):
|
||||
"""Delete factor"""
|
||||
|
||||
@ -1,2 +1,2 @@
|
||||
"""passbook api"""
|
||||
__version__ = '0.0.7-alpha'
|
||||
__version__ = '0.0.8-alpha'
|
||||
|
||||
@ -1,2 +1,2 @@
|
||||
"""passbook audit Header"""
|
||||
__version__ = '0.0.7-alpha'
|
||||
__version__ = '0.0.8-alpha'
|
||||
|
||||
@ -84,6 +84,8 @@ class LoginAttempt(CreatedUpdatedModel):
|
||||
@staticmethod
|
||||
def attempt(target_uid, request):
|
||||
"""Helper function to create attempt or count up existing one"""
|
||||
if not target_uid:
|
||||
return
|
||||
client_ip, _ = get_client_ip(request)
|
||||
# Since we can only use 254 chars for target_uid, truncate target_uid.
|
||||
target_uid = target_uid[:254]
|
||||
|
||||
@ -1,2 +1,2 @@
|
||||
"""passbook captcha_factor Header"""
|
||||
__version__ = '0.0.7-alpha'
|
||||
__version__ = '0.0.8-alpha'
|
||||
|
||||
@ -1,2 +1,2 @@
|
||||
"""passbook core"""
|
||||
__version__ = '0.0.7-alpha'
|
||||
__version__ = '0.0.8-alpha'
|
||||
|
||||
@ -1,9 +1,11 @@
|
||||
"""passbook multi-factor authentication engine"""
|
||||
from logging import getLogger
|
||||
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth import authenticate
|
||||
from django.core.exceptions import PermissionDenied
|
||||
from django.forms.utils import ErrorList
|
||||
from django.shortcuts import redirect
|
||||
from django.utils.translation import gettext as _
|
||||
from django.views.generic import FormView
|
||||
|
||||
@ -21,6 +23,19 @@ class PasswordFactor(FormView, AuthenticationFactor):
|
||||
form_class = PasswordFactorForm
|
||||
template_name = 'login/factors/backend.html'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
kwargs['show_password_forget_notice'] = CONFIG.y('passbook.password_reset.enabled')
|
||||
return super().get_context_data(**kwargs)
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
if 'password-forgotten' in request.GET:
|
||||
# TODO: Save nonce key in database for password reset
|
||||
# TODO: Send email to user
|
||||
self.authenticator.cleanup()
|
||||
messages.success(request, _('Check your E-Mails for a password reset link.'))
|
||||
return redirect('passbook_core:auth-login')
|
||||
return super().get(request, *args, **kwargs)
|
||||
|
||||
def form_valid(self, form):
|
||||
"""Authenticate against django's authentication backend"""
|
||||
uid_fields = CONFIG.y('passbook.uid_fields')
|
||||
|
||||
@ -111,7 +111,7 @@ class AuthenticationView(UserPassesTestMixin, View):
|
||||
"""Show error message, user cannot login.
|
||||
This should only be shown if user authenticated successfully, but is disabled/locked/etc"""
|
||||
LOGGER.debug("User invalid")
|
||||
self._cleanup()
|
||||
self.cleanup()
|
||||
return redirect(reverse('passbook_core:auth-denied'))
|
||||
|
||||
def _user_passed(self):
|
||||
@ -121,13 +121,13 @@ class AuthenticationView(UserPassesTestMixin, View):
|
||||
login(self.request, self.pending_user, backend=backend)
|
||||
LOGGER.debug("Logged in user %s", self.pending_user)
|
||||
# Cleanup
|
||||
self._cleanup()
|
||||
self.cleanup()
|
||||
next_param = self.request.GET.get('next', None)
|
||||
if next_param and is_url_absolute(next_param):
|
||||
return redirect(next_param)
|
||||
return redirect(reverse('passbook_core:overview'))
|
||||
|
||||
def _cleanup(self):
|
||||
def cleanup(self):
|
||||
"""Remove temporary data from session"""
|
||||
session_keys = [self.SESSION_FACTOR, self.SESSION_PENDING_FACTORS,
|
||||
self.SESSION_PENDING_USER, self.SESSION_USER_BACKEND, ]
|
||||
|
||||
@ -11,7 +11,7 @@ class PasswordFactorForm(forms.ModelForm):
|
||||
class Meta:
|
||||
|
||||
model = PasswordFactor
|
||||
fields = GENERAL_FIELDS + ['backends']
|
||||
fields = GENERAL_FIELDS + ['backends', 'password_policies']
|
||||
widgets = {
|
||||
'name': forms.TextInput(),
|
||||
'order': forms.NumberInput(),
|
||||
|
||||
25
passbook/core/migrations/0011_auto_20190225_1438.py
Normal file
25
passbook/core/migrations/0011_auto_20190225_1438.py
Normal file
@ -0,0 +1,25 @@
|
||||
# Generated by Django 2.1.7 on 2019-02-25 14:38
|
||||
|
||||
import django.utils.timezone
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('passbook_core', '0010_auto_20190224_1016'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='passwordfactor',
|
||||
name='password_policies',
|
||||
field=models.ManyToManyField(blank=True, to='passbook_core.Policy'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='user',
|
||||
name='password_change_date',
|
||||
field=models.DateTimeField(auto_now_add=True, default=django.utils.timezone.now),
|
||||
preserve_default=False,
|
||||
),
|
||||
]
|
||||
@ -9,9 +9,11 @@ from django.contrib.auth.models import AbstractUser
|
||||
from django.contrib.postgres.fields import ArrayField
|
||||
from django.db import models
|
||||
from django.urls import reverse_lazy
|
||||
from django.utils.timezone import now
|
||||
from django.utils.translation import gettext as _
|
||||
from model_utils.managers import InheritanceManager
|
||||
|
||||
from passbook.core.signals import password_changed
|
||||
from passbook.lib.models import CreatedUpdatedModel, UUIDModel
|
||||
|
||||
LOGGER = getLogger(__name__)
|
||||
@ -38,6 +40,12 @@ class User(AbstractUser):
|
||||
sources = models.ManyToManyField('Source', through='UserSourceConnection')
|
||||
applications = models.ManyToManyField('Application')
|
||||
groups = models.ManyToManyField('Group')
|
||||
password_change_date = models.DateTimeField(auto_now_add=True)
|
||||
|
||||
def set_password(self, password):
|
||||
password_changed.send(sender=self, user=self, password=password)
|
||||
self.password_change_date = now()
|
||||
return super().set_password(password)
|
||||
|
||||
class Provider(models.Model):
|
||||
"""Application-independent Provider instance. For example SAML2 Remote, OAuth2 Application"""
|
||||
@ -87,6 +95,7 @@ class PasswordFactor(Factor):
|
||||
"""Password-based Django-backend Authentication Factor"""
|
||||
|
||||
backends = ArrayField(models.TextField())
|
||||
password_policies = models.ManyToManyField('Policy', blank=True)
|
||||
|
||||
type = 'passbook.core.auth.factors.password.PasswordFactor'
|
||||
form = 'passbook.core.forms.factors.PasswordFactorForm'
|
||||
@ -94,6 +103,13 @@ class PasswordFactor(Factor):
|
||||
def has_user_settings(self):
|
||||
return _('Change Password'), 'pficon-key', 'passbook_core:user-change-password'
|
||||
|
||||
def password_passes(self, user: User) -> bool:
|
||||
"""Return true if user's password passes, otherwise False or raise Exception"""
|
||||
for policy in self.policies.all():
|
||||
if not policy.passes(user):
|
||||
return False
|
||||
return True
|
||||
|
||||
def __str__(self):
|
||||
return "Password Factor %s" % self.slug
|
||||
|
||||
@ -155,10 +171,15 @@ class Source(PolicyModel):
|
||||
return False
|
||||
|
||||
@property
|
||||
def get_url(self):
|
||||
"""Return URL used for logging in"""
|
||||
def get_login_button(self):
|
||||
"""Return a tuple of URL, Icon name and Name"""
|
||||
raise NotImplementedError
|
||||
|
||||
@property
|
||||
def additional_info(self):
|
||||
"""Return additional Info, such as a callback URL. Show in the administration interface."""
|
||||
return None
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
@ -73,6 +73,7 @@ INSTALLED_APPS = [
|
||||
'passbook.saml_idp.apps.PassbookSAMLIDPConfig',
|
||||
'passbook.otp.apps.PassbookOTPConfig',
|
||||
'passbook.captcha_factor.apps.PassbookCaptchaFactorConfig',
|
||||
'passbook.hibp_policy.apps.PassbookHIBPConfig',
|
||||
]
|
||||
|
||||
# Message Tag fix for bootstrap CSS Classes
|
||||
|
||||
@ -9,3 +9,4 @@ from django.core.signals import Signal
|
||||
user_signed_up = Signal(providing_args=['request', 'user'])
|
||||
invitation_created = Signal(providing_args=['request', 'invitation'])
|
||||
invitation_used = Signal(providing_args=['request', 'invitation', 'user'])
|
||||
password_changed = Signal(providing_args=['user', 'password'])
|
||||
|
||||
@ -23,16 +23,16 @@
|
||||
{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<div class="toast-notifications-list-pf">
|
||||
{% include 'partials/messages.html' %}
|
||||
</div>
|
||||
<div class="login-pf-page">
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-md-6 col-md-offset-3">
|
||||
{% include 'partials/messages.html' %}
|
||||
</div>
|
||||
<div class="col-sm-6 col-sm-offset-3 col-md-6 col-md-offset-3 col-lg-4 col-lg-offset-4">
|
||||
<header class="login-pf-page-header">
|
||||
<img class="login-pf-brand" style="max-height: 10rem;" src="{% static 'img/logo.svg' %}"
|
||||
alt="PatternFly logo" />
|
||||
alt="passbook logo" />
|
||||
{% if config.login.subtext %}
|
||||
<p>{{ config.login.subtext }}</p>
|
||||
{% endif %}
|
||||
|
||||
@ -2,3 +2,8 @@
|
||||
|
||||
{% load i18n %}
|
||||
|
||||
{% block beneath_form %}
|
||||
{% if show_password_forget_notice %}
|
||||
<a href="{% url 'passbook_core:auth-process' %}?password-forgotten">{% trans 'Forgot password?' %}</a>
|
||||
{% endif %}
|
||||
{% endblock %}
|
||||
|
||||
@ -8,10 +8,11 @@
|
||||
<h1>{% trans title %}</h1>
|
||||
</header>
|
||||
<form method="POST">
|
||||
{% csrf_token %}
|
||||
{% block above_form %}
|
||||
{% endblock %}
|
||||
{% include 'partials/form_login.html' %}
|
||||
{% block beneath_form %}
|
||||
{% endblock %}
|
||||
<button type="submit" class="btn btn-primary btn-block btn-lg">{% trans primary_action %}</button>
|
||||
</form>
|
||||
{% if show_sign_up_notice %}
|
||||
|
||||
@ -1,131 +0,0 @@
|
||||
{% load static %}
|
||||
<!DOCTYPE html>
|
||||
<!--[if IE 9]><html lang="en-us" class="ie9 login-pf"><![endif]-->
|
||||
<!--[if gt IE 9]><!-->
|
||||
<html lang="en-us" class="login-pf">
|
||||
<!--<![endif]-->
|
||||
<head>
|
||||
<title>Login Social Account (two column) - Red Hat® Common User Experience</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=Edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<link rel="shortcut icon" href="{% static 'img/favicon.ico' %}">
|
||||
<!-- iPad retina icon -->
|
||||
<link rel="apple-touch-icon-precomposed" sizes="152x152" href="{% static 'img/apple-touch-icon-precomposed-152.png' %}">
|
||||
<!-- iPad retina icon (iOS < 7) -->
|
||||
<link rel="apple-touch-icon-precomposed" sizes="144x144" href="{% static 'img/apple-touch-icon-precomposed-144.png' %}">
|
||||
<!-- iPad non-retina icon -->
|
||||
<link rel="apple-touch-icon-precomposed" sizes="76x76" href="{% static 'img/apple-touch-icon-precomposed-76.png' %}">
|
||||
<!-- iPad non-retina icon (iOS < 7) -->
|
||||
<link rel="apple-touch-icon-precomposed" sizes="72x72" href="{% static 'img/apple-touch-icon-precomposed-72.png' %}">
|
||||
<!-- iPhone 6 Plus icon -->
|
||||
<link rel="apple-touch-icon-precomposed" sizes="120x120" href="{% static 'img/apple-touch-icon-precomposed-180.png' %}">
|
||||
<!-- iPhone retina icon (iOS < 7) -->
|
||||
<link rel="apple-touch-icon-precomposed" sizes="114x114" href="{% static 'img/apple-touch-icon-precomposed-114.png' %}">
|
||||
<!-- iPhone non-retina icon (iOS < 7) -->
|
||||
<link rel="apple-touch-icon-precomposed" sizes="57x57" href="{% static 'img/apple-touch-icon-precomposed-57.png' %}">
|
||||
<link rel="stylesheet" type="text/css" href="{% static 'css/patternfly.min.css' %}">
|
||||
<link rel="stylesheet" type="text/css" href="{% static 'css/patternfly-additions.min.css' %}">
|
||||
|
||||
<script src="{% static 'js/jquery.min.js' %}"></script>
|
||||
<script src="{% static 'js/bootstrap.min.js' %}"></script>
|
||||
<script src="//cdnjs.cloudflare.com/ajax/libs/bootstrap-select/1.12.2/js/bootstrap-select.min.js"></script>
|
||||
<script src="{% static 'js/patternfly.min.js' %}"></script>
|
||||
</head>
|
||||
<div class="toast-notifications-list-pf">
|
||||
<div class="toast-pf alert alert-warning alert-dismissable">
|
||||
<button type="button" class="close" data-dismiss="alert" aria-hidden="true">
|
||||
<span class="pficon pficon-close"></span>
|
||||
</button>
|
||||
<span class="pficon pficon-warning-triangle-o"></span>
|
||||
These examples are included for development testing purposes. For official documentation, see <a href="https://www.patternfly.org" class="alert-link">https://www.patternfly.org</a> and <a href="http://getbootstrap.com" class="alert-link">http://getbootstrap.com</a>.
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<body>
|
||||
<div class="login-pf-page login-pf-page-accounts">
|
||||
<header class="login-pf-page-header">
|
||||
<img class="login-pf-brand" src="/" alt="Red Hat® logo" />
|
||||
</header>
|
||||
<div class="card-pf login-pf-accounts">
|
||||
<header class="login-pf-header">
|
||||
<select class="selectpicker">
|
||||
<option>English</option>
|
||||
<option>French</option>
|
||||
<option>Italian</option>
|
||||
</select>
|
||||
<h1>Log In to Your Account</h1>
|
||||
</header>
|
||||
<section class="login-pf-social-section" role="contentinfo" aria-label="Log in to your patternfly account">
|
||||
<form>
|
||||
<div class="form-group">
|
||||
<label class="sr-only" for="exampleInputEmail1">Email address</label>
|
||||
<input type="email" class="form-control input-lg" id="exampleInputEmail1" placeholder="Email address">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label class="sr-only" for="exampleInputPassword1">Password
|
||||
</label>
|
||||
<input type="password" class="form-control input-lg" id="exampleInputPassword1" placeholder="Password">
|
||||
</div>
|
||||
<div class="login-pf-settings">
|
||||
<label class="checkbox-label">
|
||||
<input type="checkbox"> Keep me logged in for 30 days
|
||||
</label>
|
||||
<a href="#">Forgot password?</a>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary btn-block btn-lg">Log In</button>
|
||||
</form>
|
||||
</section><!--login-pf-section-->
|
||||
<section class="login-pf-social-section" role="contentinfo" aria-label="Log in with third party account">
|
||||
<ul class="login-pf-social login-pf-social-double-col list-unstyled">
|
||||
<li class="login-pf-social-link"><a href="#"><img src="{% static 'img/google-logo.svg' %}" alt="Google account login">Google</a></li>
|
||||
<li class="login-pf-social-link"><a href="#"><img src="{% static 'img/github-logo.svg' %}" alt="github account login">Github</a></li>
|
||||
<li class="login-pf-social-link"><a href="#"><img src="{% static 'img/facebook-logo.svg' %}" alt="Facebook account login">Facebook</a></li>
|
||||
<li class="login-pf-social-link"><a href="#"><img src="{% static 'img/twitter-logo.svg' %}" alt="Twitter account login">Twitter</a></li>
|
||||
<li class="login-pf-social-link"><a href="#"><img src="{% static 'img/linkedin-logo.svg' %}" alt="LinkIn account login">LinkIn</a></li>
|
||||
<li class="login-pf-social-link"><a href="#"><img src="{% static 'img/stack-exchange-logo.svg' %}" alt="Stack Exchange logo">Stack Exchange</a></li>
|
||||
<li class="login-pf-social-link"><a href="#"><img src="{% static 'img/open-id-logo.svg' %}" alt="Open ID account login">Open ID</a></li>
|
||||
<li class="login-pf-social-link"><a href="#"><img src="{% static 'img/instagram-logo.png' %}" alt="Instagram account login">Instagram</a></li>
|
||||
<li class="login-pf-social-link login-pf-social-link-more"><a href="#"><img src="{% static 'img/git-logo.svg' %}" alt="Git account login">Git</a></li>
|
||||
<li class="login-pf-social-link login-pf-social-link-more"><a href="#"><img src="{% static 'img/dropbox-logo.svg' %}" alt="dropbox account login">Dropbox</a></li>
|
||||
<li class="login-pf-social-link login-pf-social-link-more"><a href="#"><img src="{% static 'img/fedora-logo.png' %}" alt="fedora account login">Fedora</a></li>
|
||||
<li class="login-pf-social-link login-pf-social-link-more"><a href="#"><img src="{% static 'img/skype-logo.svg' %}" alt="skype account logingit ">Skype</a></li>
|
||||
</ul>
|
||||
<button type="button" id="socialAccountsToggle" class="btn btn-link login-pf-social-toggle">More<span class="caret"></span></button>
|
||||
</section><!--login-pf-section-->
|
||||
<p class="login-pf-signup">Need an account?<a href="#">Sign up</a></p>
|
||||
</div><!-- card -->
|
||||
<div class="row">
|
||||
<div class="col-md-6 col-md-offset-3">
|
||||
<footer class="login-pf-page-footer">
|
||||
<ul class="login-pf-page-footer-links list-unstyled">
|
||||
<li><a class="login-pf-page-footer-link" href="#">Terms of Use</a></li>
|
||||
<li><a class="login-pf-page-footer-link" href="#">Help</a></li>
|
||||
<li><a class="login-pf-page-footer-link" href="#">Privacy Policy</a></li>
|
||||
</ul>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div><!-- login-pf-page -->
|
||||
<script>
|
||||
$("#socialAccountsToggle").on("click", function(e) {
|
||||
var $toggle = $(e.target);
|
||||
var text = $toggle.contents().first()[0];
|
||||
var socialContainer = $('.login-pf-social-section > .login-pf-social');
|
||||
|
||||
if ($toggle.hasClass('login-pf-social-toggle-active')) {
|
||||
$toggle.removeClass('login-pf-social-toggle-active');
|
||||
text.textContent = 'More';
|
||||
socialContainer.removeClass('login-pf-social-all');
|
||||
} else {
|
||||
$toggle.addClass('login-pf-social-toggle-active');
|
||||
text.textContent = 'Less';
|
||||
socialContainer.addClass('login-pf-social-all');
|
||||
}
|
||||
});
|
||||
</script>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@ -1,51 +1,79 @@
|
||||
{% extends 'login/base.html' %}
|
||||
{% extends 'base/skeleton.html' %}
|
||||
|
||||
{% load static %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block row %}
|
||||
{% include 'partials/messages.html' %}
|
||||
<div class="col-md-6">
|
||||
<div class="card-pf">
|
||||
{% block head %}
|
||||
<style>
|
||||
.login-pf-page .login-pf-page-footer-links {
|
||||
padding: 15px;
|
||||
background-color: #fff;
|
||||
border-top: 2px solid transparent;
|
||||
box-shadow: 0 1px 1px rgba(3, 3, 3, .175);
|
||||
}
|
||||
|
||||
.login-pf-page .login-pf-page-footer-link {
|
||||
color: #72767b;
|
||||
}
|
||||
|
||||
.login-pf-page .login-pf-page-footer-links li:not(:last-of-type):after {
|
||||
color: #72767b;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block body %}
|
||||
<div class="toast-notifications-list-pf">
|
||||
{% include 'partials/messages.html' %}
|
||||
</div>
|
||||
<div class="login-pf-page login-pf-page-accounts">
|
||||
<header class="login-pf-page-header">
|
||||
<img class="login-pf-brand" style="max-height: 10rem;" src="{% static 'img/logo.svg' %}" alt="passbook logo" />
|
||||
{% if config.login.subtext %}
|
||||
<p>{{ config.login.subtext }}</p>
|
||||
{% endif %}
|
||||
</header>
|
||||
<div class="card-pf login-pf-accounts">
|
||||
<header class="login-pf-header">
|
||||
<h1>{% trans title %}</h1>
|
||||
</header>
|
||||
<form method="POST">
|
||||
{% csrf_token %}
|
||||
{% block above_form %}
|
||||
{% endblock %}
|
||||
{% include 'partials/form_login.html' %}
|
||||
<button type="submit" class="btn btn-primary btn-block btn-lg">{% trans primary_action %}</button>
|
||||
</form>
|
||||
<section class="login-pf-social-section" role="contentinfo" aria-label="Log in to your patternfly account">
|
||||
<form method="POST">
|
||||
{% block above_form %}
|
||||
{% endblock %}
|
||||
{% include 'partials/form_login.html' %}
|
||||
<button type="submit" class="btn btn-primary btn-block btn-lg">{% trans primary_action %}</button>
|
||||
</form>
|
||||
</section>
|
||||
<!--login-pf-section-->
|
||||
<section class="login-pf-social-section" role="contentinfo" aria-label="Log in with third party account">
|
||||
<ul class="login-pf-social login-pf-social-double-col list-unstyled">
|
||||
{% for url, icon, name in sources %}
|
||||
<li class="login-pf-social-link">
|
||||
<a href="{{ url }}">
|
||||
<img src="{% static 'img/' %}{{ icon }}.svg" alt="{{ name }}"> {{ name }}
|
||||
</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</section>
|
||||
{% if show_sign_up_notice %}
|
||||
<p class="login-pf-signup">
|
||||
{% trans 'Need an account?' %}
|
||||
<a href="{% url 'passbook_core:auth-sign-up' %}">{% trans 'Sign up' %}</a>
|
||||
</p>
|
||||
{% endif %}
|
||||
</div><!-- card -->
|
||||
<div class="row">
|
||||
<div class="col-md-6 col-md-offset-3">
|
||||
<footer class="login-pf-page-footer">
|
||||
<ul class="login-pf-page-footer-links list-unstyled">
|
||||
<li><a class="login-pf-page-footer-link" href="#">Terms of Use</a></li>
|
||||
<li><a class="login-pf-page-footer-link" href="#">Help</a></li>
|
||||
<li><a class="login-pf-page-footer-link" href="#">Privacy Policy</a></li>
|
||||
</ul>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="card-pf">
|
||||
<header class="login-pf-header">
|
||||
<h1>{% trans title %}</h1>
|
||||
<ul>
|
||||
{% for source in sources %}
|
||||
<li>
|
||||
<a class="btn btn-block btn-primary" href="{{ source.get_url }}">{{ source }}</a>
|
||||
</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</header>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-10 col-sm-offset-1 col-md-8 col-md-offset-2 col-lg-8 col-lg-offset-2">
|
||||
<footer class="login-pf-page-footer">
|
||||
<ul class="login-pf-page-footer-links list-unstyled">
|
||||
<li><a class="login-pf-page-footer-link" href="#">Terms of Use</a></li>
|
||||
<li><a class="login-pf-page-footer-link" href="#">Help</a></li>
|
||||
<li><a class="login-pf-page-footer-link" href="#">Privacy Policy</a></li>
|
||||
</ul>
|
||||
</footer>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
@ -5,83 +5,83 @@
|
||||
{% load is_active %}
|
||||
|
||||
{% block body %}
|
||||
<div class="toast-notifications-list-pf">
|
||||
{% include 'partials/messages.html' %}
|
||||
</div>
|
||||
<nav class="navbar navbar-default navbar-pf" role="navigation">
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse-1">
|
||||
<span class="sr-only">{% trans 'Toggle navigation' %}</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="/">
|
||||
<img src="{% static 'img/brand.svg' %}" alt="passbook" />
|
||||
</a>
|
||||
</div>
|
||||
<div class="collapse navbar-collapse navbar-collapse-1">
|
||||
<ul class="nav navbar-nav navbar-utility">
|
||||
<li class="dropdown">
|
||||
<button class="btn btn-link nav-item-iconic" id="horizontalDropdownMenu1" data-toggle="dropdown" aria-haspopup="true"
|
||||
aria-expanded="true">
|
||||
<span title="Help" class="fa pficon-help dropdown-title"></span>
|
||||
<div class="navbar-header">
|
||||
<button type="button" class="navbar-toggle" data-toggle="collapse" data-target=".navbar-collapse-1">
|
||||
<span class="sr-only">{% trans 'Toggle navigation' %}</span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu" aria-labelledby="horizontalDropdownMenu1">
|
||||
{% comment %} <li><a href="#0">Help</a></li> {% endcomment %}
|
||||
<li><a data-toggle="modal" data-target="#about-modal" href="#0">{% trans 'About' %}</a></li>
|
||||
<a class="navbar-brand" href="/">
|
||||
<img src="{% static 'img/brand.svg' %}" alt="passbook" />
|
||||
</a>
|
||||
</div>
|
||||
<div class="collapse navbar-collapse navbar-collapse-1">
|
||||
<ul class="nav navbar-nav navbar-utility">
|
||||
<li class="dropdown">
|
||||
<button class="btn btn-link nav-item-iconic" id="horizontalDropdownMenu1" data-toggle="dropdown"
|
||||
aria-haspopup="true" aria-expanded="true">
|
||||
<span title="Help" class="fa pficon-help dropdown-title"></span>
|
||||
</button>
|
||||
<ul class="dropdown-menu" aria-labelledby="horizontalDropdownMenu1">
|
||||
{% comment %} <li><a href="#0">Help</a></li> {% endcomment %}
|
||||
<li><a data-toggle="modal" data-target="#about-modal" href="#0">{% trans 'About' %}</a></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="dropdown">
|
||||
<button class="btn btn-link dropdown-toggle" data-toggle="dropdown">
|
||||
<span class="pficon pficon-user"></span>
|
||||
<span class="dropdown-title">
|
||||
{{ user.username }} <b class="caret"></b>
|
||||
</span>
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<li>
|
||||
<a href="{% url 'passbook_core:user-settings' %}">{% trans 'User Settings' %}</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{% url 'passbook_core:user-change-password' %}">{% trans 'Change Password' %}</a>
|
||||
</li>
|
||||
<li class="divider"></li>
|
||||
<li>
|
||||
<a href="{% url 'passbook_core:auth-logout' %}">{% trans 'Logout' %}</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="dropdown">
|
||||
<button class="btn btn-link dropdown-toggle" data-toggle="dropdown">
|
||||
<span class="pficon pficon-user"></span>
|
||||
<span class="dropdown-title">
|
||||
{{ user.username }} <b class="caret"></b>
|
||||
</span>
|
||||
</button>
|
||||
<ul class="dropdown-menu">
|
||||
<li>
|
||||
<a href="{% url 'passbook_core:user-settings' %}">{% trans 'User Settings' %}</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{% url 'passbook_core:user-change-password' %}">{% trans 'Change Password' %}</a>
|
||||
</li>
|
||||
<li class="divider"></li>
|
||||
<li>
|
||||
<a href="{% url 'passbook_core:auth-logout' %}">{% trans 'Logout' %}</a>
|
||||
</li>
|
||||
{% is_active_app 'passbook_admin' as is_admin %}
|
||||
<ul class="nav navbar-nav navbar-primary {% if is_admin == 'active' %}persistent-secondary{% endif %}">
|
||||
<li class="{% is_active_url 'passbook_core:overview' %}">
|
||||
<a href="{% url 'passbook_core:overview' %}">{% trans 'Overview' %}</a>
|
||||
</li>
|
||||
{% if user.is_superuser %}
|
||||
<li class="{% is_active_app 'passbook_admin' %}">
|
||||
<a href="{% url 'passbook_admin:overview' %}">{% trans 'Administration' %}</a>
|
||||
{% block nav_secondary %}
|
||||
{% endblock %}
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</li>
|
||||
</ul>
|
||||
{% is_active_app 'passbook_admin' as is_admin %}
|
||||
<ul class="nav navbar-nav navbar-primary {% if is_admin == 'active' %}persistent-secondary{% endif %}">
|
||||
<li class="{% is_active_url 'passbook_core:overview' %}">
|
||||
<a href="{% url 'passbook_core:overview' %}">{% trans 'Overview' %}</a>
|
||||
</li>
|
||||
{% if user.is_superuser %}
|
||||
<li class="{% is_active_app 'passbook_admin' %}">
|
||||
<a href="{% url 'passbook_admin:overview' %}">{% trans 'Administration' %}</a>
|
||||
{% block nav_secondary %}
|
||||
{% endblock %}
|
||||
</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<div class="container-fluid container-cards-pf">
|
||||
<div class="container">
|
||||
{% include 'partials/messages.html' %}
|
||||
</div>
|
||||
{% block content %}
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
{% endblock %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block scripts %}
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
// initialize tooltips
|
||||
$('[data-toggle="tooltip"]').tooltip();
|
||||
$(document).ready(function () {
|
||||
// initialize tooltips
|
||||
$('[data-toggle="tooltip"]').tooltip();
|
||||
|
||||
// Initialize the vertical navigation
|
||||
$().setupVerticalNavigation(true);
|
||||
});
|
||||
// Initialize the vertical navigation
|
||||
$().setupVerticalNavigation(true);
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
@ -25,9 +25,6 @@
|
||||
<label class="checkbox-label">
|
||||
{{ field }} {{ field.label }}
|
||||
</label>
|
||||
{% if show_password_forget_notice %}
|
||||
<a href="#">{% trans 'Forgot password?' %}</a>
|
||||
{% endif %}
|
||||
{% else %}
|
||||
<label class="col-sm-2 sr-only" {% if field.field.required %}class="required"{% endif %} for="{{ field.name }}-{{ forloop.counter0 }}">
|
||||
{{ field.label }}
|
||||
|
||||
@ -1,19 +1,19 @@
|
||||
{% if messages %}
|
||||
{% for msg in messages %}
|
||||
<div class="alert alert-{{ msg.level_tag }}">
|
||||
{% for msg in messages %}
|
||||
<div class="toast-pf alert alert-dismissable alert-{{ msg.level_tag }}">
|
||||
<button type="button" class="close" data-dismiss="alert" aria-label="Close">
|
||||
<span class="pficon pficon-close"></span>
|
||||
<span class="pficon pficon-close"></span>
|
||||
</button>
|
||||
{% if msg.level_tag == 'danger' %}
|
||||
<span class="pficon pficon-error-circle-o"></span>
|
||||
<span class="pficon pficon-error-circle-o"></span>
|
||||
{% elif msg.level_tag == 'warning' %}
|
||||
<span class="pficon pficon-warning-triangle-o"></span>
|
||||
<span class="pficon pficon-warning-triangle-o"></span>
|
||||
{% elif msg.level_tag == 'success' %}
|
||||
<span class="pficon pficon-ok"></span>
|
||||
<span class="pficon pficon-ok"></span>
|
||||
{% elif msg.level_tag == 'info' %}
|
||||
<span class="pficon pficon-info"></span>
|
||||
<span class="pficon pficon-info"></span>
|
||||
{% endif %}
|
||||
<strong>{{ msg.message|safe }}</strong>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{{ msg.message|safe }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
0
passbook/core/tests/__init__.py
Normal file
0
passbook/core/tests/__init__.py
Normal file
10
passbook/core/tests/test_login.py
Normal file
10
passbook/core/tests/test_login.py
Normal file
@ -0,0 +1,10 @@
|
||||
"""passbook core login test"""
|
||||
|
||||
from django.test import TestCase
|
||||
|
||||
|
||||
class LoginTest(TestCase):
|
||||
"""Test login"""
|
||||
|
||||
def test(self):
|
||||
"""Stub test"""
|
||||
@ -41,10 +41,12 @@ class LoginView(UserPassesTestMixin, FormView):
|
||||
kwargs['title'] = _('Log in to your account')
|
||||
kwargs['primary_action'] = _('Log in')
|
||||
kwargs['show_sign_up_notice'] = CONFIG.y('passbook.sign_up.enabled')
|
||||
kwargs['show_password_forget_notice'] = CONFIG.y('passbook.password_reset.enabled')
|
||||
kwargs['sources'] = Source.objects.filter(enabled=True).select_subclasses()
|
||||
if any(source.is_link for source in kwargs['sources']):
|
||||
self.template_name = 'login/test.html'
|
||||
kwargs['sources'] = []
|
||||
sources = Source.objects.filter(enabled=True).select_subclasses()
|
||||
if any(source.is_link for source in sources):
|
||||
for source in sources:
|
||||
kwargs['sources'].append(source.get_login_button)
|
||||
self.template_name = 'login/with_sources.html'
|
||||
return super().get_context_data(**kwargs)
|
||||
|
||||
def get_user(self, uid_value) -> User:
|
||||
|
||||
2
passbook/hibp_policy/__init__.py
Normal file
2
passbook/hibp_policy/__init__.py
Normal file
@ -0,0 +1,2 @@
|
||||
"""passbook hibp_policy"""
|
||||
__version__ = '0.0.7-alpha'
|
||||
5
passbook/hibp_policy/admin.py
Normal file
5
passbook/hibp_policy/admin.py
Normal file
@ -0,0 +1,5 @@
|
||||
"""Passbook HIBP Admin"""
|
||||
|
||||
from passbook.lib.admin import admin_autoregister
|
||||
|
||||
admin_autoregister('passbook_hibp_policy')
|
||||
11
passbook/hibp_policy/apps.py
Normal file
11
passbook/hibp_policy/apps.py
Normal file
@ -0,0 +1,11 @@
|
||||
"""Passbook hibp app config"""
|
||||
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class PassbookHIBPConfig(AppConfig):
|
||||
"""Passbook hibp app config"""
|
||||
|
||||
name = 'passbook.hibp_policy'
|
||||
label = 'passbook_hibp_policy'
|
||||
verbose_name = 'passbook HaveIBeenPwned Policy'
|
||||
19
passbook/hibp_policy/forms.py
Normal file
19
passbook/hibp_policy/forms.py
Normal file
@ -0,0 +1,19 @@
|
||||
"""passbook HaveIBeenPwned Policy forms"""
|
||||
|
||||
from django import forms
|
||||
|
||||
from passbook.core.forms.policies import GENERAL_FIELDS
|
||||
from passbook.hibp_policy.models import HaveIBeenPwendPolicy
|
||||
|
||||
|
||||
class HaveIBeenPwnedPolicyForm(forms.ModelForm):
|
||||
"""Edit HaveIBeenPwendPolicy instances"""
|
||||
|
||||
class Meta:
|
||||
|
||||
model = HaveIBeenPwendPolicy
|
||||
fields = GENERAL_FIELDS + ['allowed_count']
|
||||
widgets = {
|
||||
'name': forms.TextInput(),
|
||||
'order': forms.NumberInput(),
|
||||
}
|
||||
28
passbook/hibp_policy/migrations/0001_initial.py
Normal file
28
passbook/hibp_policy/migrations/0001_initial.py
Normal file
@ -0,0 +1,28 @@
|
||||
# Generated by Django 2.1.7 on 2019-02-25 15:50
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
('passbook_core', '0011_auto_20190225_1438'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='HaveIBeenPwendPolicy',
|
||||
fields=[
|
||||
('policy_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='passbook_core.Policy')),
|
||||
('allowed_count', models.IntegerField(default=0)),
|
||||
],
|
||||
options={
|
||||
'verbose_name': 'HaveIBeenPwned Policy',
|
||||
'verbose_name_plural': 'HaveIBeenPwned Policies',
|
||||
},
|
||||
bases=('passbook_core.policy',),
|
||||
),
|
||||
]
|
||||
0
passbook/hibp_policy/migrations/__init__.py
Normal file
0
passbook/hibp_policy/migrations/__init__.py
Normal file
43
passbook/hibp_policy/models.py
Normal file
43
passbook/hibp_policy/models.py
Normal file
@ -0,0 +1,43 @@
|
||||
"""passbook HIBP Models"""
|
||||
|
||||
from hashlib import sha1
|
||||
|
||||
from django.db import models
|
||||
from django.utils.translation import gettext as _
|
||||
from requests import get
|
||||
|
||||
from passbook.core.models import Policy, User
|
||||
|
||||
|
||||
class HaveIBeenPwendPolicy(Policy):
|
||||
"""Check if password is on HaveIBeenPwned's list by upload the first
|
||||
5 characters of the SHA1 Hash."""
|
||||
|
||||
allowed_count = models.IntegerField(default=0)
|
||||
|
||||
form = 'passbook.hibp_policy.forms.HaveIBeenPwnedPolicyForm'
|
||||
|
||||
def passes(self, user: User) -> bool:
|
||||
"""Check if password is in HIBP DB. Hashes given Password with SHA1, uses the first 5
|
||||
characters of Password in request and checks if full hash is in response. Returns 0
|
||||
if Password is not in result otherwise the count of how many times it was used."""
|
||||
# Only check if password is being set
|
||||
if not hasattr(user, '__password__'):
|
||||
return True
|
||||
password = getattr(user, '__password__')
|
||||
pw_hash = sha1(password.encode('utf-8')).hexdigest() # nosec
|
||||
url = 'https://api.pwnedpasswords.com/range/%s' % pw_hash[:5]
|
||||
result = get(url).text
|
||||
final_count = 0
|
||||
for line in result.split('\r\n'):
|
||||
full_hash, count = line.split(':')
|
||||
if pw_hash[5:] == full_hash.lower():
|
||||
final_count = int(count)
|
||||
if final_count > self.allowed_count:
|
||||
return False
|
||||
return True
|
||||
|
||||
class Meta:
|
||||
|
||||
verbose_name = _('have i been pwned Policy')
|
||||
verbose_name_plural = _('have i been pwned Policies')
|
||||
@ -1,2 +1,2 @@
|
||||
"""Passbook ldap app Header"""
|
||||
__version__ = '0.0.7-alpha'
|
||||
__version__ = '0.0.8-alpha'
|
||||
|
||||
@ -29,7 +29,7 @@ class LDAPSource(Source):
|
||||
form = 'passbook.ldap.forms.LDAPSourceForm'
|
||||
|
||||
@property
|
||||
def get_url(self):
|
||||
def get_login_button(self):
|
||||
raise NotImplementedError()
|
||||
|
||||
class Meta:
|
||||
|
||||
@ -1,2 +1,2 @@
|
||||
"""passbook lib"""
|
||||
__version__ = '0.0.7-alpha'
|
||||
__version__ = '0.0.8-alpha'
|
||||
|
||||
@ -1,2 +1,2 @@
|
||||
"""passbook oauth_client Header"""
|
||||
__version__ = '0.0.7-alpha'
|
||||
__version__ = '0.0.8-alpha'
|
||||
|
||||
@ -26,9 +26,17 @@ class OAuthSource(Source):
|
||||
return True
|
||||
|
||||
@property
|
||||
def get_url(self):
|
||||
return reverse_lazy('passbook_oauth_client:oauth-client-login',
|
||||
kwargs={'source_slug': self.slug})
|
||||
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')
|
||||
|
||||
@property
|
||||
def additional_info(self):
|
||||
return "Callback URL: '%s'" % reverse_lazy('passbook_oauth_client:oauth-client-callback',
|
||||
kwargs={'source_slug': self.slug})
|
||||
|
||||
class Meta:
|
||||
|
||||
|
||||
@ -1,2 +1,2 @@
|
||||
"""passbook oauth_provider Header"""
|
||||
__version__ = '0.0.7-alpha'
|
||||
__version__ = '0.0.8-alpha'
|
||||
|
||||
@ -1,2 +1,2 @@
|
||||
"""passbook otp Header"""
|
||||
__version__ = '0.0.7-alpha'
|
||||
__version__ = '0.0.8-alpha'
|
||||
|
||||
@ -1,2 +1,2 @@
|
||||
"""passbook saml_idp Header"""
|
||||
__version__ = '0.0.7-alpha'
|
||||
__version__ = '0.0.8-alpha'
|
||||
|
||||
Reference in New Issue
Block a user