core: add basic model against which rules can be checked
This commit is contained in:
parent
849f9c9251
commit
095a5c0268
|
@ -20,12 +20,24 @@ class User(AbstractUser):
|
||||||
|
|
||||||
@reversion.register()
|
@reversion.register()
|
||||||
class Provider(models.Model):
|
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
|
# 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()
|
@reversion.register()
|
||||||
class Application(UUIDModel, CreatedUpdatedModel):
|
class Application(RuleModel):
|
||||||
"""Every Application which uses passbook for authentication/identification/authorization
|
"""Every Application which uses passbook for authentication/identification/authorization
|
||||||
needs an Application record. Other authentication types can subclass this Model to
|
needs an Application record. Other authentication types can subclass this Model to
|
||||||
add custom fields and other properties"""
|
add custom fields and other properties"""
|
||||||
|
@ -45,7 +57,7 @@ class Application(UUIDModel, CreatedUpdatedModel):
|
||||||
return self.name
|
return self.name
|
||||||
|
|
||||||
@reversion.register()
|
@reversion.register()
|
||||||
class Source(UUIDModel, CreatedUpdatedModel):
|
class Source(RuleModel):
|
||||||
"""Base Authentication source, i.e. an OAuth Provider, SAML Remote or LDAP Server"""
|
"""Base Authentication source, i.e. an OAuth Provider, SAML Remote or LDAP Server"""
|
||||||
|
|
||||||
name = models.TextField()
|
name = models.TextField()
|
||||||
|
@ -82,7 +94,6 @@ class Rule(UUIDModel, CreatedUpdatedModel):
|
||||||
)
|
)
|
||||||
|
|
||||||
name = models.TextField(blank=True, null=True)
|
name = models.TextField(blank=True, null=True)
|
||||||
application = models.ForeignKey(Application, on_delete=models.CASCADE)
|
|
||||||
action = models.CharField(max_length=20, choices=ACTIONS)
|
action = models.CharField(max_length=20, choices=ACTIONS)
|
||||||
negate = models.BooleanField(default=False)
|
negate = models.BooleanField(default=False)
|
||||||
|
|
||||||
|
@ -91,9 +102,9 @@ class Rule(UUIDModel, CreatedUpdatedModel):
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
if self.name:
|
if self.name:
|
||||||
return 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"""
|
"""Check if user instance passes this rule"""
|
||||||
raise NotImplementedError()
|
raise NotImplementedError()
|
||||||
|
|
||||||
|
@ -120,13 +131,13 @@ class FieldMatcherRule(Rule):
|
||||||
value = models.TextField()
|
value = models.TextField()
|
||||||
|
|
||||||
def __str__(self):
|
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)
|
self.match_action, self.value)
|
||||||
if self.name:
|
if self.name:
|
||||||
description = "%s: %s" % (self.name, description)
|
description = "%s: %s" % (self.name, description)
|
||||||
return description
|
return description
|
||||||
|
|
||||||
def user_passes(self, user: User) -> bool:
|
def passes(self, user: User) -> bool:
|
||||||
"""Check if user instance passes this role"""
|
"""Check if user instance passes this role"""
|
||||||
if not hasattr(user, self.user_field):
|
if not hasattr(user, self.user_field):
|
||||||
raise ValueError("Field does not exist")
|
raise ValueError("Field does not exist")
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
django>=2.0
|
django>=2.0
|
||||||
django-reversion
|
django-reversion
|
||||||
|
django-model-utils
|
||||||
|
django-crispy-forms
|
||||||
|
djangorestframework
|
||||||
PyYAML
|
PyYAML
|
||||||
raven
|
raven
|
||||||
djangorestframework
|
|
||||||
markdown
|
markdown
|
||||||
django-model-utils
|
|
||||||
colorlog
|
colorlog
|
|
@ -54,8 +54,10 @@ INSTALLED_APPS = [
|
||||||
'django.contrib.staticfiles',
|
'django.contrib.staticfiles',
|
||||||
'reversion',
|
'reversion',
|
||||||
'rest_framework',
|
'rest_framework',
|
||||||
|
'crispy_forms',
|
||||||
'passbook.core',
|
'passbook.core',
|
||||||
'passbook.admin',
|
'passbook.admin',
|
||||||
|
'passbook.audit',
|
||||||
'passbook.lib',
|
'passbook.lib',
|
||||||
'passbook.ldap',
|
'passbook.ldap',
|
||||||
'passbook.oauth_client',
|
'passbook.oauth_client',
|
||||||
|
|
|
@ -53,8 +53,8 @@
|
||||||
</ul>
|
</ul>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<ul class="nav navbar-nav navbar-primary">
|
{% is_active_app 'passbook_admin' as is_admin %}
|
||||||
{# FIXME: Detect active application #}
|
<ul class="nav navbar-nav navbar-primary {% if is_admin == 'active' %}persistent-secondary{% endif %}">
|
||||||
<li class="{% is_active_app 'passbook_core' %}">
|
<li class="{% is_active_app 'passbook_core' %}">
|
||||||
<a href="{% url 'passbook_core:overview' %}">{% trans 'Overview' %}</a>
|
<a href="{% url 'passbook_core:overview' %}">{% trans 'Overview' %}</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
{% extends "overview/base.html" %}
|
{% extends "overview/base.html" %}
|
||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
<div class="container">
|
||||||
<div class="row row-cards-pf">
|
<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 -->
|
<!-- 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">
|
<div class="col-xs-12 col-sm-6 col-md-3">
|
||||||
|
@ -58,4 +59,5 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div><!-- /row -->
|
</div><!-- /row -->
|
||||||
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
|
@ -26,7 +26,7 @@ urlpatterns = [
|
||||||
for _passbook_app in get_apps():
|
for _passbook_app in get_apps():
|
||||||
if hasattr(_passbook_app, 'mountpoint'):
|
if hasattr(_passbook_app, 'mountpoint'):
|
||||||
_path = path(_passbook_app.mountpoint, include((_passbook_app.name+'.urls',
|
_path = path(_passbook_app.mountpoint, include((_passbook_app.name+'.urls',
|
||||||
_passbook_app.name),
|
_passbook_app.label),
|
||||||
namespace=_passbook_app.label))
|
namespace=_passbook_app.label))
|
||||||
urlpatterns.append(_path)
|
urlpatterns.append(_path)
|
||||||
LOGGER.debug("Loaded %s's URLs", _passbook_app.name)
|
LOGGER.debug("Loaded %s's URLs", _passbook_app.name)
|
||||||
|
|
|
@ -11,5 +11,5 @@ class OverviewView(LoginRequiredMixin, TemplateView):
|
||||||
template_name = 'overview/index.html'
|
template_name = 'overview/index.html'
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
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)
|
return super().get_context_data(**kwargs)
|
||||||
|
|
Reference in a new issue