Compare commits

...

8 Commits

63 changed files with 203 additions and 116 deletions

View File

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

View File

@ -53,7 +53,7 @@ package-docker:
before_script: before_script:
- echo "{\"auths\":{\"docker.$NEXUS_URL\":{\"auth\":\"$NEXUS_AUTH\"}}}" > /kaniko/.docker/config.json - echo "{\"auths\":{\"docker.$NEXUS_URL\":{\"auth\":\"$NEXUS_AUTH\"}}}" > /kaniko/.docker/config.json
script: script:
- /kaniko/executor --context $CI_PROJECT_DIR --dockerfile $CI_PROJECT_DIR/Dockerfile --destination docker.pkg.beryju.org/passbook:latest --destination docker.pkg.beryju.org/passbook:0.0.12-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.13-alpha
stage: build stage: build
only: only:
- tags - tags

View File

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

View File

@ -105,10 +105,9 @@ data:
email: mail # or userPrincipalName email: mail # or userPrincipalName
user_attribute_map: user_attribute_map:
active_directory: active_directory:
sAMAccountName: username username: "%(sAMAccountName)s"
mail: email email: "%(mail)s"
given_name: first_name name: "%(displayName)"
name: last_name
# # Create new users in LDAP upon sign-up # # Create new users in LDAP upon sign-up
# create_users: true # create_users: true
# # Reset LDAP password when user reset their password # # Reset LDAP password when user reset their password

View File

@ -5,7 +5,7 @@
replicaCount: 1 replicaCount: 1
image: image:
tag: 0.0.12-alpha tag: 0.0.13-alpha
nameOverride: "" nameOverride: ""

View File

@ -1,2 +1,2 @@
"""passbook""" """passbook"""
__version__ = '0.0.12-alpha' __version__ = '0.0.13-alpha'

View File

@ -1,2 +1,2 @@
"""passbook admin""" """passbook admin"""
__version__ = '0.0.12-alpha' __version__ = '0.0.13-alpha'

View File

@ -11,7 +11,7 @@ class UserSerializer(ModelSerializer):
class Meta: class Meta:
model = User model = User
fields = ['is_superuser', 'username', 'first_name', 'last_name', 'email', 'date_joined', fields = ['is_superuser', 'username', 'name', 'email', 'date_joined',
'uuid'] 'uuid']

View File

@ -12,7 +12,7 @@
<h1><span class="pficon-applications"></span> {% trans "Applications" %}</h1> <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> <span>{% trans "External Applications which use passbook as Identity-Provider, utilizing protocols like OAuth2 and SAML." %}</span>
<hr> <hr>
<a href="{% url 'passbook_admin:application-create' %}" class="btn btn-primary"> <a href="{% url 'passbook_admin:application-create' %}?back={{ request.get_full_path }}" class="btn btn-primary">
{% trans 'Create...' %} {% trans 'Create...' %}
</a> </a>
<hr> <hr>
@ -21,6 +21,7 @@
<tr> <tr>
<th>{% trans 'Name' %}</th> <th>{% trans 'Name' %}</th>
<th>{% trans 'Provider' %}</th> <th>{% trans 'Provider' %}</th>
<th>{% trans 'Provider Type' %}</th>
<th></th> <th></th>
</tr> </tr>
</thead> </thead>
@ -28,7 +29,8 @@
{% for application in object_list %} {% for application in object_list %}
<tr> <tr>
<td>{{ application.name }}</td> <td>{{ application.name }}</td>
<td>{{ application.provider }}</td> <td>{{ application.get_provider }}</td>
<td>{{ application.get_provider|verbose_name }}</td>
<td> <td>
<a class="btn btn-default btn-sm" <a class="btn btn-default btn-sm"
href="{% url 'passbook_admin:application-update' pk=application.uuid %}?back={{ request.get_full_path }}">{% trans 'Edit' %}</a> href="{% url 'passbook_admin:application-update' pk=application.uuid %}?back={{ request.get_full_path }}">{% trans 'Edit' %}</a>

View File

@ -21,7 +21,7 @@
<ul class="dropdown-menu" role="menu" aria-labelledby="createDropdown"> <ul class="dropdown-menu" role="menu" aria-labelledby="createDropdown">
{% for type, name in types.items %} {% for type, name in types.items %}
<li role="presentation"><a role="menuitem" tabindex="-1" <li role="presentation"><a role="menuitem" tabindex="-1"
href="{% url 'passbook_admin:factor-create' %}?type={{ type }}">{{ name }}</a></li> href="{% url 'passbook_admin:factor-create' %}?type={{ type }}&back={{ request.get_full_path }}">{{ name }}</a></li>
{% endfor %} {% endfor %}
</ul> </ul>
</div> </div>
@ -40,7 +40,7 @@
{% for factor in object_list %} {% for factor in object_list %}
<tr> <tr>
<td>{{ factor.name }} ({{ factor.slug }})</td> <td>{{ factor.name }} ({{ factor.slug }})</td>
<td>{{ factor.type }}</td> <td>{{ factor|verbose_name }}</td>
<td>{{ factor.order }}</td> <td>{{ factor.order }}</td>
<td>{{ factor.enabled }}</td> <td>{{ factor.enabled }}</td>
<td> <td>

View File

@ -12,7 +12,7 @@
<h1><span class="pficon-migration"></span> {% trans "Invitations" %}</h1> <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> <span>{% trans "Create Invitation Links which optionally force a username or expire on a set date." %}</span>
<hr> <hr>
<a href="{% url 'passbook_admin:invitation-create' %}" class="btn btn-primary"> <a href="{% url 'passbook_admin:invitation-create' %}?back={{ request.get_full_path }}" class="btn btn-primary">
{% trans 'Create...' %} {% trans 'Create...' %}
</a> </a>
<hr> <hr>

View File

@ -54,7 +54,11 @@
<p class="card-pf-aggregate-status-notifications"> <p class="card-pf-aggregate-status-notifications">
<span class="card-pf-aggregate-status-notification"> <span class="card-pf-aggregate-status-notification">
<a href="{% url 'passbook_admin:providers' %}"> <a href="{% url 'passbook_admin:providers' %}">
<span class="pficon pficon-ok"></span>{{ provider_count }} {% if providers_without_application.exists %}
<span class="pficon-warning-triangle-o" data-toggle="tooltip" data-placement="right" title="{% trans 'Warning: At least one Provider has no application assigned.' %}"></span> {{ provider_count }}
{% else %}
<span class="pficon pficon-ok"></span> {{ provider_count }}
{% endif %}
</a> </a>
</span> </span>
</p> </p>
@ -168,7 +172,12 @@
<p class="card-pf-aggregate-status-notifications"> <p class="card-pf-aggregate-status-notifications">
<span class="card-pf-aggregate-status-notification"> <span class="card-pf-aggregate-status-notification">
<a href="#"> <a href="#">
{% if worker_count < 1%}
<span class="pficon-error-circle-o" data-toggle="tooltip" data-placement="right"
title="{% trans 'No workers connected. Policies may not work.' %}"></span> {{ worker_count }}
{% else %}
<span class="pficon pficon-ok"></span>{{ worker_count }} <span class="pficon pficon-ok"></span>{{ worker_count }}
{% endif %}
</a> </a>
</span> </span>
</p> </p>

View File

@ -20,7 +20,7 @@
<ul class="dropdown-menu" role="menu" aria-labelledby="createDropdown"> <ul class="dropdown-menu" role="menu" aria-labelledby="createDropdown">
{% for type, name in types.items %} {% for type, name in types.items %}
<li role="presentation"><a role="menuitem" tabindex="-1" <li role="presentation"><a role="menuitem" tabindex="-1"
href="{% url 'passbook_admin:policy-create' %}?type={{ type }}">{{ name }}</a></li> href="{% url 'passbook_admin:policy-create' %}?type={{ type }}&back={{ request.get_full_path }}">{{ name }}</a></li>
{% endfor %} {% endfor %}
</ul> </ul>
</div> </div>
@ -29,7 +29,7 @@
<thead> <thead>
<tr> <tr>
<th>{% trans 'Name' %}</th> <th>{% trans 'Name' %}</th>
<th>{% trans 'Class' %}</th> <th>{% trans 'Type' %}</th>
<th></th> <th></th>
</tr> </tr>
</thead> </thead>
@ -37,7 +37,7 @@
{% for policy in object_list %} {% for policy in object_list %}
<tr> <tr>
<td>{{ policy.name }}</td> <td>{{ policy.name }}</td>
<td>{{ policy|fieldtype }}</td> <td>{{ policy|verbose_name }}</td>
<td> <td>
<a class="btn btn-default btn-sm" <a class="btn btn-default btn-sm"
href="{% url 'passbook_admin:policy-update' pk=policy.uuid %}?back={{ request.get_full_path }}">{% trans 'Edit' %}</a> href="{% url 'passbook_admin:policy-update' pk=policy.uuid %}?back={{ request.get_full_path }}">{% trans 'Edit' %}</a>

View File

@ -21,7 +21,7 @@
<ul class="dropdown-menu" role="menu" aria-labelledby="createDropdown"> <ul class="dropdown-menu" role="menu" aria-labelledby="createDropdown">
{% for type, name in types.items %} {% for type, name in types.items %}
<li role="presentation"><a role="menuitem" tabindex="-1" <li role="presentation"><a role="menuitem" tabindex="-1"
href="{% url 'passbook_admin:provider-create' %}?type={{ type }}">{{ name }}</a></li> href="{% url 'passbook_admin:provider-create' %}?type={{ type }}&back={{ request.get_full_path }}">{{ name }}</a></li>
{% endfor %} {% endfor %}
</ul> </ul>
</div> </div>
@ -29,16 +29,24 @@
<table class="table table-striped table-bordered"> <table class="table table-striped table-bordered">
<thead> <thead>
<tr> <tr>
<th></th>
<th>{% trans 'Name' %}</th> <th>{% trans 'Name' %}</th>
<th>{% trans 'Class' %}</th> <th>{% trans 'Type' %}</th>
<th></th> <th></th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
{% for provider in object_list %} {% for provider in object_list %}
<tr> <tr {% if not provider.application %} class="warning" {% endif %}>
<th>
{% if not provider.application %}
<span class="pficon-warning-triangle-o" data-toggle="tooltip" data-placement="right" title="{% trans 'Warning: Provider has no application assigned.' %}"></span>
{% else %}
<span class="pficon-ok" data-toggle="tooltip" data-placement="right" title="{% blocktrans with app=provider.application %}Assigned to Application {{ app }}{% endblocktrans %}"></span>
{% endif %}
</th>
<td>{{ provider.name }}</td> <td>{{ provider.name }}</td>
<td>{{ provider|fieldtype }}</td> <td>{{ provider|verbose_name }}</td>
<td> <td>
<a class="btn btn-default btn-sm" <a class="btn btn-default btn-sm"
href="{% url 'passbook_admin:provider-update' pk=provider.pk %}?back={{ request.get_full_path }}">{% trans 'Edit' %}</a> href="{% url 'passbook_admin:provider-update' pk=provider.pk %}?back={{ request.get_full_path }}">{% trans 'Edit' %}</a>

View File

@ -17,7 +17,7 @@
<ul class="dropdown-menu" role="menu" aria-labelledby="createDropdown"> <ul class="dropdown-menu" role="menu" aria-labelledby="createDropdown">
{% for type, name in types.items %} {% for type, name in types.items %}
<li role="presentation"><a role="menuitem" tabindex="-1" <li role="presentation"><a role="menuitem" tabindex="-1"
href="{% url 'passbook_admin:source-create' %}?type={{ type }}">{{ name }}</a></li> href="{% url 'passbook_admin:source-create' %}?type={{ type }}&back={{ request.get_full_path }}">{{ name }}</a></li>
{% endfor %} {% endfor %}
</ul> </ul>
</div> </div>

View File

@ -11,8 +11,7 @@
<thead> <thead>
<tr> <tr>
<th>{% trans 'Username' %}</th> <th>{% trans 'Username' %}</th>
<th>{% trans 'First Name' %}</th> <th>{% trans 'Name' %}</th>
<th>{% trans 'Last Name' %}</th>
<th>{% trans 'Active' %}</th> <th>{% trans 'Active' %}</th>
<th>{% trans 'Last Login' %}</th> <th>{% trans 'Last Login' %}</th>
<th></th> <th></th>
@ -22,8 +21,7 @@
{% for user in object_list %} {% for user in object_list %}
<tr> <tr>
<td>{{ user.username }}</td> <td>{{ user.username }}</td>
<td>{{ user.first_name|default:'-' }}</td> <td>{{ user.name|default:'-' }}</td>
<td>{{ user.last_name|default:'-' }}</td>
<td>{{ user.is_active }}</td> <td>{{ user.is_active }}</td>
<td>{{ user.last_login }}</td> <td>{{ user.last_login }}</td>
<td> <td>

View File

@ -1,11 +1,12 @@
{% extends "generic/form.html" %} {% extends "generic/form.html" %}
{% load utils %}
{% load i18n %} {% load i18n %}
{% block above_form %} {% block above_form %}
<h1>{% trans 'Create' %}</h1> <h1>{% blocktrans with type=form|form_verbose_name %}Create {{ type }}{% endblocktrans %}</h1>
{% endblock %} {% endblock %}
{% block action %} {% block action %}
{% trans 'Create' %} {% blocktrans with type=form|form_verbose_name %}Create {{ type }}{% endblocktrans %}
{% endblock %} {% endblock %}

View File

@ -1,11 +0,0 @@
{% extends "generic/create.html" %}
{% load i18n %}
{% block title %}
{% blocktrans with type=request.GET.type %}Create {{ type }}{% endblocktrans %}
{% endblock %}
{% block above_form %}
<h1>{% blocktrans with type=request.GET.type %}Create {{ type }}{% endblocktrans %}</h1>
{% endblock %}

View File

@ -1,11 +1,12 @@
{% extends "generic/form.html" %} {% extends "generic/form.html" %}
{% load utils %}
{% load i18n %} {% load i18n %}
{% block above_form %} {% block above_form %}
<h1>{% trans 'Update' %}</h1> <h1>{% blocktrans with type=form|form_verbose_name %}Update {{ type }}{% endblocktrans %}</h1>
{% endblock %} {% endblock %}
{% block action %} {% block action %}
{% trans 'Update' %} {% blocktrans with type=form|form_verbose_name %}Update {{ type }}{% endblocktrans %}
{% endblock %} {% endblock %}

View File

@ -14,6 +14,7 @@ class ApplicationListView(AdminRequiredMixin, ListView):
"""Show list of all applications""" """Show list of all applications"""
model = Application model = Application
ordering = 'name'
template_name = 'administration/application/list.html' template_name = 'administration/application/list.html'
def get_queryset(self): def get_queryset(self):
@ -29,6 +30,10 @@ class ApplicationCreateView(SuccessMessageMixin, AdminRequiredMixin, CreateView)
success_url = reverse_lazy('passbook_admin:applications') success_url = reverse_lazy('passbook_admin:applications')
success_message = _('Successfully created Application') success_message = _('Successfully created Application')
def get_context_data(self, **kwargs):
kwargs['type'] = 'Application'
return super().get_context_data(**kwargs)
class ApplicationUpdateView(SuccessMessageMixin, AdminRequiredMixin, UpdateView): class ApplicationUpdateView(SuccessMessageMixin, AdminRequiredMixin, UpdateView):
"""Update application""" """Update application"""

View File

@ -34,7 +34,7 @@ class FactorListView(AdminRequiredMixin, ListView):
class FactorCreateView(SuccessMessageMixin, AdminRequiredMixin, CreateView): class FactorCreateView(SuccessMessageMixin, AdminRequiredMixin, CreateView):
"""Create new Factor""" """Create new Factor"""
template_name = 'generic/create_inheritance.html' template_name = 'generic/create.html'
success_url = reverse_lazy('passbook_admin:factors') success_url = reverse_lazy('passbook_admin:factors')
success_message = _('Successfully created Factor') success_message = _('Successfully created Factor')

View File

@ -27,6 +27,10 @@ class InvitationCreateView(SuccessMessageMixin, AdminRequiredMixin, CreateView):
success_message = _('Successfully created Invitation') success_message = _('Successfully created Invitation')
form_class = InvitationForm form_class = InvitationForm
def get_context_data(self, **kwargs):
kwargs['type'] = 'Invitation'
return super().get_context_data(**kwargs)
def form_valid(self, form): def form_valid(self, form):
obj = form.save(commit=False) obj = form.save(commit=False)
obj.created_by = self.request.user obj.created_by = self.request.user

View File

@ -23,4 +23,5 @@ class AdministrationOverviewView(AdminRequiredMixin, TemplateView):
kwargs['invitation_count'] = len(Invitation.objects.all()) kwargs['invitation_count'] = len(Invitation.objects.all())
kwargs['version'] = __version__ kwargs['version'] = __version__
kwargs['worker_count'] = len(CELERY_APP.control.ping(timeout=0.5)) kwargs['worker_count'] = len(CELERY_APP.control.ping(timeout=0.5))
kwargs['providers_without_application'] = Provider.objects.filter(application=None)
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)

View File

@ -32,7 +32,7 @@ class PolicyListView(AdminRequiredMixin, ListView):
class PolicyCreateView(SuccessMessageMixin, AdminRequiredMixin, CreateView): class PolicyCreateView(SuccessMessageMixin, AdminRequiredMixin, CreateView):
"""Create new Policy""" """Create new Policy"""
template_name = 'generic/create_inheritance.html' template_name = 'generic/create.html'
success_url = reverse_lazy('passbook_admin:policies') success_url = reverse_lazy('passbook_admin:policies')
success_message = _('Successfully created Policy') success_message = _('Successfully created Policy')

View File

@ -29,7 +29,7 @@ class ProviderListView(AdminRequiredMixin, ListView):
class ProviderCreateView(SuccessMessageMixin, AdminRequiredMixin, CreateView): class ProviderCreateView(SuccessMessageMixin, AdminRequiredMixin, CreateView):
"""Create new Provider""" """Create new Provider"""
template_name = 'generic/create_inheritance.html' template_name = 'generic/create.html'
success_url = reverse_lazy('passbook_admin:providers') success_url = reverse_lazy('passbook_admin:providers')
success_message = _('Successfully created Provider') success_message = _('Successfully created Provider')

View File

@ -34,7 +34,7 @@ class SourceListView(AdminRequiredMixin, ListView):
class SourceCreateView(SuccessMessageMixin, AdminRequiredMixin, CreateView): class SourceCreateView(SuccessMessageMixin, AdminRequiredMixin, CreateView):
"""Create new Source""" """Create new Source"""
template_name = 'generic/create_inheritance.html' template_name = 'generic/create.html'
success_url = reverse_lazy('passbook_admin:sources') success_url = reverse_lazy('passbook_admin:sources')
success_message = _('Successfully created Source') success_message = _('Successfully created Source')

View File

@ -1,2 +1,2 @@
"""passbook api""" """passbook api"""
__version__ = '0.0.12-alpha' __version__ = '0.0.13-alpha'

View File

@ -14,8 +14,8 @@ class OpenIDUserInfoView(ScopedResourceMixin, View):
payload = { payload = {
'sub': request.user.uuid.int, 'sub': request.user.uuid.int,
'name': request.user.get_full_name(), 'name': request.user.get_full_name(),
'given_name': request.user.first_name, 'given_name': request.user.name,
'family_name': request.user.last_name, 'family_name': '',
'preferred_username': request.user.username, 'preferred_username': request.user.username,
'email': request.user.email, 'email': request.user.email,
} }

View File

@ -1,2 +1,2 @@
"""passbook audit Header""" """passbook audit Header"""
__version__ = '0.0.12-alpha' __version__ = '0.0.13-alpha'

View File

@ -1,2 +1,2 @@
"""passbook captcha_factor Header""" """passbook captcha_factor Header"""
__version__ = '0.0.12-alpha' __version__ = '0.0.13-alpha'

View File

@ -1,2 +1,2 @@
"""passbook core""" """passbook core"""
__version__ = '0.0.12-alpha' __version__ = '0.0.13-alpha'

View File

@ -8,6 +8,7 @@ from django.utils.http import urlencode
from django.views.generic import View from django.views.generic import View
from passbook.core.models import Factor, User from passbook.core.models import Factor, User
from passbook.core.policies import PolicyEngine
from passbook.core.views.utils import PermissionDeniedView from passbook.core.views.utils import PermissionDeniedView
from passbook.lib.utils.reflection import class_to_path, path_to_class from passbook.lib.utils.reflection import class_to_path, path_to_class
from passbook.lib.utils.urls import is_url_absolute from passbook.lib.utils.urls import is_url_absolute
@ -63,7 +64,9 @@ class AuthenticationView(UserPassesTestMixin, View):
_all_factors = Factor.objects.filter(enabled=True).order_by('order').select_subclasses() _all_factors = Factor.objects.filter(enabled=True).order_by('order').select_subclasses()
self.pending_factors = [] self.pending_factors = []
for factor in _all_factors: for factor in _all_factors:
if factor.passes(self.pending_user): policy_engine = PolicyEngine(factor.policies.all())
policy_engine.for_user(self.pending_user)
if policy_engine.result[0]:
self.pending_factors.append((factor.uuid.hex, factor.type)) self.pending_factors.append((factor.uuid.hex, factor.type))
# Read and instantiate factor from session # Read and instantiate factor from session
factor_uuid, factor_class = None, None factor_uuid, factor_class = None, None

View File

@ -38,10 +38,8 @@ class SignUpForm(forms.Form):
"""SignUp Form""" """SignUp Form"""
title = _('Sign Up') title = _('Sign Up')
first_name = forms.CharField(label=_('First Name'), name = forms.CharField(label=_('Name'),
widget=forms.TextInput(attrs={'placeholder': _('First Name')})) widget=forms.TextInput(attrs={'placeholder': _('Name')}))
last_name = forms.CharField(label=_('Last Name'),
widget=forms.TextInput(attrs={'placeholder': _('Last Name')}))
username = forms.CharField(label=_('Username'), username = forms.CharField(label=_('Username'),
widget=forms.TextInput(attrs={'placeholder': _('Username')})) widget=forms.TextInput(attrs={'placeholder': _('Username')}))
email = forms.EmailField(label=_('E-Mail'), email = forms.EmailField(label=_('E-Mail'),

View File

@ -13,7 +13,10 @@ class UserDetailForm(forms.ModelForm):
class Meta: class Meta:
model = User model = User
fields = ['username', 'first_name', 'last_name', 'email'] fields = ['username', 'name', 'email']
widgets = {
'name': forms.TextInput
}
class PasswordChangeForm(forms.Form): class PasswordChangeForm(forms.Form):
"""Form to update password""" """Form to update password"""

View File

@ -0,0 +1,38 @@
# Generated by Django 2.1.7 on 2019-02-27 13:55
from django.db import migrations, models
def migrate_names(apps, schema_editor):
"""migrate first_name and last_name to name"""
User = apps.get_model("passbook_core", "User")
for user in User.objects.all():
user.name = '%s %s' % (user.first_name, user.last_name)
user.save()
class Migration(migrations.Migration):
dependencies = [
('passbook_core', '0015_passwordpolicy_error_message'),
]
operations = [
migrations.AddField(
model_name='user',
name='name',
field=models.TextField(default=''),
preserve_default=False,
),
migrations.RunPython(migrate_names),
migrations.AlterField(
model_name='user',
name='name',
field=models.TextField(),
preserve_default=False,
),
migrations.AlterField(
model_name='fieldmatcherpolicy',
name='user_field',
field=models.TextField(choices=[('username', 'Username'), ('name', 'Name'), ('email', 'E-Mail'), ('is_staff', 'Is staff'), ('is_active', 'Is active'), ('data_joined', 'Date joined')]),
),
]

View File

@ -44,6 +44,8 @@ class User(AbstractUser):
"""Custom User model to allow easier adding o f user-based settings""" """Custom User model to allow easier adding o f user-based settings"""
uuid = models.UUIDField(default=uuid4, editable=False) uuid = models.UUIDField(default=uuid4, editable=False)
name = models.TextField()
sources = models.ManyToManyField('Source', through='UserSourceConnection') sources = models.ManyToManyField('Source', through='UserSourceConnection')
applications = models.ManyToManyField('Application') applications = models.ManyToManyField('Application')
groups = models.ManyToManyField('Group') groups = models.ManyToManyField('Group')
@ -71,14 +73,6 @@ class PolicyModel(UUIDModel, CreatedUpdatedModel):
policies = models.ManyToManyField('Policy', blank=True) policies = models.ManyToManyField('Policy', blank=True)
def passes(self, user: User) -> Union[bool, Tuple[bool, str]]:
"""Return False, str if a user fails where str is a
reasons shown to the user. Return True if user succeeds."""
for policy in self.policies.all():
if not policy.passes(user):
return False
return True
class Factor(PolicyModel): class Factor(PolicyModel):
"""Authentication factor, multiple instances of the same Factor can be used""" """Authentication factor, multiple instances of the same Factor can be used"""
@ -161,6 +155,10 @@ class Application(PolicyModel):
from passbook.core.policies import PolicyEngine from passbook.core.policies import PolicyEngine
return PolicyEngine(self.policies.all()).for_user(user).result return PolicyEngine(self.policies.all()).for_user(user).result
def get_provider(self):
"""Get casted provider instance"""
return Provider.objects.get_subclass(pk=self.provider.pk)
def __str__(self): def __str__(self):
return self.name return self.name
@ -249,8 +247,7 @@ class FieldMatcherPolicy(Policy):
USER_FIELDS = ( USER_FIELDS = (
('username', _('Username'),), ('username', _('Username'),),
('first_name', _('First Name'),), ('name', _('Name'),),
('last_name', _('Last Name'),),
('email', _('E-Mail'),), ('email', _('E-Mail'),),
('is_staff', _('Is staff'),), ('is_staff', _('Is staff'),),
('is_active', _('Is active'),), ('is_active', _('Is active'),),

View File

@ -19,7 +19,6 @@ def password_policy_checker(sender, password, **kwargs):
setattr(sender, '__password__', password) setattr(sender, '__password__', password)
_all_factors = PasswordFactor.objects.filter(enabled=True).order_by('order') _all_factors = PasswordFactor.objects.filter(enabled=True).order_by('order')
for factor in _all_factors: for factor in _all_factors:
if factor.passes(sender):
policy_engine = PolicyEngine(factor.password_policies.all().select_subclasses()) policy_engine = PolicyEngine(factor.password_policies.all().select_subclasses())
policy_engine.for_user(sender) policy_engine.for_user(sender)
passing, messages = policy_engine.result passing, messages = policy_engine.result

View File

@ -2,7 +2,7 @@
{% csrf_token %} {% csrf_token %}
{% for field in form %} {% for field in form %}
<div class="form-group"> <div class="form-group {% if field.errors %} has-error {% endif %}">
{% if field.field.widget|fieldtype == 'RadioSelect' %} {% if field.field.widget|fieldtype == 'RadioSelect' %}
<label class="col-sm-2 control-label" {% if field.field.required %}class="required"{% endif %} for="{{ field.name }}-{{ forloop.counter0 }}"> <label class="col-sm-2 control-label" {% if field.field.required %}class="required"{% endif %} for="{{ field.name }}-{{ forloop.counter0 }}">
{{ field.label }} {{ field.label }}
@ -40,11 +40,9 @@
</span> </span>
{% endif %} {% endif %}
{% for error in field.errors %} {% for error in field.errors %}
<hr> <span class="help-block">
<div class="alert alert-danger"> {{ error }}
<span class="pficon pficon-error-circle-o"></span> </span>
<strong>{{ error }}</strong>
</div>
{% endfor %} {% endfor %}
</div> </div>
{% endif %} {% endif %}

View File

@ -3,6 +3,7 @@
from django import template from django import template
from passbook.core.models import Factor from passbook.core.models import Factor
from passbook.core.policies import PolicyEngine
register = template.Library() register = template.Library()
@ -14,6 +15,8 @@ def user_factors(context):
matching_factors = [] matching_factors = []
for factor in _all_factors: for factor in _all_factors:
_link = factor.has_user_settings() _link = factor.has_user_settings()
if factor.passes(user) and _link: policy_engine = PolicyEngine(factor.policies.all())
policy_engine.for_user(user)
if policy_engine.result[0] and _link:
matching_factors.append(_link) matching_factors.append(_link)
return matching_factors return matching_factors

View File

@ -15,8 +15,7 @@ class TestAuthenticationViews(TestCase):
def setUp(self): def setUp(self):
super().setUp() super().setUp()
self.sign_up_data = { self.sign_up_data = {
'first_name': 'Test', 'name': 'Test',
'last_name': 'User',
'username': 'beryjuorg', 'username': 'beryjuorg',
'email': 'unittest@passbook.beryju.org', 'email': 'unittest@passbook.beryju.org',
'password': 'B3ryju0rg!', 'password': 'B3ryju0rg!',

View File

@ -204,8 +204,7 @@ class SignUpView(UserPassesTestMixin, FormView):
new_user = User.objects.create( new_user = User.objects.create(
username=data.get('username'), username=data.get('username'),
email=data.get('email'), email=data.get('email'),
first_name=data.get('first_name'), name=data.get('name'),
last_name=data.get('last_name'),
) )
new_user.is_active = True new_user.is_active = True
try: try:

View File

@ -1,8 +1,10 @@
"""passbook core user views""" """passbook core user views"""
from django.contrib import messages from django.contrib import messages
from django.contrib.auth import logout, update_session_auth_hash from django.contrib.auth import logout, update_session_auth_hash
from django.contrib.messages.views import SuccessMessageMixin
from django.forms.utils import ErrorList from django.forms.utils import ErrorList
from django.shortcuts import redirect, reverse from django.shortcuts import redirect, reverse
from django.urls import reverse_lazy
from django.utils.translation import gettext as _ from django.utils.translation import gettext as _
from django.views.generic import DeleteView, FormView, UpdateView from django.views.generic import DeleteView, FormView, UpdateView
@ -11,12 +13,15 @@ from passbook.core.forms.users import PasswordChangeForm, UserDetailForm
from passbook.lib.config import CONFIG from passbook.lib.config import CONFIG
class UserSettingsView(UpdateView): class UserSettingsView(SuccessMessageMixin, UpdateView):
"""Update User settings""" """Update User settings"""
template_name = 'user/settings.html' template_name = 'user/settings.html'
form_class = UserDetailForm form_class = UserDetailForm
success_message = _('Successfully updated user.')
success_url = reverse_lazy('passbook_core:user-settings')
def get_object(self): def get_object(self):
return self.request.user return self.request.user

View File

@ -0,0 +1,17 @@
# Generated by Django 2.1.7 on 2019-02-27 15:05
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('passbook_hibp_policy', '0002_auto_20190225_1912'),
]
operations = [
migrations.AlterModelOptions(
name='haveibeenpwendpolicy',
options={'verbose_name': 'Have I Been Pwned Policy', 'verbose_name_plural': 'Have I Been Pwned Policies'},
),
]

View File

@ -41,5 +41,5 @@ class HaveIBeenPwendPolicy(Policy):
class Meta: class Meta:
verbose_name = _('have i been pwned Policy') verbose_name = _('Have I Been Pwned Policy')
verbose_name_plural = _('have i been pwned Policies') verbose_name_plural = _('Have I Been Pwned Policies')

View File

@ -1,2 +1,2 @@
"""Passbook ldap app Header""" """Passbook ldap app Header"""
__version__ = '0.0.12-alpha' __version__ = '0.0.13-alpha'

View File

@ -129,7 +129,7 @@ class LDAPConnector:
# Create the user data. # Create the user data.
field_map = { field_map = {
'username': '%(' + USERNAME_FIELD + ')s', 'username': '%(' + USERNAME_FIELD + ')s',
'first_name': '%(givenName)s %(sn)s', 'name': '%(givenName)s %(sn)s',
'email': '%(mail)s', 'email': '%(mail)s',
} }
user_fields = {} user_fields = {}
@ -224,9 +224,9 @@ class LDAPConnector:
'cn': str(username), 'cn': str(username),
'description': str('t=' + time()), 'description': str('t=' + time()),
'sAMAccountName': str(username_trunk), 'sAMAccountName': str(username_trunk),
'givenName': str(user.first_name), 'givenName': str(user.name),
'displayName': str(user.username), 'displayName': str(user.username),
'name': str(user.first_name), 'name': str(user.name),
'mail': str(user.email), 'mail': str(user.email),
'userPrincipalName': str(username + '@' + self._source.domain), 'userPrincipalName': str(username + '@' + self._source.domain),
'objectClass': ['top', 'person', 'organizationalPerson', 'user'], 'objectClass': ['top', 'person', 'organizationalPerson', 'user'],

View File

@ -1,2 +1,2 @@
"""passbook lib""" """passbook lib"""
__version__ = '0.0.12-alpha' __version__ = '0.0.13-alpha'

View File

@ -77,10 +77,9 @@ ldap:
email: mail # or userPrincipalName email: mail # or userPrincipalName
user_attribute_map: user_attribute_map:
active_directory: active_directory:
sAMAccountName: username username: "%(sAMAccountName)s"
mail: email email: "%(mail)s"
given_name: first_name name: "%(displayName)"
name: last_name
oauth_client: oauth_client:
# List of python packages with sources types to load. # List of python packages with sources types to load.
types: types:

View File

@ -207,3 +207,15 @@ def gravatar(email, size=None, rating=None):
gravatar_url += '?' + urlencode(parameters, doseq=True) gravatar_url += '?' + urlencode(parameters, doseq=True)
return escape(gravatar_url) return escape(gravatar_url)
@register.filter
def verbose_name(obj):
"""Return Object's Verbose Name"""
return obj._meta.verbose_name
@register.filter
def form_verbose_name(obj):
"""Return ModelForm's Object's Verbose Name"""
return obj._meta.model._meta.verbose_name

View File

@ -1,2 +1,2 @@
"""passbook oauth_client Header""" """passbook oauth_client Header"""
__version__ = '0.0.12-alpha' __version__ = '0.0.13-alpha'

View File

@ -52,7 +52,7 @@ class DiscordOAuth2Callback(OAuthCallback):
user_data = { user_data = {
'username': info.get('username'), 'username': info.get('username'),
'email': info.get('email', 'None'), 'email': info.get('email', 'None'),
'first_name': info.get('username'), 'name': info.get('username'),
'password': None, 'password': None,
} }
discord_user = user_get_or_create(**user_data) discord_user = user_get_or_create(**user_data)

View File

@ -23,7 +23,7 @@ class FacebookOAuth2Callback(OAuthCallback):
user_data = { user_data = {
'username': info.get('name'), 'username': info.get('name'),
'email': info.get('email', ''), 'email': info.get('email', ''),
'first_name': info.get('name'), 'name': info.get('name'),
'password': None, 'password': None,
} }
fb_user = user_get_or_create(**user_data) fb_user = user_get_or_create(**user_data)

View File

@ -13,7 +13,7 @@ class GitHubOAuth2Callback(OAuthCallback):
user_data = { user_data = {
'username': info.get('login'), 'username': info.get('login'),
'email': info.get('email', ''), 'email': info.get('email', ''),
'first_name': info.get('name'), 'name': info.get('name'),
'password': None, 'password': None,
} }
gh_user = user_get_or_create(**user_data) gh_user = user_get_or_create(**user_data)

View File

@ -22,7 +22,7 @@ class GoogleOAuth2Callback(OAuthCallback):
user_data = { user_data = {
'username': info.get('email'), 'username': info.get('email'),
'email': info.get('email', ''), 'email': info.get('email', ''),
'first_name': info.get('name'), 'name': info.get('name'),
'password': None, 'password': None,
} }
google_user = user_get_or_create(**user_data) google_user = user_get_or_create(**user_data)

View File

@ -61,7 +61,7 @@ class RedditOAuth2Callback(OAuthCallback):
user_data = { user_data = {
'username': info.get('name'), 'username': info.get('name'),
'email': None, 'email': None,
'first_name': info.get('name'), 'name': info.get('name'),
'password': None, 'password': None,
} }
reddit_user = user_get_or_create(**user_data) reddit_user = user_get_or_create(**user_data)

View File

@ -46,7 +46,7 @@ class SupervisrOAuthCallback(OAuthCallback):
user_data = { user_data = {
'username': info.get('username'), 'username': info.get('username'),
'email': info.get('email', ''), 'email': info.get('email', ''),
'first_name': info.get('first_name'), 'name': info.get('first_name'),
'password': None, 'password': None,
} }
sv_user = user_get_or_create(**user_data) sv_user = user_get_or_create(**user_data)

View File

@ -38,7 +38,7 @@ class TwitterOAuthCallback(OAuthCallback):
user_data = { user_data = {
'username': info.get('screen_name'), 'username': info.get('screen_name'),
'email': info.get('email', ''), 'email': info.get('email', ''),
'first_name': info.get('name'), 'name': info.get('name'),
'password': None, 'password': None,
} }
tw_user = user_get_or_create(**user_data) tw_user = user_get_or_create(**user_data)

View File

@ -1,2 +1,2 @@
"""passbook oauth_provider Header""" """passbook oauth_provider Header"""
__version__ = '0.0.12-alpha' __version__ = '0.0.13-alpha'

View File

@ -1,2 +1,2 @@
"""passbook otp Header""" """passbook otp Header"""
__version__ = '0.0.12-alpha' __version__ = '0.0.13-alpha'

View File

@ -27,7 +27,7 @@ class GitHubUserView(View):
"received_events_url": "", "received_events_url": "",
"type": "User", "type": "User",
"site_admin": False, "site_admin": False,
"name": "%s %s" % (request.user.first_name, request.user.last_name), "name": request.user.name,
"company": "", "company": "",
"blog": "", "blog": "",
"location": "", "location": "",

View File

@ -1,2 +1,2 @@
"""passbook saml_idp Header""" """passbook saml_idp Header"""
__version__ = '0.0.12-alpha' __version__ = '0.0.13-alpha'

View File

@ -157,7 +157,7 @@ class Processor:
{ {
'FriendlyName': 'cn', 'FriendlyName': 'cn',
'Name': 'urn:oid:2.5.4.3', 'Name': 'urn:oid:2.5.4.3',
'Value': self._django_request.user.first_name, 'Value': self._django_request.user.name,
}, },
{ {
'FriendlyName': 'mail', 'FriendlyName': 'mail',

View File

@ -40,11 +40,11 @@ class SAMLProvider(Provider):
def link_download_metadata(self): def link_download_metadata(self):
"""Get link to download XML metadata for admin interface""" """Get link to download XML metadata for admin interface"""
# pylint: disable=no-member try:
if self.application:
# pylint: disable=no-member # pylint: disable=no-member
return reverse('passbook_saml_idp:metadata_xml', return reverse('passbook_saml_idp:metadata_xml',
kwargs={'application': self.application.slug}) kwargs={'application': self.application.slug})
except Provider.application.RelatedObjectDoesNotExist:
return None return None
class Meta: class Meta: