Rules -> Policies, more things
This commit is contained in:
parent
d3d75737ed
commit
c941107d42
|
@ -1,6 +1,6 @@
|
|||
"""passbook core source form fields"""
|
||||
# from django import forms
|
||||
|
||||
SOURCE_FORM_FIELDS = ['name', 'slug', 'enabled']
|
||||
SOURCE_FORM_FIELDS = ['name', 'slug', 'enabled', 'policies']
|
||||
|
||||
# class SourceForm(forms.Form)
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
<h1>{% trans "Audit Log" %}</h1>
|
||||
<div id="pf-list-standard" class="list-group list-view-pf list-view-pf-view">
|
||||
{% for entry in object_list %}
|
||||
|
@ -23,27 +22,31 @@
|
|||
{{ 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>
|
||||
<span class="pficon pficon-user"></span>
|
||||
<strong>{{ entry.user }}</strong>
|
||||
</div>
|
||||
<div class="list-view-pf-additional-info-item">
|
||||
<span class="pficon pficon-screen"></span>
|
||||
<strong>{{ entry.request_ip }}</strong>
|
||||
<span class="pficon pficon-cluster"></span>
|
||||
<strong>{{ entry.app|default:'-' }}</strong>
|
||||
</div>
|
||||
<div class="list-view-pf-additional-info-item">
|
||||
<span class="pficon pficon-cluster"></span>
|
||||
<strong>{{ entry.app|default:'-' }}</strong>
|
||||
<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 %}
|
||||
</div>
|
||||
<script>
|
||||
$(document).ready(function () {
|
||||
// Row Checkbox Selection
|
||||
|
|
|
@ -4,53 +4,89 @@
|
|||
|
||||
{% block content %}
|
||||
<div class="container">
|
||||
<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>
|
||||
</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>
|
||||
</p>
|
||||
</div>
|
||||
<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>
|
||||
</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>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<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>
|
||||
</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>
|
||||
</p>
|
||||
</div>
|
||||
<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>
|
||||
</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>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<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>
|
||||
</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>
|
||||
</p>
|
||||
</div>
|
||||
<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>
|
||||
</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>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<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>
|
||||
</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>
|
||||
</p>
|
||||
</div>
|
||||
<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>
|
||||
</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>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<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>
|
||||
</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>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<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>
|
||||
</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>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<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>
|
||||
</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>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
from django.views.generic import TemplateView
|
||||
|
||||
from passbook.admin.mixins import AdminRequiredMixin
|
||||
from passbook.core.models import Application, Policy, Provider, User
|
||||
from passbook.core.models import (Application, Factor, Invitation, Policy,
|
||||
Provider, Source, User)
|
||||
|
||||
|
||||
class AdministrationOverviewView(AdminRequiredMixin, TemplateView):
|
||||
|
@ -15,4 +16,7 @@ class AdministrationOverviewView(AdminRequiredMixin, TemplateView):
|
|||
kwargs['policy_count'] = len(Policy.objects.all())
|
||||
kwargs['user_count'] = len(User.objects.all())
|
||||
kwargs['provider_count'] = len(Provider.objects.all())
|
||||
kwargs['source_count'] = len(Source.objects.all())
|
||||
kwargs['factor_count'] = len(Factor.objects.all())
|
||||
kwargs['invitation_count'] = len(Invitation.objects.all())
|
||||
return super().get_context_data(**kwargs)
|
||||
|
|
18
passbook/audit/migrations/0002_auto_20190221_1201.py
Normal file
18
passbook/audit/migrations/0002_auto_20190221_1201.py
Normal file
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 2.1.7 on 2019-02-21 12:01
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('passbook_audit', '0001_initial'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='loginattempt',
|
||||
name='created',
|
||||
field=models.DateTimeField(auto_now_add=True),
|
||||
),
|
||||
]
|
23
passbook/audit/migrations/0003_auto_20190221_1240.py
Normal file
23
passbook/audit/migrations/0003_auto_20190221_1240.py
Normal file
|
@ -0,0 +1,23 @@
|
|||
# Generated by Django 2.1.7 on 2019-02-21 12:40
|
||||
|
||||
import django.contrib.postgres.fields.jsonb
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('passbook_audit', '0002_auto_20190221_1201'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='auditentry',
|
||||
name='_context',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='auditentry',
|
||||
name='context',
|
||||
field=django.contrib.postgres.fields.jsonb.JSONField(blank=True, default=dict),
|
||||
),
|
||||
]
|
|
@ -1,10 +1,10 @@
|
|||
"""passbook audit models"""
|
||||
from datetime import timedelta
|
||||
from json import dumps, loads
|
||||
from logging import getLogger
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import AnonymousUser
|
||||
from django.contrib.postgres.fields import JSONField
|
||||
from django.core.exceptions import ValidationError
|
||||
from django.db import models
|
||||
from django.utils import timezone
|
||||
|
@ -43,18 +43,10 @@ class AuditEntry(UUIDModel):
|
|||
action = models.TextField(choices=ACTIONS)
|
||||
date = models.DateTimeField(auto_now_add=True)
|
||||
app = models.TextField()
|
||||
_context = models.TextField()
|
||||
_context_cache = None
|
||||
context = JSONField(default=dict, blank=True)
|
||||
request_ip = models.GenericIPAddressField()
|
||||
created = models.DateTimeField(auto_now_add=True)
|
||||
|
||||
@property
|
||||
def context(self):
|
||||
"""Load context data and load json"""
|
||||
if not self._context_cache:
|
||||
self._context_cache = loads(self._context)
|
||||
return self._context_cache
|
||||
|
||||
@staticmethod
|
||||
def create(action, request, **kwargs):
|
||||
"""Create AuditEntry from arguments"""
|
||||
|
@ -67,7 +59,7 @@ class AuditEntry(UUIDModel):
|
|||
user=user,
|
||||
# User 255.255.255.255 as fallback if IP cannot be determined
|
||||
request_ip=client_ip or '255.255.255.255',
|
||||
_context=dumps(kwargs))
|
||||
context=kwargs)
|
||||
LOGGER.debug("Logged %s from %s (%s)", action, request.user, client_ip)
|
||||
return entry
|
||||
|
||||
|
|
|
@ -15,8 +15,9 @@ class AuthenticationFactor(TemplateView):
|
|||
form = None
|
||||
required = True
|
||||
authenticator = None
|
||||
pending_user = None
|
||||
request = None
|
||||
template_name = 'login/form.html'
|
||||
template_name = 'login/factors/base.html'
|
||||
|
||||
def __init__(self, authenticator):
|
||||
self.authenticator = authenticator
|
||||
|
@ -26,4 +27,5 @@ class AuthenticationFactor(TemplateView):
|
|||
kwargs['is_login'] = True
|
||||
kwargs['title'] = _('Log in to your account')
|
||||
kwargs['primary_action'] = _('Log in')
|
||||
kwargs['pending_user'] = self.pending_user
|
||||
return super().get_context_data(**kwargs)
|
||||
|
|
|
@ -67,6 +67,7 @@ class AuthenticationView(UserPassesTestMixin, View):
|
|||
# Instantiate Next Factor and pass request
|
||||
factor = path_to_class(factor_class)
|
||||
self._current_factor = factor(self)
|
||||
self._current_factor.pending_user = self.pending_user
|
||||
self._current_factor.request = request
|
||||
return super().dispatch(request, *args, **kwargs)
|
||||
|
||||
|
@ -93,7 +94,8 @@ class AuthenticationView(UserPassesTestMixin, View):
|
|||
self.pending_factors
|
||||
self.request.session[AuthenticationView.SESSION_FACTOR] = next_factor
|
||||
LOGGER.debug("Rendering Factor is %s", next_factor)
|
||||
return redirect(reverse('passbook_core:auth-process', kwargs={'factor': next_factor}))
|
||||
# return redirect(reverse('passbook_core:auth-process', kwargs={'factor': next_factor}))
|
||||
return redirect(reverse('passbook_core:auth-process'))
|
||||
# User passed all factors
|
||||
LOGGER.debug("User passed all factors, logging in")
|
||||
return self._user_passed()
|
||||
|
@ -102,6 +104,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()
|
||||
return redirect(reverse('passbook_core:auth-denied'))
|
||||
|
||||
def _user_passed(self):
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
"""passbook Core Application forms"""
|
||||
from django import forms
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from passbook.core.models import Application, Provider
|
||||
|
||||
|
@ -19,3 +20,7 @@ class ApplicationForm(forms.ModelForm):
|
|||
'launch_url': forms.TextInput(),
|
||||
'icon_url': forms.TextInput(),
|
||||
}
|
||||
labels = {
|
||||
'launch_url': _('Launch URL'),
|
||||
'icon_url': _('Icon URL'),
|
||||
}
|
||||
|
|
|
@ -18,6 +18,11 @@ class LoginForm(forms.Form):
|
|||
uid_field = forms.CharField(widget=forms.TextInput(attrs={'placeholder': _('UID')}))
|
||||
remember_me = forms.BooleanField(required=False)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
if CONFIG.y('passbook.uid_fields') == ['email']:
|
||||
self.fields['uid_field'] = forms.EmailField()
|
||||
|
||||
def clean_uid_field(self):
|
||||
"""Validate uid_field after EmailValidator if 'email' is the only selected uid_fields"""
|
||||
if CONFIG.y('passbook.uid_fields') == ['email']:
|
||||
|
|
|
@ -17,7 +17,7 @@ class FactorForm(forms.ModelForm):
|
|||
class Meta:
|
||||
|
||||
model = Factor
|
||||
fields = ['name', 'slug', 'order', 'policies', 'type', 'enabled']
|
||||
fields = ['name', 'slug', 'order', 'policies', 'type', 'enabled', 'arguments']
|
||||
widgets = {
|
||||
'type': forms.Select(choices=get_factors()),
|
||||
'name': forms.TextInput(),
|
||||
|
|
28
passbook/core/migrations/0005_auto_20190221_1201.py
Normal file
28
passbook/core/migrations/0005_auto_20190221_1201.py
Normal file
|
@ -0,0 +1,28 @@
|
|||
# Generated by Django 2.1.7 on 2019-02-21 12:01
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('passbook_core', '0004_auto_20190216_1013'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='policy',
|
||||
name='created',
|
||||
field=models.DateTimeField(auto_now_add=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='policymodel',
|
||||
name='created',
|
||||
field=models.DateTimeField(auto_now_add=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='usersourceconnection',
|
||||
name='created',
|
||||
field=models.DateTimeField(auto_now_add=True),
|
||||
),
|
||||
]
|
19
passbook/core/migrations/0006_factor_arguments.py
Normal file
19
passbook/core/migrations/0006_factor_arguments.py
Normal file
|
@ -0,0 +1,19 @@
|
|||
# Generated by Django 2.1.7 on 2019-02-21 12:32
|
||||
|
||||
import django.contrib.postgres.fields.jsonb
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('passbook_core', '0005_auto_20190221_1201'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='factor',
|
||||
name='arguments',
|
||||
field=django.contrib.postgres.fields.jsonb.JSONField(default=dict),
|
||||
),
|
||||
]
|
19
passbook/core/migrations/0007_auto_20190221_1233.py
Normal file
19
passbook/core/migrations/0007_auto_20190221_1233.py
Normal file
|
@ -0,0 +1,19 @@
|
|||
# Generated by Django 2.1.7 on 2019-02-21 12:33
|
||||
|
||||
import django.contrib.postgres.fields.jsonb
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('passbook_core', '0006_factor_arguments'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='factor',
|
||||
name='arguments',
|
||||
field=django.contrib.postgres.fields.jsonb.JSONField(blank=True, default=dict),
|
||||
),
|
||||
]
|
|
@ -6,6 +6,7 @@ from time import sleep
|
|||
from uuid import uuid4
|
||||
|
||||
from django.contrib.auth.models import AbstractUser
|
||||
from django.contrib.postgres.fields import JSONField
|
||||
from django.db import models
|
||||
from django.urls import reverse_lazy
|
||||
from django.utils.translation import gettext as _
|
||||
|
@ -69,6 +70,7 @@ class Factor(PolicyModel):
|
|||
order = models.IntegerField()
|
||||
type = models.TextField(unique=True)
|
||||
enabled = models.BooleanField(default=True)
|
||||
arguments = JSONField(default=dict, blank=True)
|
||||
|
||||
def __str__(self):
|
||||
return "Factor %s" % self.slug
|
||||
|
|
|
@ -32,7 +32,7 @@ class PolicyEngine:
|
|||
"""Check policies for user"""
|
||||
signatures = []
|
||||
kwargs = {
|
||||
'__password__': getattr(user, '__password__')
|
||||
'__password__': getattr(user, '__password__', None)
|
||||
}
|
||||
for policy in self.policies:
|
||||
signatures.append(_policy_engine_task.s(user.pk, policy.pk.hex, **kwargs))
|
||||
|
|
|
@ -61,7 +61,7 @@ INSTALLED_APPS = [
|
|||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
'rest_framework',
|
||||
'rest_framework_swagger',
|
||||
'drf_yasg',
|
||||
'passbook.core.apps.PassbookCoreConfig',
|
||||
'passbook.admin.apps.PassbookAdminConfig',
|
||||
'passbook.api.apps.PassbookAPIConfig',
|
||||
|
|
|
@ -26,7 +26,7 @@
|
|||
<div class="login-pf-page">
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-sm-8 col-sm-offset-2 col-md-6 col-md-offset-3 col-lg-6 col-lg-offset-3">
|
||||
<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" />
|
||||
{% if config.login.subtext %}
|
||||
|
|
|
@ -1,8 +1,4 @@
|
|||
{% extends 'login/form.html' %}
|
||||
{% extends 'login/factors/base.html' %}
|
||||
|
||||
{% load i18n %}
|
||||
|
||||
{% block above_form %}
|
||||
<span class="pficon pficon-unlocked"></span>
|
||||
{% trans "This is a text" %}
|
||||
{% endblock %}
|
||||
|
|
31
passbook/core/templates/login/factors/base.html
Normal file
31
passbook/core/templates/login/factors/base.html
Normal file
|
@ -0,0 +1,31 @@
|
|||
{% extends 'login/form.html' %}
|
||||
|
||||
{% load i18n %}
|
||||
{% load utils %}
|
||||
|
||||
{% block head %}
|
||||
{{ block.super }}
|
||||
<style>
|
||||
.login-pf-settings img {
|
||||
max-height: 32px;
|
||||
border-radius: 100%;
|
||||
border-width: 1px;
|
||||
border-color: #000;
|
||||
}
|
||||
.login-pf-settings a {
|
||||
padding-top: 3px;
|
||||
padding-bottom: 3px;
|
||||
line-height: 32px;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block above_form %}
|
||||
<div class="form-group login-pf-settings">
|
||||
<p class="form-control-static">
|
||||
<img src="{% gravatar pending_user.email %}" alt="">
|
||||
{{ pending_user.username }}
|
||||
</p>
|
||||
<a href="{% url 'passbook_core:auth-login' %}">{% trans 'Not you?' %}</a>
|
||||
</div>
|
||||
{% endblock %}
|
|
@ -1,68 +0,0 @@
|
|||
{% extends "base/skeleton.html" %}
|
||||
|
||||
{% load static %}
|
||||
|
||||
{% block body %}
|
||||
<div class="login-pf-page">
|
||||
<div class="container-fluid">
|
||||
<div class="row">
|
||||
<div class="col-sm-8 col-sm-offset-2 col-md-6 col-md-offset-3 col-lg-6 col-lg-offset-3">
|
||||
<header class="login-pf-page-header">
|
||||
<img class="login-pf-brand" src="{% static 'img/Logo_Horizontal_Reversed.svg' %}" alt=" logo" />
|
||||
</header>
|
||||
<div class="row">
|
||||
<div class="col-sm-10 col-sm-offset-1 col-md-8 col-md-offset-2 col-lg-8 col-lg-offset-2">
|
||||
<div class="card-pf">
|
||||
<header class="login-pf-header">
|
||||
<select class="selectpicker">
|
||||
<option>English</option>
|
||||
<option>French</option>
|
||||
<option>Italian</option>
|
||||
</select>
|
||||
<h1>Single Sign-On</h1>
|
||||
<p class="text-center">Log in to <strong>Application</strong></p>
|
||||
</header>
|
||||
<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="form-group 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>
|
||||
<p class="login-pf-signup">Need an account?<a href="#">Sign up</a></p>
|
||||
</div><!-- card -->
|
||||
<footer class="login-pf-page-footer">
|
||||
<div class="login-pf-page-footer-sso-services">
|
||||
<p>One account for all your company services</p>
|
||||
<ul class="login-pf-page-footer-sso-services-logos">
|
||||
<li><img src="{% static 'img/google-drive.svg' %}" alt="google drive icon" /></li>
|
||||
<li><img src="{% static 'img/gmail.svg' %}" alt="gmail icon" /></li>
|
||||
<li><img src="{% static 'img/google-calendar.svg' %}" alt="google calendar icon" /></li>
|
||||
</ul>
|
||||
</div>
|
||||
<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><!-- col -->
|
||||
</div><!-- row -->
|
||||
</div><!-- col -->
|
||||
</div><!-- login-pf-page -->
|
||||
</div>
|
||||
<!--row-->
|
||||
</div>
|
||||
<!--container-->
|
||||
{% endblock %}
|
|
@ -14,7 +14,7 @@
|
|||
<span class="icon-bar"></span>
|
||||
</button>
|
||||
<a class="navbar-brand" href="/">
|
||||
<img src="{% static 'img/brand.svg' %}" alt="PatternFly Enterprise Application" />
|
||||
<img src="{% static 'img/brand.svg' %}" alt="passbook" />
|
||||
</a>
|
||||
</div>
|
||||
<div class="collapse navbar-collapse navbar-collapse-1">
|
||||
|
@ -63,7 +63,9 @@
|
|||
</div>
|
||||
</nav>
|
||||
<div class="container-fluid container-cards-pf">
|
||||
{% include 'partials/messages.html' %}
|
||||
<div class="container">
|
||||
{% include 'partials/messages.html' %}
|
||||
</div>
|
||||
{% block content %}
|
||||
{% endblock %}
|
||||
</div>
|
||||
|
|
|
@ -9,10 +9,18 @@
|
|||
<div class="nav-category">
|
||||
<h2>{% trans 'User Profile'%}</h2>
|
||||
<ul class="nav nav-pills nav-stacked">
|
||||
<li class="{% is_active 'passbook_core:user-settings' %}"><a href="{% url 'passbook_core:user-settings' %}"><i class="fa fa-desktop"></i>{% trans 'Details' %}</a></li>
|
||||
<li><a href="#"><i class="fa fa-cog"></i>System Services</a></li>
|
||||
<li><a href="#"><i class="fa fa-file-text-o"></i>Journal</a></li>
|
||||
<li><a href="#"><i class="fa fa-cloud"></i>Storage</a></li>
|
||||
<li class="{% is_active 'passbook_core:user-settings' %}">
|
||||
<a href="{% url 'passbook_core:user-settings' %}">
|
||||
<i class="fa fa-desktop"></i> {% trans 'Details' %}
|
||||
</a>
|
||||
</li>
|
||||
<li class="{% is_active 'passbook_core:user-settings' %}">
|
||||
<a href="{% url 'passbook_core:user-settings' %}">
|
||||
<i class="pficon pficon-locked"></i> {% trans 'Change Password' %}
|
||||
</a>
|
||||
</li>
|
||||
<li><a href="#"><i class="fa fa-file-text-o"></i> Journal</a></li>
|
||||
<li><a href="#"><i class="fa fa-cloud"></i> Storage</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
|
14
passbook/core/templates/user/change_password.html
Normal file
14
passbook/core/templates/user/change_password.html
Normal file
|
@ -0,0 +1,14 @@
|
|||
{% extends "user/base.html" %}
|
||||
|
||||
{% load i18n %}
|
||||
|
||||
{% block page %}
|
||||
<h1>{% trans 'Change Password' %}</h1>
|
||||
<form action="" method="post" class="form-horizontal">
|
||||
{% csrf_token %}
|
||||
{% include 'partials/form.html' %}
|
||||
<input class="btn btn-primary" type="submit" value="{% trans 'Update' %}">
|
||||
<a class="btn btn-danger"
|
||||
href="{% url 'passbook_core:user-delete' %}?back={{ request.get_full_path }}">{% trans 'Delete user' %}</a>
|
||||
</form>
|
||||
{% endblock %}
|
|
@ -13,6 +13,7 @@ class OverviewView(LoginRequiredMixin, TemplateView):
|
|||
template_name = 'overview/index.html'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
print(self.request.session.keys())
|
||||
kwargs['applications'] = self.request.user.applications.all()
|
||||
if self.request.user.is_superuser:
|
||||
kwargs['applications'] = Application.objects.all()
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
"""passbook LDAP Forms"""
|
||||
|
||||
from django import forms
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from passbook.admin.forms.source import SOURCE_FORM_FIELDS
|
||||
from passbook.ldap.models import LDAPSource
|
||||
|
@ -14,7 +15,7 @@ class LDAPSourceForm(forms.ModelForm):
|
|||
model = LDAPSource
|
||||
fields = SOURCE_FORM_FIELDS + ['server_uri', 'bind_cn', 'bind_password',
|
||||
'type', 'domain', 'base_dn', 'create_user',
|
||||
'reset_password', 'policies']
|
||||
'reset_password']
|
||||
widgets = {
|
||||
'name': forms.TextInput(),
|
||||
'server_uri': forms.TextInput(),
|
||||
|
@ -23,6 +24,11 @@ class LDAPSourceForm(forms.ModelForm):
|
|||
'domain': forms.TextInput(),
|
||||
'base_dn': forms.TextInput(),
|
||||
}
|
||||
labels = {
|
||||
'server_uri': _('Server URI'),
|
||||
'bind_cn': _('Bind CN'),
|
||||
'base_dn': _('Base DN'),
|
||||
}
|
||||
|
||||
# class GeneralSettingsForm(SettingsForm):
|
||||
# """general settings form"""
|
||||
|
|
|
@ -59,6 +59,7 @@ passbook:
|
|||
uid_fields:
|
||||
- username
|
||||
- email
|
||||
# Factors to load
|
||||
factors:
|
||||
- passbook.core.auth.factors.backend
|
||||
- passbook.core.auth.factors.dummy
|
||||
|
@ -67,22 +68,6 @@ passbook:
|
|||
remember_age: 2592000 # 60 * 60 * 24 * 30, one month
|
||||
# Provider-specific settings
|
||||
ldap:
|
||||
# # Completely enable or disable LDAP provider
|
||||
# enabled: false
|
||||
# # AD Domain, used to generate `userPrincipalName`
|
||||
# domain: corp.contoso.com
|
||||
# # Base DN in which passbook should look for users
|
||||
# base_dn: dn=corp,dn=contoso,dn=com
|
||||
# # LDAP field which is used to set the django username
|
||||
# username_field: sAMAccountName
|
||||
# # LDAP server to connect to, can be set to `<domain_name>`
|
||||
# server:
|
||||
# name: corp.contoso.com
|
||||
# use_tls: false
|
||||
# # Bind credentials, used for account creation
|
||||
# bind:
|
||||
# username: Administraotr@corp.contoso.com
|
||||
# password: VerySecurePassword!
|
||||
# Which field from `uid_fields` maps to which LDAP Attribute
|
||||
login_field_map:
|
||||
username: sAMAccountName
|
||||
|
@ -93,10 +78,6 @@ ldap:
|
|||
mail: email
|
||||
given_name: first_name
|
||||
name: last_name
|
||||
# # Create new users in LDAP upon sign-up
|
||||
# create_users: true
|
||||
# # Reset LDAP password when user reset their password
|
||||
# reset_password: true
|
||||
oauth_client:
|
||||
# List of python packages with sources types to load.
|
||||
types:
|
||||
|
@ -108,7 +89,6 @@ oauth_client:
|
|||
- passbook.oauth_client.source_types.supervisr
|
||||
- passbook.oauth_client.source_types.twitter
|
||||
saml_idp:
|
||||
issuer: passbook
|
||||
# List of python packages with provider types to load.
|
||||
types:
|
||||
- passbook.saml_idp.processors.generic
|
||||
|
|
|
@ -6,7 +6,7 @@ from django.db import models
|
|||
|
||||
class CreatedUpdatedModel(models.Model):
|
||||
"""Base Abstract Model to save created and update"""
|
||||
created = models.DateField(auto_now_add=True)
|
||||
created = models.DateTimeField(auto_now_add=True)
|
||||
last_updated = models.DateTimeField(auto_now=True)
|
||||
|
||||
class Meta:
|
||||
|
|
|
@ -2,8 +2,9 @@
|
|||
import glob
|
||||
import os
|
||||
import socket
|
||||
from hashlib import md5
|
||||
from importlib import import_module
|
||||
from urllib.parse import urljoin
|
||||
from urllib.parse import urlencode, urljoin
|
||||
|
||||
from django import template
|
||||
from django.apps import apps
|
||||
|
@ -11,6 +12,7 @@ from django.conf import settings
|
|||
from django.db.models import Model
|
||||
from django.template.loaders.app_directories import get_app_template_dirs
|
||||
from django.urls import reverse
|
||||
from django.utils.html import escape
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
from passbook.lib.config import CONFIG
|
||||
|
@ -178,3 +180,30 @@ def app_versions():
|
|||
ver = '.'.join([str(x) for x in ver])
|
||||
app_versions[app.verbose_name] = ver
|
||||
return app_versions
|
||||
|
||||
@register.simple_tag
|
||||
def gravatar(email, size=None, rating=None):
|
||||
"""
|
||||
Generates a Gravatar URL for the given email address.
|
||||
|
||||
Syntax::
|
||||
|
||||
{% gravatar <email> [size] [rating] %}
|
||||
|
||||
Example::
|
||||
|
||||
{% gravatar someone@example.com 48 pg %}
|
||||
"""
|
||||
# gravatar uses md5 for their URLs, so md5 can't be avoided
|
||||
gravatar_url = "%savatar/%s" % ('https://secure.gravatar.com/',
|
||||
md5(email.encode('utf-8')).hexdigest()) # nosec
|
||||
|
||||
parameters = [p for p in (
|
||||
('s', size or '158'),
|
||||
('r', rating or 'g'),
|
||||
) if p[1]]
|
||||
|
||||
if parameters:
|
||||
gravatar_url += '?' + urlencode(parameters, doseq=True)
|
||||
|
||||
return escape(gravatar_url)
|
||||
|
|
|
@ -19,7 +19,7 @@ class OAuthSource(Source):
|
|||
consumer_key = models.TextField()
|
||||
consumer_secret = models.TextField()
|
||||
|
||||
form = 'passbook.oauth_client.forms.GitHubOAuthSourceForm'
|
||||
form = 'passbook.oauth_client.forms.OAuthSourceForm'
|
||||
|
||||
@property
|
||||
def is_link(self):
|
||||
|
|
Reference in a new issue