Commit f235e201 authored by BaiJiangJie's avatar BaiJiangJie Committed by 老广

[Feature] 授权规则添加 actions 选项,控制用户对资产的操作行为 (#2610)

* [Feature] 1. perms actions - 添加 Action Model

* [Feature] 2. perms actions - 添加 Action API

* [Feature] 3. perms actions - 授权规则: 添加actions字段

* [Feature] 4. perms actions - 授权规则创建页面: 设置 actions 默认 all

* [Feature] 5. perms actions - 资产授权工具: 动态给system_user设置actions属性; 修改授权相关的API-serializer类: 添加actions字段值

* [Feature] 6. perms actions - 更新API(用户使用系统用户连接资产时权限校验): 添加actions校验

* [Feature] 7. perms actions - 迁移文件中为已经存在的perms添加默认的action

* [Feature] 8. perms actions - 创建授权规则时设置默认action(如果actions字段值为空)

* [Feature] 9. check actions - 修改校验用户资产权限API逻辑(添加actions校验)

* [Feature] 10. check actions - 修改注释

* [Feature] 11. check actions - 添加API: 获取用户指定资产和系统用户被授权的actions

* [Feature] 12. check actions - 添加翻译信息
parent fc1068a9
...@@ -61,13 +61,19 @@ class AssetSystemUserSerializer(serializers.ModelSerializer): ...@@ -61,13 +61,19 @@ class AssetSystemUserSerializer(serializers.ModelSerializer):
""" """
查看授权的资产系统用户的数据结构,这个和AssetSerializer不同,字段少 查看授权的资产系统用户的数据结构,这个和AssetSerializer不同,字段少
""" """
actions = serializers.SerializerMethodField()
class Meta: class Meta:
model = SystemUser model = SystemUser
fields = ( fields = (
'id', 'name', 'username', 'priority', 'id', 'name', 'username', 'priority',
'protocol', 'comment', 'login_mode' 'protocol', 'comment', 'login_mode', 'actions',
) )
@staticmethod
def get_actions(obj):
return [action.name for action in obj.actions]
class SystemUserSimpleSerializer(serializers.ModelSerializer): class SystemUserSimpleSerializer(serializers.ModelSerializer):
""" """
......
This diff is collapsed.
...@@ -10,7 +10,7 @@ from rest_framework.pagination import LimitOffsetPagination ...@@ -10,7 +10,7 @@ from rest_framework.pagination import LimitOffsetPagination
from common.permissions import IsOrgAdmin from common.permissions import IsOrgAdmin
from common.utils import get_object_or_none from common.utils import get_object_or_none
from ..models import AssetPermission from ..models import AssetPermission, Action
from ..hands import ( from ..hands import (
User, UserGroup, Asset, Node, SystemUser, User, UserGroup, Asset, Node, SystemUser,
) )
...@@ -20,10 +20,16 @@ from .. import serializers ...@@ -20,10 +20,16 @@ from .. import serializers
__all__ = [ __all__ = [
'AssetPermissionViewSet', 'AssetPermissionRemoveUserApi', 'AssetPermissionViewSet', 'AssetPermissionRemoveUserApi',
'AssetPermissionAddUserApi', 'AssetPermissionRemoveAssetApi', 'AssetPermissionAddUserApi', 'AssetPermissionRemoveAssetApi',
'AssetPermissionAddAssetApi', 'AssetPermissionAddAssetApi', 'ActionViewSet',
] ]
class ActionViewSet(viewsets.ReadOnlyModelViewSet):
queryset = Action.objects.all()
serializer_class = serializers.ActionSerializer
permission_classes = (IsOrgAdmin,)
class AssetPermissionViewSet(viewsets.ModelViewSet): class AssetPermissionViewSet(viewsets.ModelViewSet):
""" """
资产授权列表的增删改查api 资产授权列表的增删改查api
......
...@@ -16,7 +16,8 @@ from common.tree import TreeNodeSerializer ...@@ -16,7 +16,8 @@ from common.tree import TreeNodeSerializer
from common.utils import get_logger from common.utils import get_logger
from orgs.utils import set_to_root_org from orgs.utils import set_to_root_org
from ..utils import ( from ..utils import (
AssetPermissionUtil, parse_asset_to_tree_node, parse_node_to_tree_node AssetPermissionUtil, parse_asset_to_tree_node, parse_node_to_tree_node,
check_system_user_action
) )
from ..hands import ( from ..hands import (
AssetGrantedSerializer, User, Asset, Node, AssetGrantedSerializer, User, Asset, Node,
...@@ -24,6 +25,7 @@ from ..hands import ( ...@@ -24,6 +25,7 @@ from ..hands import (
) )
from .. import serializers from .. import serializers
from ..mixins import AssetsFilterMixin from ..mixins import AssetsFilterMixin
from ..models import Action
logger = get_logger(__name__) logger = get_logger(__name__)
...@@ -31,7 +33,7 @@ __all__ = [ ...@@ -31,7 +33,7 @@ __all__ = [
'UserGrantedAssetsApi', 'UserGrantedNodesApi', 'UserGrantedAssetsApi', 'UserGrantedNodesApi',
'UserGrantedNodesWithAssetsApi', 'UserGrantedNodeAssetsApi', 'UserGrantedNodesWithAssetsApi', 'UserGrantedNodeAssetsApi',
'ValidateUserAssetPermissionApi', 'UserGrantedNodeChildrenApi', 'ValidateUserAssetPermissionApi', 'UserGrantedNodeChildrenApi',
'UserGrantedNodesWithAssetsAsTreeApi', 'UserGrantedNodesWithAssetsAsTreeApi', 'GetUserAssetPermissionActionsApi',
] ]
...@@ -403,16 +405,45 @@ class ValidateUserAssetPermissionApi(UserPermissionCacheMixin, APIView): ...@@ -403,16 +405,45 @@ class ValidateUserAssetPermissionApi(UserPermissionCacheMixin, APIView):
user_id = request.query_params.get('user_id', '') user_id = request.query_params.get('user_id', '')
asset_id = request.query_params.get('asset_id', '') asset_id = request.query_params.get('asset_id', '')
system_id = request.query_params.get('system_user_id', '') system_id = request.query_params.get('system_user_id', '')
action_name = request.query_params.get('action_name', '')
user = get_object_or_404(User, id=user_id) user = get_object_or_404(User, id=user_id)
asset = get_object_or_404(Asset, id=asset_id) asset = get_object_or_404(Asset, id=asset_id)
system_user = get_object_or_404(SystemUser, id=system_id) su = get_object_or_404(SystemUser, id=system_id)
action = get_object_or_404(Action, name=action_name)
util = AssetPermissionUtil(user, cache_policy=self.cache_policy) util = AssetPermissionUtil(user, cache_policy=self.cache_policy)
assets_granted = util.get_assets() granted_assets = util.get_assets()
if system_user in assets_granted.get(asset, []): granted_system_users = granted_assets.get(asset, [])
return Response({'msg': True}, status=200)
else: if su not in granted_system_users:
return Response({'msg': False}, status=403)
_su = next((s for s in granted_system_users if s.id == su.id), None)
if not check_system_user_action(_su, action):
return Response({'msg': False}, status=403) return Response({'msg': False}, status=403)
return Response({'msg': True}, status=200)
class GetUserAssetPermissionActionsApi(UserPermissionCacheMixin, APIView):
permission_classes = (IsOrgAdminOrAppUser,)
def get(self, request, *args, **kwargs):
user_id = request.query_params.get('user_id', '')
asset_id = request.query_params.get('asset_id', '')
system_id = request.query_params.get('system_user_id', '')
user = get_object_or_404(User, id=user_id)
asset = get_object_or_404(Asset, id=asset_id)
su = get_object_or_404(SystemUser, id=system_id)
util = AssetPermissionUtil(user, cache_policy=self.cache_policy)
granted_assets = util.get_assets()
granted_system_users = granted_assets.get(asset, [])
_su = next((s for s in granted_system_users if s.id == su.id), None)
if not _su:
return Response({'actions': []}, status=403)
actions = [action.name for action in getattr(_su, 'actions', [])]
return Response({'actions': actions}, status=200)
# -*- coding: utf-8 -*-
#
from django.utils.translation import ugettext_lazy as _
__all__ = [
'PERMS_ACTION_NAME_ALL', 'PERMS_ACTION_NAME_CONNECT',
'PERMS_ACTION_NAME_DOWNLOAD_FILE', 'PERMS_ACTION_NAME_UPLOAD_FILE',
'PERMS_ACTION_NAME_CHOICES'
]
PERMS_ACTION_NAME_ALL = 'all'
PERMS_ACTION_NAME_CONNECT = 'connect'
PERMS_ACTION_NAME_UPLOAD_FILE = 'upload_file'
PERMS_ACTION_NAME_DOWNLOAD_FILE = 'download_file'
PERMS_ACTION_NAME_CHOICES = (
(PERMS_ACTION_NAME_ALL, _('All')),
(PERMS_ACTION_NAME_CONNECT, _('Connect')),
(PERMS_ACTION_NAME_UPLOAD_FILE, _('Upload file')),
(PERMS_ACTION_NAME_DOWNLOAD_FILE, _('Download file')),
)
...@@ -47,10 +47,17 @@ class AssetPermissionForm(OrgModelForm): ...@@ -47,10 +47,17 @@ class AssetPermissionForm(OrgModelForm):
'system_users': forms.SelectMultiple( 'system_users': forms.SelectMultiple(
attrs={'class': 'select2', 'data-placeholder': _('System user')} attrs={'class': 'select2', 'data-placeholder': _('System user')}
), ),
'actions': forms.SelectMultiple(
attrs={'class': 'select2', 'data-placeholder': _('Action')}
)
} }
labels = { labels = {
'nodes': _("Node"), 'nodes': _("Node"),
} }
help_texts = {
'actions': _('Tips: The RDP protocol does not support separate '
'controls for uploading or downloading files')
}
def clean_user_groups(self): def clean_user_groups(self):
users = self.cleaned_data.get('users') users = self.cleaned_data.get('users')
......
# Generated by Django 2.1.7 on 2019-04-12 07:00
from django.db import migrations, models
import uuid
def add_default_actions(apps, schema_editor):
from ..const import PERMS_ACTION_NAME_CHOICES
action_model = apps.get_model('perms', 'Action')
db_alias = schema_editor.connection.alias
for action, _ in PERMS_ACTION_NAME_CHOICES:
action_model.objects.using(db_alias).update_or_create(name=action)
class Migration(migrations.Migration):
dependencies = [
('perms', '0002_auto_20171228_0025_squashed_0009_auto_20180903_1132'),
]
operations = [
migrations.CreateModel(
name='Action',
fields=[
('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
('name', models.CharField(choices=[('all', 'All'), ('connect', 'Connect'), ('upload_file', 'Upload file'), ('download_file', 'Download file')], max_length=128, unique=True, verbose_name='Name')),
],
options={
'verbose_name': 'Action',
},
),
migrations.RunPython(add_default_actions)
]
# Generated by Django 2.1.7 on 2019-04-12 09:17
from django.db import migrations, models
def set_default_action_to_existing_perms(apps, schema_editor):
from orgs.utils import set_to_root_org
from ..models import Action
set_to_root_org()
perm_model = apps.get_model('perms', 'AssetPermission')
db_alias = schema_editor.connection.alias
perms = perm_model.objects.using(db_alias).all()
default_action = Action.get_action_all()
for perm in perms:
perm.actions.add(default_action.id)
class Migration(migrations.Migration):
dependencies = [
('perms', '0003_action'),
]
operations = [
migrations.AddField(
model_name='assetpermission',
name='actions',
field=models.ManyToManyField(blank=True, related_name='permissions', to='perms.Action', verbose_name='Action'),
),
migrations.RunPython(set_default_action_to_existing_perms)
]
...@@ -7,6 +7,26 @@ from django.utils import timezone ...@@ -7,6 +7,26 @@ from django.utils import timezone
from common.utils import date_expired_default, set_or_append_attr_bulk from common.utils import date_expired_default, set_or_append_attr_bulk
from orgs.mixins import OrgModelMixin, OrgManager from orgs.mixins import OrgModelMixin, OrgManager
from .const import PERMS_ACTION_NAME_CHOICES, PERMS_ACTION_NAME_ALL
class Action(models.Model):
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
name = models.CharField(
max_length=128, unique=True, choices=PERMS_ACTION_NAME_CHOICES,
verbose_name=_('Name')
)
class Meta:
verbose_name = _('Action')
def __str__(self):
return self.get_name_display()
@classmethod
def get_action_all(cls):
return cls.objects.get(name=PERMS_ACTION_NAME_ALL)
class AssetPermissionQuerySet(models.QuerySet): class AssetPermissionQuerySet(models.QuerySet):
def active(self): def active(self):
...@@ -30,6 +50,7 @@ class AssetPermission(OrgModelMixin): ...@@ -30,6 +50,7 @@ class AssetPermission(OrgModelMixin):
assets = models.ManyToManyField('assets.Asset', related_name='granted_by_permissions', blank=True, verbose_name=_("Asset")) assets = models.ManyToManyField('assets.Asset', related_name='granted_by_permissions', blank=True, verbose_name=_("Asset"))
nodes = models.ManyToManyField('assets.Node', related_name='granted_by_permissions', blank=True, verbose_name=_("Nodes")) nodes = models.ManyToManyField('assets.Node', related_name='granted_by_permissions', blank=True, verbose_name=_("Nodes"))
system_users = models.ManyToManyField('assets.SystemUser', related_name='granted_by_permissions', verbose_name=_("System user")) system_users = models.ManyToManyField('assets.SystemUser', related_name='granted_by_permissions', verbose_name=_("System user"))
actions = models.ManyToManyField('Action', related_name='permissions', blank=True, verbose_name=_('Action'))
is_active = models.BooleanField(default=True, verbose_name=_('Active')) is_active = models.BooleanField(default=True, verbose_name=_('Active'))
date_start = models.DateTimeField(default=timezone.now, db_index=True, verbose_name=_("Date start")) date_start = models.DateTimeField(default=timezone.now, db_index=True, verbose_name=_("Date start"))
date_expired = models.DateTimeField(default=date_expired_default, db_index=True, verbose_name=_('Date expired')) date_expired = models.DateTimeField(default=date_expired_default, db_index=True, verbose_name=_('Date expired'))
......
...@@ -4,7 +4,7 @@ ...@@ -4,7 +4,7 @@
from rest_framework import serializers from rest_framework import serializers
from common.fields import StringManyToManyField from common.fields import StringManyToManyField
from .models import AssetPermission from .models import AssetPermission, Action
from assets.models import Node, Asset, SystemUser from assets.models import Node, Asset, SystemUser
from assets.serializers import AssetGrantedSerializer from assets.serializers import AssetGrantedSerializer
...@@ -13,9 +13,16 @@ __all__ = [ ...@@ -13,9 +13,16 @@ __all__ = [
'AssetPermissionUpdateUserSerializer', 'AssetPermissionUpdateAssetSerializer', 'AssetPermissionUpdateUserSerializer', 'AssetPermissionUpdateAssetSerializer',
'AssetPermissionNodeSerializer', 'GrantedNodeSerializer', 'AssetPermissionNodeSerializer', 'GrantedNodeSerializer',
'GrantedAssetSerializer', 'GrantedSystemUserSerializer', 'GrantedAssetSerializer', 'GrantedSystemUserSerializer',
'ActionSerializer',
] ]
class ActionSerializer(serializers.ModelSerializer):
class Meta:
model = Action
fields = '__all__'
class AssetPermissionCreateUpdateSerializer(serializers.ModelSerializer): class AssetPermissionCreateUpdateSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = AssetPermission model = AssetPermission
...@@ -28,6 +35,7 @@ class AssetPermissionListSerializer(serializers.ModelSerializer): ...@@ -28,6 +35,7 @@ class AssetPermissionListSerializer(serializers.ModelSerializer):
assets = StringManyToManyField(many=True, read_only=True) assets = StringManyToManyField(many=True, read_only=True)
nodes = StringManyToManyField(many=True, read_only=True) nodes = StringManyToManyField(many=True, read_only=True)
system_users = StringManyToManyField(many=True, read_only=True) system_users = StringManyToManyField(many=True, read_only=True)
actions = StringManyToManyField(many=True, read_only=True)
is_valid = serializers.BooleanField() is_valid = serializers.BooleanField()
is_expired = serializers.BooleanField() is_expired = serializers.BooleanField()
......
...@@ -2,15 +2,37 @@ ...@@ -2,15 +2,37 @@
# #
from django.db.models.signals import m2m_changed, post_save, post_delete from django.db.models.signals import m2m_changed, post_save, post_delete
from django.dispatch import receiver from django.dispatch import receiver
from django.db import transaction
from common.utils import get_logger from common.utils import get_logger
from .utils import AssetPermissionUtil from .utils import AssetPermissionUtil
from .models import AssetPermission from .models import AssetPermission, Action
logger = get_logger(__file__) logger = get_logger(__file__)
def on_transaction_commit(func):
"""
如果不调用on_commit, 对象创建时添加多对多字段值失败
"""
def inner(*args, **kwargs):
transaction.on_commit(lambda: func(*args, **kwargs))
return inner
@receiver(post_save, sender=AssetPermission, dispatch_uid="my_unique_identifier")
@on_transaction_commit
def on_permission_created(sender, instance=None, created=False, **kwargs):
actions = instance.actions.all()
if created and not actions:
default_action = Action.get_action_all()
instance.actions.add(default_action)
logger.debug(
"Set default action to perms: {}".format(default_action, instance)
)
@receiver(post_save, sender=AssetPermission) @receiver(post_save, sender=AssetPermission)
def on_permission_update(sender, **kwargs): def on_permission_update(sender, **kwargs):
AssetPermissionUtil.expire_all_cache() AssetPermissionUtil.expire_all_cache()
......
...@@ -47,6 +47,9 @@ ...@@ -47,6 +47,9 @@
{% bootstrap_field form.nodes layout="horizontal" %} {% bootstrap_field form.nodes layout="horizontal" %}
{% bootstrap_field form.system_users layout="horizontal" %} {% bootstrap_field form.system_users layout="horizontal" %}
<div class="hr-line-dashed"></div> <div class="hr-line-dashed"></div>
<h3>{% trans 'Action' %}</h3>
{% bootstrap_field form.actions layout="horizontal" %}
<div class="hr-line-dashed"></div>
<h3>{% trans 'Other' %}</h3> <h3>{% trans 'Other' %}</h3>
<div class="form-group"> <div class="form-group">
<label for="{{ form.is_active.id_for_label }}" class="col-sm-2 control-label">{% trans 'Active' %}</label> <label for="{{ form.is_active.id_for_label }}" class="col-sm-2 control-label">{% trans 'Active' %}</label>
......
...@@ -7,6 +7,7 @@ from .. import api ...@@ -7,6 +7,7 @@ from .. import api
app_name = 'perms' app_name = 'perms'
router = routers.DefaultRouter() router = routers.DefaultRouter()
router.register('actions', api.ActionViewSet, 'action')
router.register('asset-permissions', api.AssetPermissionViewSet, 'asset-permission') router.register('asset-permissions', api.AssetPermissionViewSet, 'asset-permission')
urlpatterns = [ urlpatterns = [
...@@ -67,6 +68,8 @@ urlpatterns = [ ...@@ -67,6 +68,8 @@ urlpatterns = [
# 验证用户是否有某个资产和系统用户的权限 # 验证用户是否有某个资产和系统用户的权限
path('asset-permission/user/validate/', api.ValidateUserAssetPermissionApi.as_view(), path('asset-permission/user/validate/', api.ValidateUserAssetPermissionApi.as_view(),
name='validate-user-asset-permission'), name='validate-user-asset-permission'),
path('asset-permission/user/actions/', api.GetUserAssetPermissionActionsApi.as_view(),
name='get-user-asset-permission-actions'),
] ]
urlpatterns += router.urls urlpatterns += router.urls
......
# coding: utf-8 # coding: utf-8
from __future__ import absolute_import, unicode_literals
import uuid import uuid
from collections import defaultdict from collections import defaultdict
import json import json
...@@ -13,7 +12,7 @@ from django.conf import settings ...@@ -13,7 +12,7 @@ from django.conf import settings
from common.utils import get_logger from common.utils import get_logger
from common.tree import TreeNode from common.tree import TreeNode
from .models import AssetPermission from .models import AssetPermission, Action
from .hands import Node from .hands import Node
logger = get_logger(__file__) logger = get_logger(__file__)
...@@ -101,7 +100,7 @@ class AssetPermissionUtil: ...@@ -101,7 +100,7 @@ class AssetPermissionUtil:
"UserGroup": get_user_group_permissions, "UserGroup": get_user_group_permissions,
"Asset": get_asset_permissions, "Asset": get_asset_permissions,
"Node": get_node_permissions, "Node": get_node_permissions,
"SystemUser": get_node_permissions, "SystemUser": get_system_user_permissions,
} }
CACHE_KEY_PREFIX = '_ASSET_PERM_CACHE_' CACHE_KEY_PREFIX = '_ASSET_PERM_CACHE_'
...@@ -180,6 +179,24 @@ class AssetPermissionUtil: ...@@ -180,6 +179,24 @@ class AssetPermissionUtil:
) )
return assets return assets
def _setattr_actions_to_system_user(self):
"""
动态给system_use设置属性actions
"""
for asset, system_users in self._assets.items():
# 获取资产和资产的祖先节点的所有授权规则
perms = get_asset_permissions(asset, include_node=True)
# 过滤当前self.permission的授权规则
perms = perms.filter(id__in=[perm.id for perm in self.permissions])
for system_user in system_users:
actions = set()
_perms = perms.filter(system_users=system_user).\
prefetch_related('actions')
for _perm in _perms:
actions.update(_perm.actions.all())
setattr(system_user, 'actions', actions)
def get_assets_without_cache(self): def get_assets_without_cache(self):
if self._assets: if self._assets:
return self._assets return self._assets
...@@ -192,6 +209,7 @@ class AssetPermissionUtil: ...@@ -192,6 +209,7 @@ class AssetPermissionUtil:
[s for s in system_users if s.protocol == asset.protocol] [s for s in system_users if s.protocol == asset.protocol]
) )
self._assets = assets self._assets = assets
self._setattr_actions_to_system_user()
return self._assets return self._assets
def get_cache_key(self, resource): def get_cache_key(self, resource):
...@@ -395,6 +413,7 @@ def parse_asset_to_tree_node(node, asset, system_users): ...@@ -395,6 +413,7 @@ def parse_asset_to_tree_node(node, asset, system_users):
'protocol': system_user.protocol, 'protocol': system_user.protocol,
'priority': system_user.priority, 'priority': system_user.priority,
'login_mode': system_user.login_mode, 'login_mode': system_user.login_mode,
'actions': [action.name for action in system_user.actions],
'comment': system_user.comment, 'comment': system_user.comment,
}) })
data = { data = {
...@@ -423,3 +442,21 @@ def parse_asset_to_tree_node(node, asset, system_users): ...@@ -423,3 +442,21 @@ def parse_asset_to_tree_node(node, asset, system_users):
} }
tree_node = TreeNode(**data) tree_node = TreeNode(**data)
return tree_node return tree_node
#
# actions
#
def check_system_user_action(system_user, action):
"""
:param system_user: SystemUser object (包含动态属性: actions)
:param action: Action object
:return: bool
"""
check_actions = [Action.get_action_all(), action]
granted_actions = getattr(system_user, 'actions', [])
actions = list(set(granted_actions).intersection(set(check_actions)))
return bool(actions)
...@@ -11,8 +11,9 @@ from django.conf import settings ...@@ -11,8 +11,9 @@ from django.conf import settings
from common.permissions import AdminUserRequiredMixin from common.permissions import AdminUserRequiredMixin
from orgs.utils import current_org from orgs.utils import current_org
from .hands import Node, Asset, SystemUser, User, UserGroup from .hands import Node, Asset, SystemUser, User, UserGroup
from .models import AssetPermission from .models import AssetPermission, Action
from .forms import AssetPermissionForm from .forms import AssetPermissionForm
from .const import PERMS_ACTION_NAME_ALL
class AssetPermissionListView(AdminUserRequiredMixin, TemplateView): class AssetPermissionListView(AdminUserRequiredMixin, TemplateView):
...@@ -46,6 +47,8 @@ class AssetPermissionCreateView(AdminUserRequiredMixin, CreateView): ...@@ -46,6 +47,8 @@ class AssetPermissionCreateView(AdminUserRequiredMixin, CreateView):
assets_id = assets_id.split(",") assets_id = assets_id.split(",")
assets = Asset.objects.filter(id__in=assets_id) assets = Asset.objects.filter(id__in=assets_id)
form['assets'].initial = assets form['assets'].initial = assets
form['actions'].initial = Action.objects.get(name=PERMS_ACTION_NAME_ALL)
return form return form
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment