mirror of https://github.com/jumpserver/jumpserver
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
326 lines
11 KiB
326 lines
11 KiB
# ~*~ coding: utf-8 ~*~
|
|
|
|
from itertools import groupby
|
|
from celery import shared_task
|
|
from common.db.utils import get_object_if_need, get_objects
|
|
from django.utils.translation import ugettext as _
|
|
from django.db.models import Empty, Q
|
|
|
|
from common.utils import encrypt_password, get_logger
|
|
from assets.models import SystemUser, Asset, AuthBook
|
|
from orgs.utils import org_aware_func, tmp_to_root_org
|
|
from . import const
|
|
from .utils import clean_ansible_task_hosts, group_asset_by_platform
|
|
|
|
|
|
logger = get_logger(__file__)
|
|
__all__ = [
|
|
'push_system_user_util', 'push_system_user_to_assets',
|
|
'push_system_user_to_assets_manual', 'push_system_user_a_asset_manual',
|
|
'push_system_users_a_asset'
|
|
]
|
|
|
|
|
|
def _split_by_comma(raw: str):
|
|
try:
|
|
return [i.strip() for i in raw.split(',')]
|
|
except AttributeError:
|
|
return []
|
|
|
|
|
|
def _dump_args(args: dict):
|
|
return ' '.join([f'{k}={v}' for k, v in args.items() if v is not Empty])
|
|
|
|
|
|
def get_push_unixlike_system_user_tasks(system_user, username=None):
|
|
comment = system_user.name
|
|
|
|
if username is None:
|
|
username = system_user.username
|
|
|
|
if system_user.username_same_with_user:
|
|
from users.models import User
|
|
user = User.objects.filter(username=username).only('name', 'username').first()
|
|
if user:
|
|
comment = f'{system_user.name}[{str(user)}]'
|
|
|
|
password = system_user.password
|
|
public_key = system_user.public_key
|
|
|
|
groups = _split_by_comma(system_user.system_groups)
|
|
|
|
if groups:
|
|
groups = '"%s"' % ','.join(groups)
|
|
|
|
add_user_args = {
|
|
'name': username,
|
|
'shell': system_user.shell or Empty,
|
|
'state': 'present',
|
|
'home': system_user.home or Empty,
|
|
'expires': -1,
|
|
'groups': groups or Empty,
|
|
'comment': comment
|
|
}
|
|
|
|
tasks = [
|
|
{
|
|
'name': 'Add user {}'.format(username),
|
|
'action': {
|
|
'module': 'user',
|
|
'args': _dump_args(add_user_args),
|
|
}
|
|
},
|
|
{
|
|
'name': 'Add group {}'.format(username),
|
|
'action': {
|
|
'module': 'group',
|
|
'args': 'name={} state=present'.format(username),
|
|
}
|
|
}
|
|
]
|
|
if not system_user.home:
|
|
tasks.extend([
|
|
{
|
|
'name': 'Check home dir exists',
|
|
'action': {
|
|
'module': 'stat',
|
|
'args': 'path=/home/{}'.format(username)
|
|
},
|
|
'register': 'home_existed'
|
|
},
|
|
{
|
|
'name': "Set home dir permission",
|
|
'action': {
|
|
'module': 'file',
|
|
'args': "path=/home/{0} owner={0} group={0} mode=700".format(username)
|
|
},
|
|
'when': 'home_existed.stat.exists == true'
|
|
}
|
|
])
|
|
if password:
|
|
tasks.append({
|
|
'name': 'Set {} password'.format(username),
|
|
'action': {
|
|
'module': 'user',
|
|
'args': 'name={} shell={} state=present password={}'.format(
|
|
username, system_user.shell,
|
|
encrypt_password(password, salt="K3mIlKK"),
|
|
),
|
|
}
|
|
})
|
|
if public_key:
|
|
tasks.append({
|
|
'name': 'Set {} authorized key'.format(username),
|
|
'action': {
|
|
'module': 'authorized_key',
|
|
'args': "user={} state=present key='{}'".format(
|
|
username, public_key
|
|
)
|
|
}
|
|
})
|
|
if system_user.sudo:
|
|
sudo = system_user.sudo.replace('\r\n', '\n').replace('\r', '\n')
|
|
sudo_list = sudo.split('\n')
|
|
sudo_tmp = []
|
|
for s in sudo_list:
|
|
sudo_tmp.append(s.strip(','))
|
|
sudo = ','.join(sudo_tmp)
|
|
tasks.append({
|
|
'name': 'Set {} sudo setting'.format(username),
|
|
'action': {
|
|
'module': 'lineinfile',
|
|
'args': "dest=/etc/sudoers state=present regexp='^{0} ALL=' "
|
|
"line='{0} ALL=(ALL) NOPASSWD: {1}' "
|
|
"validate='visudo -cf %s'".format(username, sudo)
|
|
}
|
|
})
|
|
|
|
return tasks
|
|
|
|
|
|
def get_push_windows_system_user_tasks(system_user: SystemUser, username=None):
|
|
if username is None:
|
|
username = system_user.username
|
|
password = system_user.password
|
|
groups = {'Users', 'Remote Desktop Users'}
|
|
if system_user.system_groups:
|
|
groups.update(_split_by_comma(system_user.system_groups))
|
|
groups = ','.join(groups)
|
|
|
|
tasks = []
|
|
if not password:
|
|
logger.error("Error: no password found")
|
|
return tasks
|
|
|
|
if system_user.ad_domain:
|
|
logger.error('System user with AD domain do not support push.')
|
|
return tasks
|
|
|
|
task = {
|
|
'name': 'Add user {}'.format(username),
|
|
'action': {
|
|
'module': 'win_user',
|
|
'args': 'fullname={} '
|
|
'name={} '
|
|
'password={} '
|
|
'state=present '
|
|
'update_password=always '
|
|
'password_expired=no '
|
|
'password_never_expires=yes '
|
|
'groups="{}" '
|
|
'groups_action=add '
|
|
''.format(username, username, password, groups),
|
|
}
|
|
}
|
|
tasks.append(task)
|
|
return tasks
|
|
|
|
|
|
def get_push_system_user_tasks(system_user, platform="unixlike", username=None):
|
|
"""
|
|
:param system_user:
|
|
:param platform:
|
|
:param username: 当动态时,近推送某个
|
|
:return:
|
|
"""
|
|
get_task_map = {
|
|
"unixlike": get_push_unixlike_system_user_tasks,
|
|
"windows": get_push_windows_system_user_tasks,
|
|
}
|
|
get_tasks = get_task_map.get(platform, get_push_unixlike_system_user_tasks)
|
|
if not system_user.username_same_with_user:
|
|
return get_tasks(system_user)
|
|
tasks = []
|
|
# 仅推送这个username
|
|
if username is not None:
|
|
tasks.extend(get_tasks(system_user, username))
|
|
return tasks
|
|
users = system_user.users.all().values_list('username', flat=True)
|
|
print(_("System user is dynamic: {}").format(list(users)))
|
|
for _username in users:
|
|
tasks.extend(get_tasks(system_user, _username))
|
|
return tasks
|
|
|
|
|
|
@org_aware_func("system_user")
|
|
def push_system_user_util(system_user, assets, task_name, username=None):
|
|
from ops.utils import update_or_create_ansible_task
|
|
assets = clean_ansible_task_hosts(assets, system_user=system_user)
|
|
if not assets:
|
|
return {}
|
|
|
|
assets_sorted = sorted(assets, key=group_asset_by_platform)
|
|
platform_hosts = groupby(assets_sorted, key=group_asset_by_platform)
|
|
|
|
def run_task(_tasks, _hosts):
|
|
if not _tasks:
|
|
return
|
|
task, created = update_or_create_ansible_task(
|
|
task_name=task_name, hosts=_hosts, tasks=_tasks, pattern='all',
|
|
options=const.TASK_OPTIONS, run_as_admin=True,
|
|
)
|
|
task.run()
|
|
|
|
if system_user.username_same_with_user:
|
|
if username is None:
|
|
# 动态系统用户,但是没有指定 username
|
|
usernames = list(system_user.users.all().values_list('username', flat=True).distinct())
|
|
else:
|
|
usernames = [username]
|
|
else:
|
|
# 非动态系统用户指定 username 无效
|
|
assert username is None, 'Only Dynamic user can assign `username`'
|
|
usernames = [system_user.username]
|
|
|
|
for platform, _assets in platform_hosts:
|
|
_assets = list(_assets)
|
|
if not _assets:
|
|
continue
|
|
print(_("Start push system user for platform: [{}]").format(platform))
|
|
print(_("Hosts count: {}").format(len(_assets)))
|
|
|
|
id_asset_map = {_asset.id: _asset for _asset in _assets}
|
|
asset_ids = id_asset_map.keys()
|
|
no_special_auth = []
|
|
special_auth_set = set()
|
|
|
|
auth_books = AuthBook.objects.filter(asset_id__in=asset_ids).filter(
|
|
Q(username__in=usernames) | Q(systemuser__username__in=usernames)
|
|
).prefetch_related('systemuser')
|
|
|
|
for auth_book in auth_books:
|
|
auth_book.load_auth()
|
|
special_auth_set.add((auth_book.username, auth_book.asset_id))
|
|
|
|
for _username in usernames:
|
|
no_special_assets = []
|
|
for asset_id in asset_ids:
|
|
if (_username, asset_id) not in special_auth_set:
|
|
no_special_assets.append(id_asset_map[asset_id])
|
|
if no_special_assets:
|
|
no_special_auth.append((_username, no_special_assets))
|
|
|
|
for _username, no_special_assets in no_special_auth:
|
|
tasks = get_push_system_user_tasks(system_user, platform, username=_username)
|
|
run_task(tasks, no_special_assets)
|
|
|
|
for auth_book in auth_books:
|
|
system_user._merge_auth(auth_book)
|
|
tasks = get_push_system_user_tasks(system_user, platform, username=auth_book.username)
|
|
asset = id_asset_map[auth_book.asset_id]
|
|
run_task(tasks, [asset])
|
|
|
|
|
|
@shared_task(queue="ansible")
|
|
@tmp_to_root_org()
|
|
def push_system_user_to_assets_manual(system_user, username=None):
|
|
"""
|
|
将系统用户推送到与它关联的所有资产上
|
|
"""
|
|
system_user = get_object_if_need(SystemUser, system_user)
|
|
assets = system_user.get_related_assets()
|
|
task_name = _("Push system users to assets: {}").format(system_user.name)
|
|
return push_system_user_util(system_user, assets, task_name=task_name, username=username)
|
|
|
|
|
|
@shared_task(queue="ansible")
|
|
@tmp_to_root_org()
|
|
def push_system_user_a_asset_manual(system_user, asset, username=None):
|
|
"""
|
|
将系统用户推送到一个资产上
|
|
"""
|
|
# if username is None:
|
|
# username = system_user.username
|
|
task_name = _("Push system users to asset: {}({}) => {}").format(
|
|
system_user.name, username, asset
|
|
)
|
|
return push_system_user_util(system_user, [asset], task_name=task_name, username=username)
|
|
|
|
|
|
@shared_task(queue="ansible")
|
|
@tmp_to_root_org()
|
|
def push_system_users_a_asset(system_users, asset):
|
|
for system_user in system_users:
|
|
push_system_user_a_asset_manual(system_user, asset)
|
|
|
|
|
|
@shared_task(queue="ansible")
|
|
@tmp_to_root_org()
|
|
def push_system_user_to_assets(system_user_id, asset_ids, username=None):
|
|
"""
|
|
推送系统用户到指定的若干资产上
|
|
"""
|
|
system_user = SystemUser.objects.get(id=system_user_id)
|
|
assets = get_objects(Asset, asset_ids)
|
|
task_name = _("Push system users to assets: {}").format(system_user.name)
|
|
|
|
return push_system_user_util(system_user, assets, task_name, username=username)
|
|
|
|
# @shared_task
|
|
# @register_as_period_task(interval=3600)
|
|
# @after_app_ready_start
|
|
# @after_app_shutdown_clean_periodic
|
|
# def push_system_user_period():
|
|
# for system_user in SystemUser.objects.all():
|
|
# push_system_user_related_nodes(system_user)
|