core: add basic model against which rules can be checked
This commit is contained in:
		@ -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,
 | 
			
		||||
                                                   self.match_action, self.value)
 | 
			
		||||
        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")
 | 
			
		||||
 | 
			
		||||
@ -1,8 +1,9 @@
 | 
			
		||||
django>=2.0
 | 
			
		||||
django-reversion
 | 
			
		||||
django-model-utils
 | 
			
		||||
django-crispy-forms
 | 
			
		||||
djangorestframework
 | 
			
		||||
PyYAML
 | 
			
		||||
raven
 | 
			
		||||
djangorestframework
 | 
			
		||||
markdown
 | 
			
		||||
django-model-utils
 | 
			
		||||
colorlog
 | 
			
		||||
@ -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',
 | 
			
		||||
 | 
			
		||||
@ -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>
 | 
			
		||||
 | 
			
		||||
@ -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 %}
 | 
			
		||||
@ -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)
 | 
			
		||||
 | 
			
		||||
@ -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)
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user