core: add basic model against which rules can be checked

This commit is contained in:
Jens Langhammer 2018-11-22 13:12:59 +01:00
parent 849f9c9251
commit 095a5c0268
7 changed files with 31 additions and 15 deletions

View file

@ -20,12 +20,24 @@ class User(AbstractUser):
@reversion.register()
class Provider(models.Model):
"""Application-independant Provider instance. For example SAML2 Remote, OAuth2 Application"""
"""Application-independent Provider instance. For example SAML2 Remote, OAuth2 Application"""
# This class defines no field for easier inheritance
class RuleModel(UUIDModel, CreatedUpdatedModel):
"""Base model which can have rules applied to it"""
rules = models.ManyToManyField('Rule')
def passes(self, user: User) -> bool:
"""Return true if user passes, otherwise False or raise Exception"""
for rule in self.rules:
if not rule.passes(user):
return False
return True
@reversion.register()
class Application(UUIDModel, CreatedUpdatedModel):
class Application(RuleModel):
"""Every Application which uses passbook for authentication/identification/authorization
needs an Application record. Other authentication types can subclass this Model to
add custom fields and other properties"""
@ -45,7 +57,7 @@ class Application(UUIDModel, CreatedUpdatedModel):
return self.name
@reversion.register()
class Source(UUIDModel, CreatedUpdatedModel):
class Source(RuleModel):
"""Base Authentication source, i.e. an OAuth Provider, SAML Remote or LDAP Server"""
name = models.TextField()
@ -82,7 +94,6 @@ class Rule(UUIDModel, CreatedUpdatedModel):
)
name = models.TextField(blank=True, null=True)
application = models.ForeignKey(Application, on_delete=models.CASCADE)
action = models.CharField(max_length=20, choices=ACTIONS)
negate = models.BooleanField(default=False)
@ -91,9 +102,9 @@ class Rule(UUIDModel, CreatedUpdatedModel):
def __str__(self):
if self.name:
return self.name
return "%s action %s" % (self.application, self.action)
return "%s action %s" % (self.name, self.action)
def user_passes(self, user: User) -> bool:
def passes(self, user: User) -> bool:
"""Check if user instance passes this rule"""
raise NotImplementedError()
@ -120,13 +131,13 @@ class FieldMatcherRule(Rule):
value = models.TextField()
def __str__(self):
description = "app %s, user.%s %s '%s'" % (self.application, self.user_field,
description = "%s, user.%s %s '%s'" % (self.name, self.user_field,
self.match_action, self.value)
if self.name:
description = "%s: %s" % (self.name, description)
return description
def user_passes(self, user: User) -> bool:
def passes(self, user: User) -> bool:
"""Check if user instance passes this role"""
if not hasattr(user, self.user_field):
raise ValueError("Field does not exist")

View file

@ -1,8 +1,9 @@
django>=2.0
django-reversion
django-model-utils
django-crispy-forms
djangorestframework
PyYAML
raven
djangorestframework
markdown
django-model-utils
colorlog

View file

@ -54,8 +54,10 @@ INSTALLED_APPS = [
'django.contrib.staticfiles',
'reversion',
'rest_framework',
'crispy_forms',
'passbook.core',
'passbook.admin',
'passbook.audit',
'passbook.lib',
'passbook.ldap',
'passbook.oauth_client',

View file

@ -53,8 +53,8 @@
</ul>
</li>
</ul>
<ul class="nav navbar-nav navbar-primary">
{# FIXME: Detect active application #}
{% 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_app 'passbook_core' %}">
<a href="{% url 'passbook_core:overview' %}">{% trans 'Overview' %}</a>
</li>

View file

@ -1,6 +1,7 @@
{% extends "overview/base.html" %}
{% block content %}
<div class="container">
<div class="row row-cards-pf">
<!-- Important: if you need to nest additional .row within a .row.row-cards-pf, do *not* use .row-cards-pf on the nested .row -->
<div class="col-xs-12 col-sm-6 col-md-3">
@ -58,4 +59,5 @@
</div>
</div>
</div><!-- /row -->
</div>
{% endblock %}

View file

@ -26,7 +26,7 @@ urlpatterns = [
for _passbook_app in get_apps():
if hasattr(_passbook_app, 'mountpoint'):
_path = path(_passbook_app.mountpoint, include((_passbook_app.name+'.urls',
_passbook_app.name),
_passbook_app.label),
namespace=_passbook_app.label))
urlpatterns.append(_path)
LOGGER.debug("Loaded %s's URLs", _passbook_app.name)

View file

@ -11,5 +11,5 @@ class OverviewView(LoginRequiredMixin, TemplateView):
template_name = 'overview/index.html'
def get_context_data(self, **kwargs):
kwargs['applications'] = self.request.user.applications.objects.all()
kwargs['applications'] = self.request.user.applications.all()
return super().get_context_data(**kwargs)