Merge pull request #9688 from jumpserver/pr@dev@fix_job_center_account_not_in_perm

fix: 修复作业中心资产和用于没有过滤授权规则的问题
pull/9694/head
Eric_Lee 2023-02-22 18:51:04 +08:00 committed by GitHub
commit a4e920e410
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 85 additions and 4 deletions

View File

@ -139,8 +139,11 @@ class JMSInventory:
self.make_ssh_account_vars(host, asset, account, automation, protocols, platform, gateway) self.make_ssh_account_vars(host, asset, account, automation, protocols, platform, gateway)
return host return host
def get_asset_accounts(self, asset):
return list(asset.accounts.filter(is_active=True))
def select_account(self, asset): def select_account(self, asset):
accounts = list(asset.accounts.filter(is_active=True)) accounts = self.get_asset_accounts(asset)
if not accounts: if not accounts:
return None return None
account_selected = None account_selected = None

View File

@ -2,26 +2,90 @@ import json
import logging import logging
import os import os
import uuid import uuid
from collections import defaultdict
from celery import current_task from celery import current_task
from django.conf import settings from django.conf import settings
from django.db import models from django.db import models
from django.db.models import Q
from django.utils import timezone from django.utils import timezone
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
__all__ = ["Job", "JobExecution"] __all__ = ["Job", "JobExecution", "JMSPermedInventory"]
from simple_history.models import HistoricalRecords from simple_history.models import HistoricalRecords
from accounts.models import Account
from acls.models import CommandFilterACL from acls.models import CommandFilterACL
from assets.models import Asset
from ops.ansible import JMSInventory, AdHocRunner, PlaybookRunner from ops.ansible import JMSInventory, AdHocRunner, PlaybookRunner
from ops.mixin import PeriodTaskModelMixin from ops.mixin import PeriodTaskModelMixin
from ops.variables import * from ops.variables import *
from ops.const import Types, Modules, RunasPolicies, JobStatus from ops.const import Types, Modules, RunasPolicies, JobStatus
from orgs.mixins.models import JMSOrgBaseModel from orgs.mixins.models import JMSOrgBaseModel
from perms.models import AssetPermission
from perms.utils import UserPermAssetUtil
from terminal.notifications import CommandExecutionAlert from terminal.notifications import CommandExecutionAlert
def get_parent_keys(key, include_self=True):
keys = []
split_keys = key.split(':')
for i in range(len(split_keys)):
keys.append(':'.join(split_keys[:i + 1]))
if not include_self:
keys.pop()
return keys
class JMSPermedInventory(JMSInventory):
def __init__(self, assets, account_policy='privileged_first',
account_prefer='root,Administrator', host_callback=None, exclude_localhost=False, user=None):
super().__init__(assets, account_policy, account_prefer, host_callback, exclude_localhost)
self.user = user
self.assets_accounts_mapper = self.get_assets_accounts_mapper()
def get_asset_accounts(self, asset):
return self.assets_accounts_mapper.get(asset.id, [])
def get_assets_accounts_mapper(self):
mapper = defaultdict(set)
asset_ids = self.assets.values_list('id', flat=True)
asset_node_keys = Asset.nodes.through.objects \
.filter(asset_id__in=asset_ids) \
.values_list('asset_id', 'node__key')
node_asset_map = defaultdict(set)
for asset_id, node_key in asset_node_keys:
all_keys = get_parent_keys(node_key)
for key in all_keys:
node_asset_map[key].add(asset_id)
groups = self.user.groups.all()
perms = AssetPermission.objects \
.filter(date_expired__gte=timezone.now()) \
.filter(is_active=True) \
.filter(Q(users=self.user) | Q(user_groups__in=groups)) \
.filter(Q(assets__in=asset_ids) | Q(nodes__key__in=node_asset_map.keys())) \
.values_list('assets', 'nodes__key', 'accounts')
asset_permed_accounts_mapper = defaultdict(set)
for asset_id, node_key, accounts in perms:
if asset_id in asset_ids:
asset_permed_accounts_mapper[asset_id].update(accounts)
for my_asset in node_asset_map[node_key]:
asset_permed_accounts_mapper[my_asset].update(accounts)
accounts = Account.objects.filter(asset__in=asset_ids)
for account in accounts:
if account.asset_id not in asset_permed_accounts_mapper:
continue
permed_usernames = asset_permed_accounts_mapper[account.asset_id]
if "@ALL" in permed_usernames or account.username in permed_usernames:
mapper[account.asset_id].add(account)
return mapper
class Job(JMSOrgBaseModel, PeriodTaskModelMixin): class Job(JMSOrgBaseModel, PeriodTaskModelMixin):
name = models.CharField(max_length=128, null=True, verbose_name=_('Name')) name = models.CharField(max_length=128, null=True, verbose_name=_('Name'))
@ -90,7 +154,7 @@ class Job(JMSOrgBaseModel, PeriodTaskModelMixin):
@property @property
def inventory(self): def inventory(self):
return JMSInventory(self.assets.all(), self.runas_policy, self.runas) return JMSPermedInventory(self.assets.all(), self.runas_policy, self.runas, user=self.creator)
@property @property
def material(self): def material(self):
@ -339,7 +403,19 @@ class JobExecution(JMSOrgBaseModel):
'dangerous keyword \'{}\'\033[0m'.format(line['line'], line['file'], line['keyword'])) 'dangerous keyword \'{}\'\033[0m'.format(line['line'], line['file'], line['keyword']))
raise Exception("Playbook contains dangerous keywords") raise Exception("Playbook contains dangerous keywords")
def check_assets_perms(self):
all_permed_assets = UserPermAssetUtil(self.creator).get_all_assets()
has_permed_assets = set(self.current_job.assets.all()) & set(all_permed_assets)
for asset in self.current_job.assets.all():
if asset not in has_permed_assets:
print("\033[31mAsset {}({}) has no access permission\033[0m".format(asset.name, asset.address))
if self.current_job.assets.count() != len(has_permed_assets):
raise Exception("You do not have access rights to some assets")
def before_start(self): def before_start(self):
self.check_assets_perms()
if self.current_job.type == 'playbook': if self.current_job.type == 'playbook':
self.check_danger_keywords() self.check_danger_keywords()
if self.current_job.type == 'adhoc': if self.current_job.type == 'adhoc':

View File

@ -3,7 +3,7 @@ import uuid
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from rest_framework import serializers from rest_framework import serializers
from assets.models import Node from assets.models import Node, Asset
from perms.utils.user_perm import UserPermAssetUtil from perms.utils.user_perm import UserPermAssetUtil
from common.serializers.fields import ReadableHiddenField from common.serializers.fields import ReadableHiddenField
from ops.mixin import PeriodTaskSerializerMixin from ops.mixin import PeriodTaskSerializerMixin
@ -17,6 +17,8 @@ class JobSerializer(BulkOrgResourceModelSerializer, PeriodTaskSerializerMixin):
nodes = serializers.ListField(required=False, child=serializers.CharField()) nodes = serializers.ListField(required=False, child=serializers.CharField())
date_last_run = serializers.DateTimeField(label=_('Date last run'), read_only=True) date_last_run = serializers.DateTimeField(label=_('Date last run'), read_only=True)
name = serializers.CharField(label=_('Name'), max_length=128, allow_blank=True, required=False) name = serializers.CharField(label=_('Name'), max_length=128, allow_blank=True, required=False)
assets = serializers.PrimaryKeyRelatedField(label=_('Assets'), queryset=Asset.objects, many=True,
required=False)
def to_internal_value(self, data): def to_internal_value(self, data):
instant = data.get('instant', False) instant = data.get('instant', False)