root: add group_membership policy

This commit is contained in:
Jens Langhammer 2020-07-01 21:18:05 +02:00
parent 3478a2cf6d
commit 6634cc2edf
15 changed files with 348 additions and 17 deletions

View file

@ -21,6 +21,7 @@ from passbook.policies.api import PolicyBindingViewSet, PolicyViewSet
from passbook.policies.dummy.api import DummyPolicyViewSet
from passbook.policies.expiry.api import PasswordExpiryPolicyViewSet
from passbook.policies.expression.api import ExpressionPolicyViewSet
from passbook.policies.group_membership.api import GroupMembershipPolicyViewSet
from passbook.policies.hibp.api import HaveIBeenPwendPolicyViewSet
from passbook.policies.password.api import PasswordPolicyViewSet
from passbook.policies.reputation.api import ReputationPolicyViewSet
@ -71,9 +72,10 @@ router.register("sources/oauth", OAuthSourceViewSet)
router.register("policies/all", PolicyViewSet)
router.register("policies/bindings", PolicyBindingViewSet)
router.register("policies/expression", ExpressionPolicyViewSet)
router.register("policies/group_membership", GroupMembershipPolicyViewSet)
router.register("policies/haveibeenpwned", HaveIBeenPwendPolicyViewSet)
router.register("policies/password_expiry", PasswordExpiryPolicyViewSet)
router.register("policies/password", PasswordPolicyViewSet)
router.register("policies/passwordexpiry", PasswordExpiryPolicyViewSet)
router.register("policies/reputation", ReputationPolicyViewSet)
router.register("providers/all", ProviderViewSet)

View file

@ -2,6 +2,7 @@
from django import forms
from django.contrib.admin.widgets import FilteredSelectMultiple
from passbook.admin.fields import CodeMirrorWidget, YAMLField
from passbook.core.models import Group, User
@ -34,4 +35,8 @@ class GroupForm(forms.ModelForm):
fields = ["name", "parent", "members", "attributes"]
widgets = {
"name": forms.TextInput(),
"attributes": CodeMirrorWidget,
}
field_classes = {
"attributes": YAMLField,
}

View file

@ -0,0 +1,23 @@
"""Group Membership Policy API"""
from rest_framework.serializers import ModelSerializer
from rest_framework.viewsets import ModelViewSet
from passbook.policies.forms import GENERAL_SERIALIZER_FIELDS
from passbook.policies.group_membership.models import GroupMembershipPolicy
class GroupMembershipPolicySerializer(ModelSerializer):
"""Group Membership Policy Serializer"""
class Meta:
model = GroupMembershipPolicy
fields = GENERAL_SERIALIZER_FIELDS + [
"group",
]
class GroupMembershipPolicyViewSet(ModelViewSet):
"""Group Membership Policy Viewset"""
queryset = GroupMembershipPolicy.objects.all()
serializer_class = GroupMembershipPolicySerializer

View file

@ -0,0 +1,11 @@
"""passbook Group Membership policy app config"""
from django.apps import AppConfig
class PassbookPoliciesGroupMembershipConfig(AppConfig):
"""passbook Group Membership policy app config"""
name = "passbook.policies.group_membership"
label = "passbook_policies_group_membership"
verbose_name = "passbook Policies.Group Membership"

View file

@ -0,0 +1,20 @@
"""passbook Group Membership Policy forms"""
from django import forms
from passbook.policies.forms import GENERAL_FIELDS
from passbook.policies.group_membership.models import GroupMembershipPolicy
class GroupMembershipPolicyForm(forms.ModelForm):
"""GroupMembershipPolicy Form"""
class Meta:
model = GroupMembershipPolicy
fields = GENERAL_FIELDS + [
"group",
]
widgets = {
"name": forms.TextInput(),
}

View file

@ -0,0 +1,47 @@
# Generated by Django 3.0.7 on 2020-07-01 19:01
import django.db.models.deletion
from django.db import migrations, models
class Migration(migrations.Migration):
initial = True
dependencies = [
("passbook_policies", "0002_auto_20200528_1647"),
("passbook_core", "0003_default_user"),
]
operations = [
migrations.CreateModel(
name="GroupMembershipPolicy",
fields=[
(
"policy_ptr",
models.OneToOneField(
auto_created=True,
on_delete=django.db.models.deletion.CASCADE,
parent_link=True,
primary_key=True,
serialize=False,
to="passbook_policies.Policy",
),
),
(
"group",
models.ForeignKey(
blank=True,
null=True,
on_delete=django.db.models.deletion.SET_NULL,
to="passbook_core.Group",
),
),
],
options={
"verbose_name": "Group Membership Policy",
"verbose_name_plural": "Group Membership Policies",
},
bases=("passbook_policies.policy",),
),
]

View file

@ -0,0 +1,23 @@
"""user field matcher models"""
from django.db import models
from django.utils.translation import gettext as _
from passbook.core.models import Group
from passbook.policies.models import Policy
from passbook.policies.types import PolicyRequest, PolicyResult
class GroupMembershipPolicy(Policy):
"""Check that the user is member of the selected group."""
group = models.ForeignKey(Group, null=True, blank=True, on_delete=models.SET_NULL)
form = "passbook.policies.group_membership.forms.GroupMembershipPolicyForm"
def passes(self, request: PolicyRequest) -> PolicyResult:
return PolicyResult(self.group.user_set.filter(pk=request.user.pk).exists())
class Meta:
verbose_name = _("Group Membership Policy")
verbose_name_plural = _("Group Membership Policies")

View file

@ -0,0 +1,32 @@
"""evaluator tests"""
from django.test import TestCase
from guardian.shortcuts import get_anonymous_user
from passbook.core.models import Group
from passbook.policies.group_membership.models import GroupMembershipPolicy
from passbook.policies.types import PolicyRequest
class TestGroupMembershipPolicy(TestCase):
"""GroupMembershipPolicy tests"""
def setUp(self):
self.request = PolicyRequest(user=get_anonymous_user())
def test_invalid(self):
"""user not in group"""
group = Group.objects.create(name="test")
policy: GroupMembershipPolicy = GroupMembershipPolicy.objects.create(
group=group
)
self.assertFalse(policy.passes(self.request).passing)
def test_valid(self):
"""user in group"""
group = Group.objects.create(name="test")
group.user_set.add(get_anonymous_user())
group.save()
policy: GroupMembershipPolicy = GroupMembershipPolicy.objects.create(
group=group
)
self.assertTrue(policy.passes(self.request).passing)

View file

@ -1,4 +1,4 @@
"""Source API Views"""
"""Password Policy API Views"""
from rest_framework.serializers import ModelSerializer
from rest_framework.viewsets import ModelViewSet
@ -22,7 +22,7 @@ class PasswordPolicySerializer(ModelSerializer):
class PasswordPolicyViewSet(ModelViewSet):
"""Source Viewset"""
"""Password Policy Viewset"""
queryset = PasswordPolicy.objects.all()
serializer_class = PasswordPolicySerializer

View file

@ -86,6 +86,7 @@ INSTALLED_APPS = [
"passbook.policies.expression.apps.PassbookPolicyExpressionConfig",
"passbook.policies.hibp.apps.PassbookPolicyHIBPConfig",
"passbook.policies.password.apps.PassbookPoliciesPasswordConfig",
"passbook.policies.group_membership.apps.PassbookPoliciesGroupMembershipConfig",
"passbook.policies.reputation.apps.PassbookPolicyReputationConfig",
"passbook.providers.app_gw.apps.PassbookApplicationApplicationGatewayConfig",
"passbook.providers.oauth.apps.PassbookProviderOAuthConfig",

View file

@ -0,0 +1,23 @@
# Generated by Django 3.0.7 on 2020-07-01 19:00
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
("passbook_stages_otp_time", "0001_initial"),
]
operations = [
migrations.AlterField(
model_name="otptimestage",
name="digits",
field=models.IntegerField(
choices=[
(6, "6 digits, widely compatible"),
(8, "8 digits, not compatible with apps like Google Authenticator"),
]
),
),
]

View file

@ -1222,6 +1222,133 @@ paths:
required: true
type: string
format: uuid
/policies/group_membership/:
get:
operationId: policies_group_membership_list
description: Group Membership Policy Viewset
parameters:
- name: ordering
in: query
description: Which field to use when ordering the results.
required: false
type: string
- name: search
in: query
description: A search term.
required: false
type: string
- name: limit
in: query
description: Number of results to return per page.
required: false
type: integer
- name: offset
in: query
description: The initial index from which to return the results.
required: false
type: integer
responses:
'200':
description: ''
schema:
required:
- count
- results
type: object
properties:
count:
type: integer
next:
type: string
format: uri
x-nullable: true
previous:
type: string
format: uri
x-nullable: true
results:
type: array
items:
$ref: '#/definitions/GroupMembershipPolicy'
tags:
- policies
post:
operationId: policies_group_membership_create
description: Group Membership Policy Viewset
parameters:
- name: data
in: body
required: true
schema:
$ref: '#/definitions/GroupMembershipPolicy'
responses:
'201':
description: ''
schema:
$ref: '#/definitions/GroupMembershipPolicy'
tags:
- policies
parameters: []
/policies/group_membership/{policy_uuid}/:
get:
operationId: policies_group_membership_read
description: Group Membership Policy Viewset
parameters: []
responses:
'200':
description: ''
schema:
$ref: '#/definitions/GroupMembershipPolicy'
tags:
- policies
put:
operationId: policies_group_membership_update
description: Group Membership Policy Viewset
parameters:
- name: data
in: body
required: true
schema:
$ref: '#/definitions/GroupMembershipPolicy'
responses:
'200':
description: ''
schema:
$ref: '#/definitions/GroupMembershipPolicy'
tags:
- policies
patch:
operationId: policies_group_membership_partial_update
description: Group Membership Policy Viewset
parameters:
- name: data
in: body
required: true
schema:
$ref: '#/definitions/GroupMembershipPolicy'
responses:
'200':
description: ''
schema:
$ref: '#/definitions/GroupMembershipPolicy'
tags:
- policies
delete:
operationId: policies_group_membership_delete
description: Group Membership Policy Viewset
parameters: []
responses:
'204':
description: ''
tags:
- policies
parameters:
- name: policy_uuid
in: path
description: A UUID string identifying this Group Membership Policy.
required: true
type: string
format: uuid
/policies/haveibeenpwned/:
get:
operationId: policies_haveibeenpwned_list
@ -1352,7 +1479,7 @@ paths:
/policies/password/:
get:
operationId: policies_password_list
description: Source Viewset
description: Password Policy Viewset
parameters:
- name: ordering
in: query
@ -1401,7 +1528,7 @@ paths:
- policies
post:
operationId: policies_password_create
description: Source Viewset
description: Password Policy Viewset
parameters:
- name: data
in: body
@ -1419,7 +1546,7 @@ paths:
/policies/password/{policy_uuid}/:
get:
operationId: policies_password_read
description: Source Viewset
description: Password Policy Viewset
parameters: []
responses:
'200':
@ -1430,7 +1557,7 @@ paths:
- policies
put:
operationId: policies_password_update
description: Source Viewset
description: Password Policy Viewset
parameters:
- name: data
in: body
@ -1446,7 +1573,7 @@ paths:
- policies
patch:
operationId: policies_password_partial_update
description: Source Viewset
description: Password Policy Viewset
parameters:
- name: data
in: body
@ -1462,7 +1589,7 @@ paths:
- policies
delete:
operationId: policies_password_delete
description: Source Viewset
description: Password Policy Viewset
parameters: []
responses:
'204':
@ -1476,9 +1603,9 @@ paths:
required: true
type: string
format: uuid
/policies/passwordexpiry/:
/policies/password_expiry/:
get:
operationId: policies_passwordexpiry_list
operationId: policies_password_expiry_list
description: Password Expiry Viewset
parameters:
- name: ordering
@ -1527,7 +1654,7 @@ paths:
tags:
- policies
post:
operationId: policies_passwordexpiry_create
operationId: policies_password_expiry_create
description: Password Expiry Viewset
parameters:
- name: data
@ -1543,9 +1670,9 @@ paths:
tags:
- policies
parameters: []
/policies/passwordexpiry/{policy_uuid}/:
/policies/password_expiry/{policy_uuid}/:
get:
operationId: policies_passwordexpiry_read
operationId: policies_password_expiry_read
description: Password Expiry Viewset
parameters: []
responses:
@ -1556,7 +1683,7 @@ paths:
tags:
- policies
put:
operationId: policies_passwordexpiry_update
operationId: policies_password_expiry_update
description: Password Expiry Viewset
parameters:
- name: data
@ -1572,7 +1699,7 @@ paths:
tags:
- policies
patch:
operationId: policies_passwordexpiry_partial_update
operationId: policies_password_expiry_partial_update
description: Password Expiry Viewset
parameters:
- name: data
@ -1588,7 +1715,7 @@ paths:
tags:
- policies
delete:
operationId: policies_passwordexpiry_delete
operationId: policies_password_expiry_delete
description: Password Expiry Viewset
parameters: []
responses:
@ -5661,6 +5788,23 @@ definitions:
title: Expression
type: string
minLength: 1
GroupMembershipPolicy:
type: object
properties:
pk:
title: Policy uuid
type: string
format: uuid
readOnly: true
name:
title: Name
type: string
x-nullable: true
group:
title: Group
type: string
format: uuid
x-nullable: true
HaveIBeenPwendPolicy:
type: object
properties: