diff --git a/abac/migrations/0001_initial.py b/abac/migrations/0001_initial.py index 9851920..fd44c8d 100644 --- a/abac/migrations/0001_initial.py +++ b/abac/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 4.2.5 on 2023-09-28 12:45 +# Generated by Django 4.2.5 on 2023-09-28 16:35 from django.conf import settings import django.contrib.auth.models @@ -72,6 +72,9 @@ class Migration(migrations.Migration): fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('name', models.CharField(max_length=255)), + ('read', models.BooleanField(default=False)), + ('write', models.BooleanField(default=False)), + ('delete', models.BooleanField(default=False)), ('file', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='abac.file')), ], ), @@ -80,7 +83,6 @@ class Migration(migrations.Migration): fields=[ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('value', models.IntegerField()), - ('last_modified', models.DateTimeField(auto_now=True)), ('attribute_type', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='abac.attributetype')), ], ), @@ -88,6 +90,7 @@ class Migration(migrations.Migration): name='UserAttribute', fields=[ ('attribute_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='abac.attribute')), + ('last_modified', models.DateTimeField(auto_now=True)), ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), ], bases=('abac.attribute',), @@ -96,6 +99,7 @@ class Migration(migrations.Migration): name='RuleAttribute', fields=[ ('attribute_ptr', models.OneToOneField(auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, to='abac.attribute')), + ('operator', models.CharField(choices=[('EQ', 'Equals'), ('NEQ', "Doesn't Equal"), ('GT', 'Greater Than'), ('LT', 'Less Than')], default='EQ', max_length=3)), ('rule', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='abac.rule')), ], bases=('abac.attribute',), diff --git a/abac/migrations/0002_remove_attribute_last_modified_rule_delete_rule_read_and_more.py b/abac/migrations/0002_remove_attribute_last_modified_rule_delete_rule_read_and_more.py deleted file mode 100644 index b9c31fa..0000000 --- a/abac/migrations/0002_remove_attribute_last_modified_rule_delete_rule_read_and_more.py +++ /dev/null @@ -1,42 +0,0 @@ -# Generated by Django 4.2.5 on 2023-09-28 13:51 - -from django.db import migrations, models - - -class Migration(migrations.Migration): - - dependencies = [ - ('abac', '0001_initial'), - ] - - operations = [ - migrations.RemoveField( - model_name='attribute', - name='last_modified', - ), - migrations.AddField( - model_name='rule', - name='delete', - field=models.BooleanField(default=False), - ), - migrations.AddField( - model_name='rule', - name='read', - field=models.BooleanField(default=False), - ), - migrations.AddField( - model_name='rule', - name='write', - field=models.BooleanField(default=False), - ), - migrations.AddField( - model_name='ruleattribute', - name='option', - field=models.CharField(choices=[('EQ', 'Equals'), ('NEQ', "Doesn't Equal"), ('GT', 'Greater Than'), ('LT', 'Less Than')], default='EQ', max_length=3), - ), - migrations.AddField( - model_name='userattribute', - name='last_modified', - field=models.DateTimeField(auto_now=True), - ), - ] diff --git a/abac/migrations/0003_rename_option_ruleattribute_operator.py b/abac/migrations/0003_rename_option_ruleattribute_operator.py deleted file mode 100644 index c2b10c3..0000000 --- a/abac/migrations/0003_rename_option_ruleattribute_operator.py +++ /dev/null @@ -1,18 +0,0 @@ -# Generated by Django 4.2.5 on 2023-09-28 14:49 - -from django.db import migrations - - -class Migration(migrations.Migration): - - dependencies = [ - ('abac', '0002_remove_attribute_last_modified_rule_delete_rule_read_and_more'), - ] - - operations = [ - migrations.RenameField( - model_name='ruleattribute', - old_name='option', - new_name='operator', - ), - ] diff --git a/abac/models.py b/abac/models.py index 44c48d9..f4ae7e9 100644 --- a/abac/models.py +++ b/abac/models.py @@ -86,6 +86,34 @@ class Rule(models.Model): write = models.BooleanField(default=False) delete = models.BooleanField(default=False) + def is_satisfied_by(self, user): + for rule_attribute in self.ruleattribute_set.all(): + try: + user_attribute = UserAttribute.objects.get( + user=user, + attribute_type=rule_attribute.attribute_type, + ) + except UserAttribute.DoesNotExist: + # The user does not have this attribute; the rule is not satisfied. + return False + + if not self.attribute_satisfies_rule(user_attribute, rule_attribute): + return False + + return True + + def attribute_satisfies_rule(self, user_attribute, rule_attribute): + operator = rule_attribute.operator + if operator == 'EQ': + return user_attribute.value == rule_attribute.value + elif operator == 'NEQ': + return not (user_attribute.value == rule_attribute.value) + elif operator == 'GT': + return user_attribute.value > rule_attribute.value + elif operator == 'LT': + return user_attribute.value < rule_attribute.value + return False + class RuleAttribute(Attribute): OPTION_EQUALS = 'EQ' OPTION_DOES_NOT_EQUAL = 'NEQ' diff --git a/abac/templates/base.html b/abac/templates/base.html index 67bef41..6948598 100644 --- a/abac/templates/base.html +++ b/abac/templates/base.html @@ -29,8 +29,8 @@ {% if user.is_authenticated %}