Commit 8f699fa3 authored by ibuler's avatar ibuler

[Update] 修改Permission

parent 8e9b3f13
...@@ -7,15 +7,36 @@ from django.utils.translation import ugettext_lazy as _ ...@@ -7,15 +7,36 @@ from django.utils.translation import ugettext_lazy as _
from orgs.mixins import OrgModelForm from orgs.mixins import OrgModelForm
from orgs.utils import current_org from orgs.utils import current_org
from perms.models import AssetPermission
from assets.models import Asset, Node from assets.models import Asset, Node
from ..models import AssetPermission, ActionFlag
__all__ = [ __all__ = [
'AssetPermissionForm', 'AssetPermissionForm',
] ]
class ActionField(forms.MultipleChoiceField):
def __init__(self, *args, **kwargs):
kwargs['choices'] = ActionFlag.CHOICES
kwargs['initial'] = ActionFlag.ALL
kwargs['label'] = _("Action")
kwargs['widget'] = forms.CheckboxSelectMultiple()
super().__init__(*args, **kwargs)
def to_python(self, value):
value = super().to_python(value)
return ActionFlag.choices_to_value(value)
def prepare_value(self, value):
if value is None:
return value
value = ActionFlag.value_to_choices(value)
return value
class AssetPermissionForm(OrgModelForm): class AssetPermissionForm(OrgModelForm):
action = ActionField()
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
users_field = self.fields.get('users') users_field = self.fields.get('users')
...@@ -32,10 +53,6 @@ class AssetPermissionForm(OrgModelForm): ...@@ -32,10 +53,6 @@ class AssetPermissionForm(OrgModelForm):
nodes_field = self.fields['nodes'] nodes_field = self.fields['nodes']
nodes_field._queryset = Node.get_queryset() nodes_field._queryset = Node.get_queryset()
def clean_action(self):
actions = self.cleaned_data.get("action")
return reduce(lambda x, y: x | y, actions)
class Meta: class Meta:
model = AssetPermission model = AssetPermission
exclude = ( exclude = (
......
...@@ -36,10 +36,7 @@ class Migration(migrations.Migration): ...@@ -36,10 +36,7 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='assetpermission', model_name='assetpermission',
name='action', name='action',
field=models.IntegerField( field=models.IntegerField(choices=[(255, 'All'), (1, 'Connect'), (2, 'Upload file'), (5, 'Upload download'), (4, 'Download file')], default=255, verbose_name='Action'),
choices=[(255, 'All'), (1, 'Connect'), (2, 'Upload file'),
(6, 'Upload download'), (4, 'Download file')],
default=255, verbose_name='Action'),
), ),
migrations.RunPython(migrate_old_actions), migrations.RunPython(migrate_old_actions),
] ]
import uuid import uuid
from functools import reduce
from django.db import models from django.db import models
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
...@@ -37,17 +38,41 @@ class ActionFlag: ...@@ -37,17 +38,41 @@ class ActionFlag:
CONNECT = 0b00000001 CONNECT = 0b00000001
UPLOAD = 0b00000010 UPLOAD = 0b00000010
DOWNLOAD = 0b00000100 DOWNLOAD = 0b00000100
UPDOWNLOAD = CONNECT | DOWNLOAD UPDOWNLOAD = UPLOAD | DOWNLOAD
CONNECT_UPLOADOWN = CONNECT | UPDOWNLOAD
ALL = 0b11111111 ALL = 0b11111111
NAME_MAP = {
"connect": CONNECT,
"upload": UPLOAD,
"download": DOWNLOAD,
"updownload": UPDOWNLOAD,
"all": ALL,
}
CHOICES = ( CHOICES = (
(ALL, _('All')), (ALL, _('All')),
(CONNECT, _('Connect')), (CONNECT, _('Connect')),
(UPLOAD, _('Upload file')),
(UPDOWNLOAD, _("Upload download")), (UPDOWNLOAD, _("Upload download")),
(UPLOAD, _('Upload file')),
(DOWNLOAD, _('Download file')), (DOWNLOAD, _('Download file')),
) )
@classmethod
def value_to_choices(cls, value):
value = int(value)
if value == cls.ALL:
return [cls.ALL]
elif value == cls.UPDOWNLOAD:
return [cls.UPDOWNLOAD]
elif value == cls.CONNECT_UPLOADOWN:
return [cls.CONNECT, cls.UPDOWNLOAD]
else:
return [i for i in dict(cls.CHOICES) if i == i & int(value)]
@classmethod
def choices_to_value(cls, value):
return reduce(lambda x, y: int(x) | int(y), value)
class AssetPermission(BasePermission): class AssetPermission(BasePermission):
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"))
...@@ -60,13 +85,9 @@ class AssetPermission(BasePermission): ...@@ -60,13 +85,9 @@ class AssetPermission(BasePermission):
unique_together = [('org_id', 'name')] unique_together = [('org_id', 'name')]
verbose_name = _("Asset permission") verbose_name = _("Asset permission")
def get_all_assets(self): @classmethod
assets = set(self.assets.all()) def get_queryset_with_prefetch(cls):
for node in self.nodes.all(): return cls.objects.all().valid().prefetch_related('nodes', 'assets', 'system_users')
_assets = node.get_all_assets()
set_or_append_attr_bulk(_assets, 'inherit', node.value)
assets.update(set(_assets))
return assets
class NodePermission(OrgModelMixin): class NodePermission(OrgModelMixin):
......
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
from functools import reduce
from rest_framework import serializers from rest_framework import serializers
from common.fields import StringManyToManyField from common.fields import StringManyToManyField
from orgs.mixins import BulkOrgResourceModelSerializer from orgs.mixins import BulkOrgResourceModelSerializer
from perms.models import AssetPermission, Action from perms.models import AssetPermission, Action, ActionFlag
from assets.models import Node from assets.models import Node
from assets.serializers import AssetGrantedSerializer from assets.serializers import AssetGrantedSerializer
...@@ -17,7 +18,28 @@ __all__ = [ ...@@ -17,7 +18,28 @@ __all__ = [
] ]
class ActionField(serializers.MultipleChoiceField):
def __init__(self, *args, **kwargs):
kwargs['choices'] = ActionFlag.CHOICES
super().__init__(*args, **kwargs)
def to_representation(self, value):
return ActionFlag.value_to_choices(value)
def to_internal_value(self, data):
return ActionFlag.choices_to_value(data)
class ActionDisplayField(ActionField):
def to_representation(self, value):
values = super().to_representation(value)
choices = dict(ActionFlag.CHOICES)
return [choices.get(i) for i in values]
class AssetPermissionCreateUpdateSerializer(BulkOrgResourceModelSerializer): class AssetPermissionCreateUpdateSerializer(BulkOrgResourceModelSerializer):
action = ActionField()
class Meta: class Meta:
model = AssetPermission model = AssetPermission
exclude = ('created_by', 'date_created') exclude = ('created_by', 'date_created')
...@@ -29,7 +51,7 @@ class AssetPermissionListSerializer(BulkOrgResourceModelSerializer): ...@@ -29,7 +51,7 @@ class AssetPermissionListSerializer(BulkOrgResourceModelSerializer):
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)
action = serializers.IntegerField(read_only=True) action = ActionDisplayField()
is_valid = serializers.BooleanField() is_valid = serializers.BooleanField()
is_expired = serializers.BooleanField() is_expired = serializers.BooleanField()
......
...@@ -110,6 +110,7 @@ var dateOptions = { ...@@ -110,6 +110,7 @@ var dateOptions = {
format: 'YYYY-MM-DD HH:mm' format: 'YYYY-MM-DD HH:mm'
} }
}; };
var api_action = "{{ api_action }}";
$(document).ready(function () { $(document).ready(function () {
$('.select2').select2({ $('.select2').select2({
closeOnSelect: false closeOnSelect: false
...@@ -147,21 +148,17 @@ $(document).ready(function () { ...@@ -147,21 +148,17 @@ $(document).ready(function () {
.on("submit", "form", function (evt) { .on("submit", "form", function (evt) {
evt.preventDefault(); evt.preventDefault();
var the_url = '{% url 'api-perms:asset-permission-list' %}'; var the_url = '{% url 'api-perms:asset-permission-list' %}';
var redirect_to = '{% url "perms:asset-permission-list" %}';
var method = "POST"; var method = "POST";
{% if api_action == "update" %}
the_url = '{% url 'api-perms:asset-permission-detail' pk=object.id %}';
method = "PUT";
{% endif %}
var redirect_to = '{% url "perms:asset-permission-list" %}';
var form = $("form"); var form = $("form");
var data = form.serializeObject(); var data = form.serializeObject();
console.log(data) objectAttrsIsList(data, ['users', 'user_groups', 'system_users', 'nodes', 'assets', 'actions']);
var actions = data.action;
var action = 0;
for (i=0;i<actions.length;i++) {
console.log(actions[i])
action |= actions[i];
}
data.action = action;
objectAttrsIsList(data, ['users', 'user_groups', 'system_users', 'nodes', 'assets']);
objectAttrsIsDatetime(data, ['date_start', 'date_expired']); objectAttrsIsDatetime(data, ['date_start', 'date_expired']);
objectAttrsIsBool(data, ['is_active']) objectAttrsIsBool(data, ['is_active']);
console.log(data) console.log(data)
var props = { var props = {
url: the_url, url: the_url,
......
...@@ -122,8 +122,8 @@ function format(d) { ...@@ -122,8 +122,8 @@ function format(d) {
if (d.system_users.length > 0) { if (d.system_users.length > 0) {
data += makeLabel(["{% trans 'System user' %}", d.system_users.join(", ")]) data += makeLabel(["{% trans 'System user' %}", d.system_users.join(", ")])
} }
if (d.actions.length > 0) { if (d.action.length > 0) {
data += makeLabel(["{% trans 'Action' %}", d.actions.join(", ")]) data += makeLabel(["{% trans 'Action' %}", d.action.join(", ")])
} }
return data return data
} }
......
...@@ -103,11 +103,11 @@ def get_user_permissions(user, include_group=True): ...@@ -103,11 +103,11 @@ def get_user_permissions(user, include_group=True):
arg = Q(users=user) | Q(user_groups__in=groups) arg = Q(users=user) | Q(user_groups__in=groups)
else: else:
arg = Q(users=user) arg = Q(users=user)
return AssetPermission.objects.valid().filter(arg) return AssetPermission.get_queryset_with_prefetch().filter(arg)
def get_user_group_permissions(user_group): def get_user_group_permissions(user_group):
return AssetPermission.objects.valid().filter( return AssetPermission.get_queryset_with_prefetch().filter(
user_groups=user_group user_groups=user_group
) )
...@@ -282,36 +282,55 @@ class AssetPermissionCacheMixin: ...@@ -282,36 +282,55 @@ class AssetPermissionCacheMixin:
cache.delete_pattern(key) cache.delete_pattern(key)
class FlatPermissionQueryset: class FlatPermissionQueryset(set):
def __init__(self): def add_many(self, assets_or_nodes, system_users, action, rtp="asset"):
self.queryset = defaultdict(list) print("Add many: {}-{}-{}".format(len(assets_or_nodes), len(system_users), action))
if not any([assets_or_nodes, system_users, action]):
def add(self, permission):
self.queryset[permission.id].append(permission)
def add_many(self, assets_or_nodes, system_users, actions):
if any([assets_or_nodes, system_users, actions]):
return return
iterable = itertools.product(assets_or_nodes, system_users, actions) iterable = itertools.product(assets_or_nodes, system_users, [action])
for source, sysuser, action in iterable: for source, sysuser, action in iterable:
permission = FlatPermission(source, sysuser, action) permission = FlatPermission(source, sysuser, action, rtp=rtp)
print("ADDDDDDDDDDDDDDDd")
self.add(permission) self.add(permission)
def clean(self): def group_by_resource(self):
pass resources = defaultdict(lambda: defaultdict(int))
for i in self:
resources[i.resource][i.system_user] |= i.action
return resources
class FlatPermission: class FlatPermission:
def __init__(self, asset_or_node, system_user, action): def __init__(self, assets_or_node, system_user, action, rtp="asset"):
self.id = asset_or_node.id self.id = "{}_{}_{}".format(assets_or_node.id, system_user.id, action)
self.source = asset_or_node self.resource = assets_or_node
self.resource_type = rtp
self.system_user = system_user self.system_user = system_user
self.action = action self.action = action
def __eq__(self, other): def __eq__(self, other):
pass if self.id == other.id:
return True
# 资产不同
if self.resource_type == "asset" and self.id != other.id:
return False
# 不是子节点
elif self.resource_type == "node" and not other.resource.key.startswith(self.resource.key):
return False
# 系统用户优先级大于后者,则相同
if self.system_user.priority > self.system_user.priority:
return True
# 如果系统用户不同,则不同
elif self.system_user != other.system_user:
return False
# 如果action为与后的结果则相同
if self.action == self.action | other.action:
return True
return False
def __hash__(self):
return hash(self.id)
class AssetPermissionUtil(AssetPermissionCacheMixin): class AssetPermissionUtil(AssetPermissionCacheMixin):
...@@ -355,33 +374,20 @@ class AssetPermissionUtil(AssetPermissionCacheMixin): ...@@ -355,33 +374,20 @@ class AssetPermissionUtil(AssetPermissionCacheMixin):
self._permissions = self.permissions.filter(**filters) self._permissions = self.permissions.filter(**filters)
self._filter_id = md5(filters_json.encode()).hexdigest() self._filter_id = md5(filters_json.encode()).hexdigest()
@staticmethod
@timeit
def _structured_system_user(system_users, actions):
"""
结构化系统用户
:param system_users:
:param actions:
:return: {system_user1: {'actions': set(), }, }
"""
_attr = {'actions': set(actions)}
_system_users = {system_user: _attr for system_user in system_users}
return _system_users
@timeit @timeit
def get_nodes_direct(self): def get_nodes_direct(self):
""" """
返回用户/组授权规则直接关联的节点 返回用户/组授权规则直接关联的节点
:return: {node1: {system_user1: {'actions': set()},}} :return: {node1: {system_user1: {'actions': set()},}}
""" """
nodes = FlatPermissionQueryset() queryset = FlatPermissionQueryset()
permissions = self.permissions for perm in self.permissions:
for perm in permissions: actions = perm.action
actions = perm.actions.all()
system_users = perm.system_users.all() system_users = perm.system_users.all()
_nodes = perm.nodes.all() nodes = perm.nodes.all()
nodes.add_many(_nodes, system_users, actions) queryset.add_many(nodes, system_users, actions, rtp="nodes")
return nodes print(queryset)
return queryset.group_by_resource()
@timeit @timeit
def get_assets_direct(self): def get_assets_direct(self):
...@@ -389,15 +395,14 @@ class AssetPermissionUtil(AssetPermissionCacheMixin): ...@@ -389,15 +395,14 @@ class AssetPermissionUtil(AssetPermissionCacheMixin):
返回用户授权规则直接关联的资产 返回用户授权规则直接关联的资产
:return: {asset1: {system_user1: {'actions': set()},}} :return: {asset1: {system_user1: {'actions': set()},}}
""" """
assets = defaultdict(dict) queryset = FlatPermissionQueryset()
permissions = self.permissions.prefetch_related('assets', 'system_users') for perm in self.permissions:
for perm in permissions: action = perm.action
actions = perm.actions.all() assets = perm.assets.all()
for asset in perm.assets.all().valid().prefetch_related('nodes'): system_users = perm.system_users.all()
system_users = perm.system_users.filter(protocol__in=asset.protocols_name) queryset.add_many(assets, system_users, action, rtp="assets")
system_users = self._structured_system_user(system_users, actions) print(queryset)
assets[asset].update(system_users) return queryset.group_by_resource()
return assets
@timeit @timeit
def get_assets_without_cache(self): def get_assets_without_cache(self):
...@@ -408,27 +413,10 @@ class AssetPermissionUtil(AssetPermissionCacheMixin): ...@@ -408,27 +413,10 @@ class AssetPermissionUtil(AssetPermissionCacheMixin):
return self._assets return self._assets
assets = self.get_assets_direct() assets = self.get_assets_direct()
nodes = self.get_nodes_direct() nodes = self.get_nodes_direct()
# for node, system_users in nodes.items(): print("++++++++++++++++++++++")
# print(">>>>> Node<<<<<<<<<<<<: ", node.value) print(assets)
# _assets = list(node.get_all_valid_assets()) print("---------------------")
# for asset in _assets: print(nodes)
# for system_user, attr_dict in system_users.items():
# if not asset.has_protocol(system_user.protocol):
# continue
# if system_user in assets[asset]:
# actions = assets[asset][system_user]['actions']
# attr_dict['actions'].update(actions)
# system_users.update({system_user: attr_dict})
# assets[asset].update(system_users)
__assets = defaultdict(set)
for asset, system_users in assets.items():
for system_user, attr_dict in system_users.items():
setattr(system_user, 'actions', attr_dict['actions'])
__assets[asset] = set(system_users.keys())
self._assets = __assets
return self._assets
@timeit @timeit
def get_nodes_with_assets_without_cache(self): def get_nodes_with_assets_without_cache(self):
......
# -*- coding: utf-8 -*-
#
from django.test import TestCase
from assets.models import Node, SystemUser
from .asset_permission import FlatPermission
from ..models import ActionFlag
class TestFlatPermissionEqual(TestCase):
def setUp(self):
node1 = Node(value="parent", key="1:1")
node2 = Node(value="child", key="1:1:1")
system_user1 = SystemUser(username="name1", name="name1", priority=20)
system_user2 = SystemUser(username="name2", name="name2", priority=10)
action1 = ActionFlag.ALL
action2 = ActionFlag.CONNECT
action3 = ActionFlag.UPDOWNLOAD
perm1 = FlatPermission(node1, system_user1, action1)
perm2 = FlatPermission(node2, system_user1, action1)
perm3 = FlatPermission(node2, system_user2, action1)
self.groups = (
(perm1, perm2, True),
(perm1, perm3, True),
)
def test_equal(self):
for k, k2, wanted in self.groups:
if (k == k2) != wanted:
print("Not equal {} {}", k, k2)
...@@ -64,6 +64,7 @@ class AssetPermissionCreateView(PermissionsMixin, CreateView): ...@@ -64,6 +64,7 @@ class AssetPermissionCreateView(PermissionsMixin, CreateView):
context = { context = {
'app': _('Perms'), 'app': _('Perms'),
'action': _('Create asset permission'), 'action': _('Create asset permission'),
'api_action': "create",
} }
kwargs.update(context) kwargs.update(context)
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)
...@@ -79,7 +80,8 @@ class AssetPermissionUpdateView(PermissionsMixin, UpdateView): ...@@ -79,7 +80,8 @@ class AssetPermissionUpdateView(PermissionsMixin, UpdateView):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = { context = {
'app': _('Perms'), 'app': _('Perms'),
'action': _('Update asset permission') 'action': _('Update asset permission'),
'api_action': "update",
} }
kwargs.update(context) kwargs.update(context)
return super().get_context_data(**kwargs) return super().get_context_data(**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