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

View File

@ -2,6 +2,7 @@
from django import forms from django import forms
from django.contrib.admin.widgets import FilteredSelectMultiple from django.contrib.admin.widgets import FilteredSelectMultiple
from passbook.admin.fields import CodeMirrorWidget, YAMLField
from passbook.core.models import Group, User from passbook.core.models import Group, User
@ -34,4 +35,8 @@ class GroupForm(forms.ModelForm):
fields = ["name", "parent", "members", "attributes"] fields = ["name", "parent", "members", "attributes"]
widgets = { widgets = {
"name": forms.TextInput(), "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.serializers import ModelSerializer
from rest_framework.viewsets import ModelViewSet from rest_framework.viewsets import ModelViewSet
@ -22,7 +22,7 @@ class PasswordPolicySerializer(ModelSerializer):
class PasswordPolicyViewSet(ModelViewSet): class PasswordPolicyViewSet(ModelViewSet):
"""Source Viewset""" """Password Policy Viewset"""
queryset = PasswordPolicy.objects.all() queryset = PasswordPolicy.objects.all()
serializer_class = PasswordPolicySerializer serializer_class = PasswordPolicySerializer

View File

@ -86,6 +86,7 @@ INSTALLED_APPS = [
"passbook.policies.expression.apps.PassbookPolicyExpressionConfig", "passbook.policies.expression.apps.PassbookPolicyExpressionConfig",
"passbook.policies.hibp.apps.PassbookPolicyHIBPConfig", "passbook.policies.hibp.apps.PassbookPolicyHIBPConfig",
"passbook.policies.password.apps.PassbookPoliciesPasswordConfig", "passbook.policies.password.apps.PassbookPoliciesPasswordConfig",
"passbook.policies.group_membership.apps.PassbookPoliciesGroupMembershipConfig",
"passbook.policies.reputation.apps.PassbookPolicyReputationConfig", "passbook.policies.reputation.apps.PassbookPolicyReputationConfig",
"passbook.providers.app_gw.apps.PassbookApplicationApplicationGatewayConfig", "passbook.providers.app_gw.apps.PassbookApplicationApplicationGatewayConfig",
"passbook.providers.oauth.apps.PassbookProviderOAuthConfig", "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 required: true
type: string type: string
format: uuid 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/: /policies/haveibeenpwned/:
get: get:
operationId: policies_haveibeenpwned_list operationId: policies_haveibeenpwned_list
@ -1352,7 +1479,7 @@ paths:
/policies/password/: /policies/password/:
get: get:
operationId: policies_password_list operationId: policies_password_list
description: Source Viewset description: Password Policy Viewset
parameters: parameters:
- name: ordering - name: ordering
in: query in: query
@ -1401,7 +1528,7 @@ paths:
- policies - policies
post: post:
operationId: policies_password_create operationId: policies_password_create
description: Source Viewset description: Password Policy Viewset
parameters: parameters:
- name: data - name: data
in: body in: body
@ -1419,7 +1546,7 @@ paths:
/policies/password/{policy_uuid}/: /policies/password/{policy_uuid}/:
get: get:
operationId: policies_password_read operationId: policies_password_read
description: Source Viewset description: Password Policy Viewset
parameters: [] parameters: []
responses: responses:
'200': '200':
@ -1430,7 +1557,7 @@ paths:
- policies - policies
put: put:
operationId: policies_password_update operationId: policies_password_update
description: Source Viewset description: Password Policy Viewset
parameters: parameters:
- name: data - name: data
in: body in: body
@ -1446,7 +1573,7 @@ paths:
- policies - policies
patch: patch:
operationId: policies_password_partial_update operationId: policies_password_partial_update
description: Source Viewset description: Password Policy Viewset
parameters: parameters:
- name: data - name: data
in: body in: body
@ -1462,7 +1589,7 @@ paths:
- policies - policies
delete: delete:
operationId: policies_password_delete operationId: policies_password_delete
description: Source Viewset description: Password Policy Viewset
parameters: [] parameters: []
responses: responses:
'204': '204':
@ -1476,9 +1603,9 @@ paths:
required: true required: true
type: string type: string
format: uuid format: uuid
/policies/passwordexpiry/: /policies/password_expiry/:
get: get:
operationId: policies_passwordexpiry_list operationId: policies_password_expiry_list
description: Password Expiry Viewset description: Password Expiry Viewset
parameters: parameters:
- name: ordering - name: ordering
@ -1527,7 +1654,7 @@ paths:
tags: tags:
- policies - policies
post: post:
operationId: policies_passwordexpiry_create operationId: policies_password_expiry_create
description: Password Expiry Viewset description: Password Expiry Viewset
parameters: parameters:
- name: data - name: data
@ -1543,9 +1670,9 @@ paths:
tags: tags:
- policies - policies
parameters: [] parameters: []
/policies/passwordexpiry/{policy_uuid}/: /policies/password_expiry/{policy_uuid}/:
get: get:
operationId: policies_passwordexpiry_read operationId: policies_password_expiry_read
description: Password Expiry Viewset description: Password Expiry Viewset
parameters: [] parameters: []
responses: responses:
@ -1556,7 +1683,7 @@ paths:
tags: tags:
- policies - policies
put: put:
operationId: policies_passwordexpiry_update operationId: policies_password_expiry_update
description: Password Expiry Viewset description: Password Expiry Viewset
parameters: parameters:
- name: data - name: data
@ -1572,7 +1699,7 @@ paths:
tags: tags:
- policies - policies
patch: patch:
operationId: policies_passwordexpiry_partial_update operationId: policies_password_expiry_partial_update
description: Password Expiry Viewset description: Password Expiry Viewset
parameters: parameters:
- name: data - name: data
@ -1588,7 +1715,7 @@ paths:
tags: tags:
- policies - policies
delete: delete:
operationId: policies_passwordexpiry_delete operationId: policies_password_expiry_delete
description: Password Expiry Viewset description: Password Expiry Viewset
parameters: [] parameters: []
responses: responses:
@ -5661,6 +5788,23 @@ definitions:
title: Expression title: Expression
type: string type: string
minLength: 1 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: HaveIBeenPwendPolicy:
type: object type: object
properties: properties: