Commit 8f699fa3 authored by ibuler's avatar ibuler

[Update] 修改Permission

parent 8e9b3f13
......@@ -7,15 +7,36 @@ from django.utils.translation import ugettext_lazy as _
from orgs.mixins import OrgModelForm
from orgs.utils import current_org
from perms.models import AssetPermission
from assets.models import Asset, Node
from ..models import AssetPermission, ActionFlag
__all__ = [
'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):
action = ActionField()
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
users_field = self.fields.get('users')
......@@ -32,10 +53,6 @@ class AssetPermissionForm(OrgModelForm):
nodes_field = self.fields['nodes']
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:
model = AssetPermission
exclude = (
......
......@@ -36,10 +36,7 @@ class Migration(migrations.Migration):
migrations.AddField(
model_name='assetpermission',
name='action',
field=models.IntegerField(
choices=[(255, 'All'), (1, 'Connect'), (2, 'Upload file'),
(6, 'Upload download'), (4, 'Download file')],
default=255, verbose_name='Action'),
field=models.IntegerField(choices=[(255, 'All'), (1, 'Connect'), (2, 'Upload file'), (5, 'Upload download'), (4, 'Download file')], default=255, verbose_name='Action'),
),
migrations.RunPython(migrate_old_actions),
]
import uuid
from functools import reduce
from django.db import models
from django.utils.translation import ugettext_lazy as _
......@@ -37,17 +38,41 @@ class ActionFlag:
CONNECT = 0b00000001
UPLOAD = 0b00000010
DOWNLOAD = 0b00000100
UPDOWNLOAD = CONNECT | DOWNLOAD
UPDOWNLOAD = UPLOAD | DOWNLOAD
CONNECT_UPLOADOWN = CONNECT | UPDOWNLOAD
ALL = 0b11111111
NAME_MAP = {
"connect": CONNECT,
"upload": UPLOAD,
"download": DOWNLOAD,
"updownload": UPDOWNLOAD,
"all": ALL,
}
CHOICES = (
(ALL, _('All')),
(CONNECT, _('Connect')),
(UPLOAD, _('Upload file')),
(UPDOWNLOAD, _("Upload download")),
(UPLOAD, _('Upload 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):
assets = models.ManyToManyField('assets.Asset', related_name='granted_by_permissions', blank=True, verbose_name=_("Asset"))
......@@ -60,13 +85,9 @@ class AssetPermission(BasePermission):
unique_together = [('org_id', 'name')]
verbose_name = _("Asset permission")
def get_all_assets(self):
assets = set(self.assets.all())
for node in self.nodes.all():
_assets = node.get_all_assets()
set_or_append_attr_bulk(_assets, 'inherit', node.value)
assets.update(set(_assets))
return assets
@classmethod
def get_queryset_with_prefetch(cls):
return cls.objects.all().valid().prefetch_related('nodes', 'assets', 'system_users')
class NodePermission(OrgModelMixin):
......
# -*- coding: utf-8 -*-
#
from functools import reduce
from rest_framework import serializers
from common.fields import StringManyToManyField
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.serializers import AssetGrantedSerializer
......@@ -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):
action = ActionField()
class Meta:
model = AssetPermission
exclude = ('created_by', 'date_created')
......@@ -29,7 +51,7 @@ class AssetPermissionListSerializer(BulkOrgResourceModelSerializer):
assets = StringManyToManyField(many=True, read_only=True)
nodes = 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_expired = serializers.BooleanField()
......
......@@ -110,6 +110,7 @@ var dateOptions = {
format: 'YYYY-MM-DD HH:mm'
}
};
var api_action = "{{ api_action }}";
$(document).ready(function () {
$('.select2').select2({
closeOnSelect: false
......@@ -147,21 +148,17 @@ $(document).ready(function () {
.on("submit", "form", function (evt) {
evt.preventDefault();
var the_url = '{% url 'api-perms:asset-permission-list' %}';
var redirect_to = '{% url "perms:asset-permission-list" %}';
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 data = form.serializeObject();
console.log(data)
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']);
objectAttrsIsList(data, ['users', 'user_groups', 'system_users', 'nodes', 'assets', 'actions']);
objectAttrsIsDatetime(data, ['date_start', 'date_expired']);
objectAttrsIsBool(data, ['is_active'])
objectAttrsIsBool(data, ['is_active']);
console.log(data)
var props = {
url: the_url,
......
......@@ -122,8 +122,8 @@ function format(d) {
if (d.system_users.length > 0) {
data += makeLabel(["{% trans 'System user' %}", d.system_users.join(", ")])
}
if (d.actions.length > 0) {
data += makeLabel(["{% trans 'Action' %}", d.actions.join(", ")])
if (d.action.length > 0) {
data += makeLabel(["{% trans 'Action' %}", d.action.join(", ")])
}
return data
}
......
......@@ -103,11 +103,11 @@ def get_user_permissions(user, include_group=True):
arg = Q(users=user) | Q(user_groups__in=groups)
else:
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):
return AssetPermission.objects.valid().filter(
return AssetPermission.get_queryset_with_prefetch().filter(
user_groups=user_group
)
......@@ -282,36 +282,55 @@ class AssetPermissionCacheMixin:
cache.delete_pattern(key)
class FlatPermissionQueryset:
def __init__(self):
self.queryset = defaultdict(list)
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]):
class FlatPermissionQueryset(set):
def add_many(self, assets_or_nodes, system_users, action, rtp="asset"):
print("Add many: {}-{}-{}".format(len(assets_or_nodes), len(system_users), action))
if not any([assets_or_nodes, system_users, action]):
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:
permission = FlatPermission(source, sysuser, action)
permission = FlatPermission(source, sysuser, action, rtp=rtp)
print("ADDDDDDDDDDDDDDDd")
self.add(permission)
def clean(self):
pass
def group_by_resource(self):
resources = defaultdict(lambda: defaultdict(int))
for i in self:
resources[i.resource][i.system_user] |= i.action
return resources
class FlatPermission:
def __init__(self, asset_or_node, system_user, action):
self.id = asset_or_node.id
self.source = asset_or_node
def __init__(self, assets_or_node, system_user, action, rtp="asset"):
self.id = "{}_{}_{}".format(assets_or_node.id, system_user.id, action)
self.resource = assets_or_node
self.resource_type = rtp
self.system_user = system_user
self.action = action
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):
......@@ -355,33 +374,20 @@ class AssetPermissionUtil(AssetPermissionCacheMixin):
self._permissions = self.permissions.filter(**filters)
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
def get_nodes_direct(self):
"""
返回用户/组授权规则直接关联的节点
:return: {node1: {system_user1: {'actions': set()},}}
"""
nodes = FlatPermissionQueryset()
permissions = self.permissions
for perm in permissions:
actions = perm.actions.all()
queryset = FlatPermissionQueryset()
for perm in self.permissions:
actions = perm.action
system_users = perm.system_users.all()
_nodes = perm.nodes.all()
nodes.add_many(_nodes, system_users, actions)
return nodes
nodes = perm.nodes.all()
queryset.add_many(nodes, system_users, actions, rtp="nodes")
print(queryset)
return queryset.group_by_resource()
@timeit
def get_assets_direct(self):
......@@ -389,15 +395,14 @@ class AssetPermissionUtil(AssetPermissionCacheMixin):
返回用户授权规则直接关联的资产
:return: {asset1: {system_user1: {'actions': set()},}}
"""
assets = defaultdict(dict)
permissions = self.permissions.prefetch_related('assets', 'system_users')
for perm in permissions:
actions = perm.actions.all()
for asset in perm.assets.all().valid().prefetch_related('nodes'):
system_users = perm.system_users.filter(protocol__in=asset.protocols_name)
system_users = self._structured_system_user(system_users, actions)
assets[asset].update(system_users)
return assets
queryset = FlatPermissionQueryset()
for perm in self.permissions:
action = perm.action
assets = perm.assets.all()
system_users = perm.system_users.all()
queryset.add_many(assets, system_users, action, rtp="assets")
print(queryset)
return queryset.group_by_resource()
@timeit
def get_assets_without_cache(self):
......@@ -408,27 +413,10 @@ class AssetPermissionUtil(AssetPermissionCacheMixin):
return self._assets
assets = self.get_assets_direct()
nodes = self.get_nodes_direct()
# for node, system_users in nodes.items():
# print(">>>>> Node<<<<<<<<<<<<: ", node.value)
# _assets = list(node.get_all_valid_assets())
# for asset in _assets:
# 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
print("++++++++++++++++++++++")
print(assets)
print("---------------------")
print(nodes)
@timeit
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):
context = {
'app': _('Perms'),
'action': _('Create asset permission'),
'api_action': "create",
}
kwargs.update(context)
return super().get_context_data(**kwargs)
......@@ -79,7 +80,8 @@ class AssetPermissionUpdateView(PermissionsMixin, UpdateView):
def get_context_data(self, **kwargs):
context = {
'app': _('Perms'),
'action': _('Update asset permission')
'action': _('Update asset permission'),
'api_action': "update",
}
kwargs.update(context)
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