diff --git a/apps/assets/models/asset.py b/apps/assets/models/asset.py index 1a9fd8ea3..9db42c938 100644 --- a/apps/assets/models/asset.py +++ b/apps/assets/models/asset.py @@ -44,28 +44,18 @@ class Asset(models.Model): ip = models.GenericIPAddressField(max_length=32, verbose_name=_('IP'), db_index=True) hostname = models.CharField(max_length=128, unique=True, verbose_name=_('Hostname')) port = models.IntegerField(default=22, verbose_name=_('Port')) - groups = models.ManyToManyField(AssetGroup, blank=True, related_name='assets', - verbose_name=_('Asset groups')) - admin_user = models.ForeignKey(AdminUser, null=True, blank=True, related_name='assets', - on_delete=models.SET_NULL, verbose_name=_("Admin user")) - system_users = models.ManyToManyField(SystemUser, blank=True, - related_name='assets', - verbose_name=_("System User")) - idc = models.ForeignKey(IDC, blank=True, null=True, related_name='assets', - on_delete=models.SET_NULL, verbose_name=_('IDC'),) + groups = models.ManyToManyField(AssetGroup, blank=True, related_name='assets', verbose_name=_('Asset groups')) + admin_user = models.ForeignKey(AdminUser, null=True, blank=True, related_name='assets', on_delete=models.SET_NULL, verbose_name=_("Admin user")) + system_users = models.ManyToManyField(SystemUser, blank=True, related_name='assets', verbose_name=_("System User")) + idc = models.ForeignKey(IDC, blank=True, null=True, related_name='assets', on_delete=models.SET_NULL, verbose_name=_('IDC'),) is_active = models.BooleanField(default=True, verbose_name=_('Is active')) - type = models.CharField(choices=TYPE_CHOICES, max_length=16, blank=True, null=True, - default='Server', verbose_name=_('Asset type'),) - env = models.CharField(choices=ENV_CHOICES, max_length=8, blank=True, null=True, - default='Prod', verbose_name=_('Asset environment'),) - status = models.CharField(choices=STATUS_CHOICES, max_length=12, null=True, blank=True, - default='In use', verbose_name=_('Asset status')) + type = models.CharField(choices=TYPE_CHOICES, max_length=16, blank=True, null=True, default='Server', verbose_name=_('Asset type'),) + env = models.CharField(choices=ENV_CHOICES, max_length=8, blank=True, null=True, default='Prod', verbose_name=_('Asset environment'),) + status = models.CharField(choices=STATUS_CHOICES, max_length=12, null=True, blank=True, default='In use', verbose_name=_('Asset status')) # Some information - public_ip = models.GenericIPAddressField(max_length=32, blank=True, - null=True, verbose_name=_('Public IP')) - remote_card_ip = models.CharField(max_length=16, null=True, blank=True, - verbose_name=_('Remote control card IP')) + public_ip = models.GenericIPAddressField(max_length=32, blank=True, null=True, verbose_name=_('Public IP')) + remote_card_ip = models.CharField(max_length=16, null=True, blank=True, verbose_name=_('Remote control card IP')) cabinet_no = models.CharField(max_length=32, null=True, blank=True, verbose_name=_('Cabinet number')) cabinet_pos = models.IntegerField(null=True, blank=True, verbose_name=_('Cabinet position')) number = models.CharField(max_length=32, null=True, blank=True, verbose_name=_('Asset number')) @@ -114,22 +104,27 @@ class Asset(models.Model): def _to_secret_json(self): """Ansible use it create inventory""" - return { + data = { 'id': self.id, 'hostname': self.hostname, 'ip': self.ip, 'port': self.port, 'groups': [group.name for group in self.groups.all()], - 'username': self.admin_user.username if self.admin_user else '', - 'password': self.admin_user.password if self.admin_user else '', - 'private_key': self.admin_user.private_key_file if self.admin_user else None, - 'become': { - 'method': self.admin_user.become_method, - 'user': self.admin_user.become_user, - 'pass': self.admin_user.become_pass, - } if self.admin_user and self.admin_user.become else {}, } + if self.admin_user: + data.update({ + 'username': self.admin_user.username, + 'password': self.admin_user.password, + 'private_key': self.admin_user.private_key_file, + 'become': { + 'method': self.admin_user.become_method, + 'user': self.admin_user.become_user, + 'pass': self.admin_user.become_pass, + } + }) + return data + class Meta: unique_together = ('ip', 'port') diff --git a/apps/assets/models/user.py b/apps/assets/models/user.py index a95041a64..7cb5f83ba 100644 --- a/apps/assets/models/user.py +++ b/apps/assets/models/user.py @@ -28,6 +28,9 @@ def private_key_validator(value): class AdminUser(models.Model): + """ + Ansible use admin user as devops user to run adHoc and Playbook + """ BECOME_METHOD_CHOICES = ( ('sudo', 'sudo'), ('su', 'su'), @@ -35,24 +38,19 @@ class AdminUser(models.Model): id = models.UUIDField(default=uuid.uuid4, primary_key=True) name = models.CharField(max_length=128, unique=True, verbose_name=_('Name')) username = models.CharField(max_length=16, verbose_name=_('Username')) - _password = models.CharField( - max_length=256, blank=True, null=True, verbose_name=_('Password')) - _private_key = models.TextField(max_length=4096, blank=True, null=True, verbose_name=_('SSH private key'), - validators=[private_key_validator,]) + _password = models.CharField(max_length=256, blank=True, null=True, verbose_name=_('Password')) + _private_key = models.TextField(max_length=4096, blank=True, null=True, verbose_name=_('SSH private key'), validators=[private_key_validator,]) become = models.BooleanField(default=True) become_method = models.CharField(choices=BECOME_METHOD_CHOICES, default='sudo', max_length=4) become_user = models.CharField(default='root', max_length=64) become_pass = models.CharField(default='', max_length=128) - _public_key = models.TextField( - max_length=4096, blank=True, verbose_name=_('SSH public key')) + _public_key = models.TextField(max_length=4096, blank=True, verbose_name=_('SSH public key')) comment = models.TextField(blank=True, verbose_name=_('Comment')) date_created = models.DateTimeField(auto_now_add=True, null=True) - created_by = models.CharField( - max_length=32, null=True, verbose_name=_('Created by')) + created_by = models.CharField(max_length=32, null=True, verbose_name=_('Created by')) - def __unicode__(self): + def __str__(self): return self.name - __str__ = __unicode__ @property def password(self): @@ -134,33 +132,22 @@ class SystemUser(models.Model): ('K', 'Public key'), ) id = models.UUIDField(default=uuid.uuid4, primary_key=True) - name = models.CharField(max_length=128, unique=True, - verbose_name=_('Name')) + name = models.CharField(max_length=128, unique=True, verbose_name=_('Name')) username = models.CharField(max_length=16, verbose_name=_('Username')) - _password = models.CharField( - max_length=256, blank=True, verbose_name=_('Password')) - protocol = models.CharField( - max_length=16, choices=PROTOCOL_CHOICES, default='ssh', verbose_name=_('Protocol')) - _private_key = models.TextField( - max_length=8192, blank=True, verbose_name=_('SSH private key')) - _public_key = models.TextField( - max_length=8192, blank=True, verbose_name=_('SSH public key')) - auth_method = models.CharField(choices=AUTH_METHOD_CHOICES, default='K', - max_length=1, verbose_name=_('Auth method')) + _password = models.CharField(max_length=256, blank=True, verbose_name=_('Password')) + protocol = models.CharField(max_length=16, choices=PROTOCOL_CHOICES, default='ssh', verbose_name=_('Protocol')) + _private_key = models.TextField(max_length=8192, blank=True, verbose_name=_('SSH private key')) + _public_key = models.TextField(max_length=8192, blank=True, verbose_name=_('SSH public key')) + auth_method = models.CharField(choices=AUTH_METHOD_CHOICES, default='K', max_length=1, verbose_name=_('Auth method')) auto_push = models.BooleanField(default=True, verbose_name=_('Auto push')) - sudo = models.TextField( - max_length=4096, default='/sbin/ifconfig', verbose_name=_('Sudo')) - shell = models.CharField( - max_length=64, default='/bin/bash', verbose_name=_('Shell')) + sudo = models.TextField(default='/sbin/ifconfig', verbose_name=_('Sudo')) + shell = models.CharField(max_length=64, default='/bin/bash', verbose_name=_('Shell')) date_created = models.DateTimeField(auto_now_add=True) - created_by = models.CharField( - max_length=32, blank=True, verbose_name=_('Created by')) - comment = models.TextField( - max_length=128, blank=True, verbose_name=_('Comment')) + created_by = models.CharField(max_length=32, blank=True, verbose_name=_('Created by')) + comment = models.TextField(max_length=128, blank=True, verbose_name=_('Comment')) - def __unicode__(self): + def __str__(self): return self.name - __str__ = __unicode__ @property def password(self): @@ -182,9 +169,24 @@ class SystemUser(models.Model): def private_key(self, private_key_raw): self._private_key = signer.sign(private_key_raw) + @property + def private_key_file(self): + if not self.private_key: + return None + project_dir = settings.PROJECT_DIR + tmp_dir = os.path.join(project_dir, 'tmp') + key_name = md5(self._private_key.encode()).hexdigest() + key_path = os.path.join(tmp_dir, key_name) + if not os.path.exists(key_path): + self.private_key.write_private_key_file(key_path) + return key_path + @property def public_key(self): - return signer.unsign(self._public_key) + if self._public_key: + return signer.unsign(self._public_key) + else: + return None @public_key.setter def public_key(self, public_key_raw): @@ -213,7 +215,8 @@ class SystemUser(models.Model): 'shell': self.shell, 'sudo': self.sudo, 'password': self.password, - 'public_key': self.public_key + 'public_key': self.public_key, + 'private_key_file': self.private_key_file, } @property diff --git a/apps/assets/utils.py b/apps/assets/utils.py index 644011cf1..6e9493f3f 100644 --- a/apps/assets/utils.py +++ b/apps/assets/utils.py @@ -19,3 +19,32 @@ def test_admin_user_connective_manual(asset): def get_assets_by_id_list(id_list): return Asset.objects.filter(id__in=id_list) + + +def get_assets_by_hostname_list(hostname_list): + return Asset.objects.filter(hostname__in=hostname_list) + + +def get_asset_admin_user(user, asset): + if user.is_superuser: + return asset.admin_user + else: + msg = "{} have no permission for admin user".format(user.username) + raise PermissionError(msg) + + +def get_asset_system_user(user, asset, system_user_name): + from perms.utils import get_user_granted_assets + assets = get_user_granted_assets(user) + system_users = {system_user.name: system_user for system_user in assets.get(asset)} + + if system_user_name in system_users: + return system_users[system_user_name] + else: + msg = "{} have no permission for {}".format(user.name, system_user_name) + raise PermissionError(msg) + + +def get_assets_with_admin_by_hostname_list(hostname_list): + assets = Asset.objects.filter(hostname__in=hostname_list) + return [(asset, asset.admin_user) for asset in assets] diff --git a/apps/common/exceptions.py b/apps/common/exceptions.py new file mode 100644 index 000000000..3d98261b1 --- /dev/null +++ b/apps/common/exceptions.py @@ -0,0 +1,3 @@ +# -*- coding: utf-8 -*- +# + diff --git a/apps/ops/ansible/callback.py b/apps/ops/ansible/callback.py index b5dd3bbe1..ac46d82cf 100644 --- a/apps/ops/ansible/callback.py +++ b/apps/ops/ansible/callback.py @@ -9,65 +9,85 @@ class AdHocResultCallback(CallbackBase): """ def __init__(self, display=None): # result_raw example: { - # "ok": {"hostname": []}, - # "failed": {"hostname": []}, - # "unreachable: {"hostname": []}, - # "skipped": {"hostname": []}, + # "ok": {"hostname": [{"task_name": {},...],..}, + # "failed": {"hostname": ["task_name": {}..], ..}, + # "unreachable: {"hostname": ["task_name": {}, ..]}, + # "skipped": {"hostname": ["task_name": {}, ..], ..}, # } # results_summary example: { # "contacted": {"hostname",...}, - # "dark": {"hostname": ["error",...],}, + # "dark": {"hostname": [{"task_name": "error"},...],}, # } self.results_raw = dict(ok={}, failed={}, unreachable={}, skipped={}) self.results_summary = dict(contacted=set(), dark={}) super().__init__(display) - def gather_result(self, t, host, res): - if self.results_raw[t].get(host): - self.results_raw[t][host].append(res) - else: - self.results_raw[t][host] = [res] - self.clean_result(t, host, res) + def gather_result(self, t, res): + host = res._host.get_name() + task_name = res.task_name + task_result = res._result - def clean_result(self, t, host, res): + if self.results_raw[t].get(host): + self.results_raw[t][host].append({task_name: task_result}) + else: + self.results_raw[t][host] = [{task_name: task_result}] + self.clean_result(t, host, task_name, task_result) + + def clean_result(self, t, host, task_name, task_result): contacted = self.results_summary["contacted"] dark = self.results_summary["dark"] if t in ("ok", "skipped") and host not in dark: contacted.add(host) else: - dark[host].append(res) + if dark.get(host): + dark[host].append({task_name: task_result}) + else: + dark[host] = [{task_name: task_result}] if host in contacted: contacted.remove(dark) - def runner_on_ok(self, host, res): - self.gather_result("ok", host, res) + def v2_runner_on_failed(self, result, ignore_errors=False): + self.gather_result("failed", result) - def runner_on_failed(self, host, res, ignore_errors=False): - self.gather_result("failed", host, res) + def v2_runner_on_ok(self, result): + self.gather_result("ok", result) - def runner_on_unreachable(self, host, res): - self.gather_result("unreachable", host, res) + def v2_runner_on_skipped(self, result): + self.gather_result("skipped", result) - def runner_on_skipped(self, host, item=None): - self.gather_result("skipped", host, item) + def v2_runner_on_unreachable(self, result): + self.gather_result("unreachable", result) class CommandResultCallback(AdHocResultCallback): + """ + Command result callback + """ def __init__(self, display=None): + # results_command: { + # "cmd": "", + # "stderr": "", + # "stdout": "", + # "rc": 0, + # "delta": 0:0:0.123 + # } + # self.results_command = dict() super().__init__(display) - def gather_result(self, t, host, res): - super().gather_result(t, host, res) - self.gather_cmd(t, host, res) + def gather_result(self, t, res): + super().gather_result(t, res) + self.gather_cmd(t, res) - def gather_cmd(self, t, host, res): + def gather_cmd(self, t, res): + host = res._host.get_name() cmd = {} if t == "ok": - cmd['cmd'] = res.get('cmd') - cmd['stderr'] = res.get('stderr') - cmd['stdout'] = res.get('stdout') - cmd['rc'] = res.get('rc') + cmd['cmd'] = res._result.get('cmd') + cmd['stderr'] = res._result.get('stderr') + cmd['stdout'] = res._result.get('stdout') + cmd['rc'] = res._result.get('rc') + cmd['delta'] = res._result.get('delta') else: cmd['err'] = "Error: {}".format(res) self.results_command[host] = cmd diff --git a/apps/ops/ansible/runner.py b/apps/ops/ansible/runner.py index 616605114..ffe35935c 100644 --- a/apps/ops/ansible/runner.py +++ b/apps/ops/ansible/runner.py @@ -170,6 +170,10 @@ class AdHocRunner: "pattern: %s dose not match any hosts." % pattern ) + def set_option(self, k, v): + kwargs = {k: v} + self.options = self.options._replace(**kwargs) + def run(self, tasks, pattern, play_name='Ansible Ad-hoc', gather_facts='no'): """ :param tasks: [{'action': {'module': 'shell', 'args': 'ls'}, ...}, ] diff --git a/apps/ops/ansible/test_runner.py b/apps/ops/ansible/test_runner.py index 3e0426be2..ec1da5fa5 100644 --- a/apps/ops/ansible/test_runner.py +++ b/apps/ops/ansible/test_runner.py @@ -24,8 +24,8 @@ class TestAdHocRunner(unittest.TestCase): def test_run(self): tasks = [ - {"action": {"module": "shell", "args": "ls"}}, - {"action": {"module": "shell", "args": "whoami"}}, + {"action": {"module": "shell", "args": "ls"}, "name": "run_cmd"}, + {"action": {"module": "shell", "args": "whoami"}, "name": "run_whoami"}, ] ret = self.runner.run(tasks, "all") print(ret.results_summary) @@ -48,6 +48,7 @@ class TestCommandRunner(unittest.TestCase): def test_execute(self): res = self.runner.execute('ls', 'all') print(res.results_command) + print(res.results_raw) if __name__ == "__main__": diff --git a/apps/ops/api.py b/apps/ops/api.py index 878c29cdb..35eadd2ac 100644 --- a/apps/ops/api.py +++ b/apps/ops/api.py @@ -4,12 +4,12 @@ from rest_framework import viewsets from .hands import IsSuperUser -from .models import Playbook +from .models import AdHoc from .serializers import TaskSerializer class TaskViewSet(viewsets.ModelViewSet): - queryset = Playbook.objects.all() + queryset = AdHoc.objects.all() serializer_class = TaskSerializer permission_classes = (IsSuperUser,) diff --git a/apps/ops/models.py b/apps/ops/models.py index 2feabe731..f1238ee3f 100644 --- a/apps/ops/models.py +++ b/apps/ops/models.py @@ -1,56 +1,76 @@ # ~*~ coding: utf-8 ~*~ import logging -from collections import OrderedDict import json import uuid from django.db import models from django.utils.translation import ugettext_lazy as _ -__all__ = ["Playbook"] +__all__ = ["AdHoc", "History"] logger = logging.getLogger(__name__) class AdHoc(models.Model): - uuid = models.UUIDField(default=uuid.uuid4, primary_key=True) + id = models.UUIDField(default=uuid.uuid4, primary_key=True) name = models.CharField(max_length=128, blank=True, verbose_name=_('Name')) - tasks = models.TextField(verbose_name=_('Tasks')) # [{'name': 'task_name', 'module': '', 'args': ''}, ] - hosts = models.TextField(blank=True, null=True, verbose_name=_('Hosts')) # Asset inventory may be change - pattern = models.CharField(max_length=64, default='all', verbose_name=_('Playbook run pattern')) + + @property + def short_id(self): + return str(self.id).split('-')[-1] def __str__(self): - return "%s" % self.name + return self.name - def get_hosts_mapped_assets(self): - from assets.utils import get_assets_by_id_list - assets_id = [i for i in self.hosts.split(',')] - assets = get_assets_by_id_list(assets_id) - return assets + +class AdHocData(models.Model): + BECOME_METHOD_CHOICES = ( + ('sudo', 'sudo'), + ('su', 'su'), + ) + version = models.UUIDField(default=uuid.uuid4, primary_key=True) + subject = models.ForeignKey(AdHoc, on_delete=models.CASCADE) + _tasks = models.TextField(verbose_name=_('Tasks')) # [{'name': 'task_name', 'action': {'module': '', 'args': ''}, 'other..': ''}, ] + _hosts = models.TextField(blank=True, verbose_name=_('Hosts')) # ['hostname1', 'hostname2'] + run_as_admin = models.BooleanField(default=False, verbose_name=_('Run as admin')) + run_as = models.CharField(max_length=128, verbose_name=_("Run as")) + become = models.BooleanField(default=False, verbose_name=_("Become")) + become_method = models.CharField(choices=BECOME_METHOD_CHOICES, default='sudo', max_length=4) + become_user = models.CharField(default='root', max_length=64) + become_pass = models.CharField(default='', max_length=128) + pattern = models.CharField(max_length=64, default='', verbose_name=_('Pattern')) + created_by = models.CharField(max_length=64, verbose_name=_('Create by')) + date_created = models.DateTimeField(auto_created=True) @property - def inventory(self): - return [asset._to_secret_json() for asset in self.get_hosts_mapped_assets()] + def tasks(self): + return json.loads(self._tasks) + + @tasks.setter + def tasks(self, item): + self._tasks = json.dumps(item) @property - def module_args(self): - task_tuple = [] - for module, args in json.loads(self._modules_args, object_pairs_hook=OrderedDict).items(): - task_tuple.append((module, args)) - return task_tuple + def hosts(self): + return json.loads(self._hosts) - @module_args.setter - def module_args(self, task_tuple): - module_args_ = OrderedDict({}) - for module, args in task_tuple: - module_args_[module] = args - self._modules_args = json.dumps(module_args_) + @hosts.setter + def hosts(self, item): + self._hosts = json.dumps(item) + + @property + def short_version(self): + return str(self.version).split('-')[-1] + + def __str__(self): + return "{} of {}".format(self.subject.name, self.short_version) -class History(models.Model): +class AdHocHistory(models.Model): uuid = models.UUIDField(default=uuid.uuid4, primary_key=True) + adhoc = models.ForeignKey(AdHocData, on_delete=models.CASCADE) date_start = models.DateTimeField(auto_now_add=True, verbose_name=_('Start time')) date_finished = models.DateTimeField(blank=True, null=True, verbose_name=_('End time')) timedelta = models.FloatField(default=0.0, verbose_name=_('Time'), null=True) @@ -58,3 +78,10 @@ class History(models.Model): is_success = models.BooleanField(default=False, verbose_name=_('Is success')) result = models.TextField(blank=True, null=True, verbose_name=_('Playbook raw result')) summary = models.TextField(blank=True, null=True, verbose_name=_('Playbook summary')) + + @property + def short_id(self): + return str(self.id).split('-')[-1] + + def __str__(self): + return self.short_id diff --git a/apps/ops/serializers.py b/apps/ops/serializers.py index 21ba11c83..35727be20 100644 --- a/apps/ops/serializers.py +++ b/apps/ops/serializers.py @@ -2,12 +2,12 @@ from __future__ import unicode_literals from rest_framework import serializers -from .models import Playbook +from .models import AdHoc class TaskSerializer(serializers.ModelSerializer): class Meta: - model = Playbook + model = AdHoc fields = '__all__' diff --git a/apps/ops/utils.py b/apps/ops/utils.py index b479440cc..34fc79f0a 100644 --- a/apps/ops/utils.py +++ b/apps/ops/utils.py @@ -88,3 +88,14 @@ def is_uuid(s): else: return False + +def asset_to_dict(asset): + return asset.to_json() + + +def asset_to_dict_with_credential(asset): + return asset._to_secret_json() + + +def system_user_to_dict_with_credential(system_user): + return system_user._to_secret_json() diff --git a/apps/ops/views.py b/apps/ops/views.py index 06327e228..628bce065 100644 --- a/apps/ops/views.py +++ b/apps/ops/views.py @@ -9,13 +9,13 @@ from django.views.generic import ListView, DetailView, View from django.utils import timezone from django.shortcuts import redirect, reverse -from .models import Playbook +from .models import AdHoc, AdHocData, AdHocHistory from ops.tasks import rerun_task class TaskListView(ListView): paginate_by = settings.CONFIG.DISPLAY_PER_PAGE - model = Playbook + model = AdHoc ordering = ('-date_start',) context_object_name = 'task_list' template_name = 'ops/task_list.html' @@ -63,7 +63,7 @@ class TaskListView(ListView): class TaskDetailView(DetailView): - model = Playbook + model = AdHocHistory template_name = 'ops/task_detail.html' def get_context_data(self, **kwargs): diff --git a/apps/perms/utils.py b/apps/perms/utils.py index f7b05f759..b2c282f34 100644 --- a/apps/perms/utils.py +++ b/apps/perms/utils.py @@ -53,79 +53,6 @@ def get_user_group_granted_assets(user_group): return assets -# def get_user_granted_asset_groups_direct(user): -# """Return asset groups granted of the user direct nor inherit from user group -# -# :param user: Instance of :class: ``User`` -# :return: {asset_group: {system_user1, }, -# asset_group2: {system_user1, system_user2]} -# """ -# asset_groups = {} -# asset_permissions_direct = user.asset_permissions.all() -# -# for asset_permission in asset_permissions_direct: -# if not asset_permission.is_valid: -# continue -# for asset_group in asset_permission.asset_groups.all(): -# if asset_group in asset_groups: -# asset_groups[asset_group] |= set(asset_permission.system_users.all()) -# else: -# setattr(asset_group, 'inherited', False) -# asset_groups[asset_group] = set(asset_permission.system_users.all()) -# -# return asset_groups - - -# def get_user_granted_asset_groups_inherit_from_user_groups(user): -# """Return asset groups granted of the user and inherit from user group -# -# :param user: Instance of :class: ``User`` -# :return: {asset_group: {system_user1, }, -# asset_group2: {system_user1, system_user2]} -# """ -# asset_groups = {} -# user_groups = user.groups.all() -# asset_permissions = set() -# -# # Get asset permission list of user groups for this user -# for user_group in user_groups: -# asset_permissions |= set(user_group.asset_permissions.all()) -# -# # Get asset groups granted from user groups -# for asset_permission in asset_permissions: -# if not asset_permission.is_valid: -# continue -# for asset_group in asset_permission.asset_groups.all(): -# if asset_group in asset_groups: -# asset_groups[asset_group] |= set(asset_permission.system_users.all()) -# else: -# setattr(asset_group, 'inherited', True) -# asset_groups[asset_group] = set(asset_permission.system_users.all()) -# -# return asset_groups - - -# def get_user_granted_asset_groups(user): -# """Get user granted asset groups all, include direct and inherit from user group -# -# :param user: Instance of :class: ``User`` -# :return: {asset_group1: {system_user1, system_user2}, asset_group2: {...}} -# """ -# -# asset_groups_inherit_from_user_groups = \ -# get_user_granted_asset_groups_inherit_from_user_groups(user) -# asset_groups_direct = get_user_granted_asset_groups_direct(user) -# asset_groups = asset_groups_inherit_from_user_groups -# -# # Merge direct granted and inherit from user group -# for asset_group, system_users in asset_groups_direct.items(): -# if asset_group in asset_groups: -# asset_groups[asset_group] |= asset_groups_direct[asset_group] -# else: -# asset_groups[asset_group] = asset_groups_direct[asset_group] -# return asset_groups - - def get_user_granted_assets_direct(user): """Return assets granted of the user directly @@ -225,6 +152,22 @@ def get_user_asset_permissions(user): return direct_permissions | user_group_permissions +def get_user_granted_system_users(user): + """ + :param user: the user + :return: {"system_user": ["asset", "asset1"], "system_user": []} + """ + assets = get_user_granted_assets(user) + system_users_dict = {} + for asset, system_users in assets.items(): + for system_user in system_users: + if system_user in system_users_dict: + system_users_dict[system_user].append(asset) + else: + system_users_dict[system_user] = [asset] + return system_users_dict + + def get_user_groups_granted_in_asset(asset): pass