mirror of
https://github.com/jumpserver/jumpserver.git
synced 2025-12-15 08:32:48 +00:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1b5c957e48 | ||
|
|
d3c4634bed | ||
|
|
483c58ce23 | ||
|
|
b3d4ebd938 | ||
|
|
2f08f1258f | ||
|
|
9c9261e34d | ||
|
|
3353fbd06b | ||
|
|
628c034c53 | ||
|
|
8e6e8a0cbd | ||
|
|
493e61aa34 |
@@ -158,9 +158,11 @@ class AuthMixin:
|
|||||||
if update_fields:
|
if update_fields:
|
||||||
self.save(update_fields=update_fields)
|
self.save(update_fields=update_fields)
|
||||||
|
|
||||||
def has_special_auth(self, asset=None):
|
def has_special_auth(self, asset=None, username=None):
|
||||||
from .authbook import AuthBook
|
from .authbook import AuthBook
|
||||||
queryset = AuthBook.objects.filter(username=self.username)
|
if username is None:
|
||||||
|
username = self.username
|
||||||
|
queryset = AuthBook.objects.filter(username=username)
|
||||||
if asset:
|
if asset:
|
||||||
queryset = queryset.filter(asset=asset)
|
queryset = queryset.filter(asset=asset)
|
||||||
return queryset.exists()
|
return queryset.exists()
|
||||||
|
|||||||
@@ -160,6 +160,11 @@ class SystemUser(BaseUser):
|
|||||||
def is_need_test_asset_connective(self):
|
def is_need_test_asset_connective(self):
|
||||||
return self.protocol not in [self.PROTOCOL_MYSQL]
|
return self.protocol not in [self.PROTOCOL_MYSQL]
|
||||||
|
|
||||||
|
def has_special_auth(self, asset=None, username=None):
|
||||||
|
if username is None and self.username_same_with_user:
|
||||||
|
raise TypeError('System user is dynamic, username should be pass')
|
||||||
|
return super().has_special_auth(asset=asset, username=username)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def can_perm_to_asset(self):
|
def can_perm_to_asset(self):
|
||||||
return self.protocol not in [self.PROTOCOL_MYSQL]
|
return self.protocol not in [self.PROTOCOL_MYSQL]
|
||||||
|
|||||||
@@ -132,6 +132,7 @@ def get_push_windows_system_user_tasks(system_user, username=None):
|
|||||||
|
|
||||||
tasks = []
|
tasks = []
|
||||||
if not password:
|
if not password:
|
||||||
|
logger.error("Error: no password found")
|
||||||
return tasks
|
return tasks
|
||||||
task = {
|
task = {
|
||||||
'name': 'Add user {}'.format(username),
|
'name': 'Add user {}'.format(username),
|
||||||
@@ -207,14 +208,15 @@ def push_system_user_util(system_user, assets, task_name, username=None):
|
|||||||
print(_("Start push system user for platform: [{}]").format(platform))
|
print(_("Start push system user for platform: [{}]").format(platform))
|
||||||
print(_("Hosts count: {}").format(len(_hosts)))
|
print(_("Hosts count: {}").format(len(_hosts)))
|
||||||
|
|
||||||
if not system_user.has_special_auth():
|
# 如果没有特殊密码设置,就不需要单独推送某台机器了
|
||||||
|
if not system_user.has_special_auth(username=username):
|
||||||
logger.debug("System user not has special auth")
|
logger.debug("System user not has special auth")
|
||||||
tasks = get_push_system_user_tasks(system_user, platform, username=username)
|
tasks = get_push_system_user_tasks(system_user, platform, username=username)
|
||||||
run_task(tasks, _hosts)
|
run_task(tasks, _hosts)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
for _host in _hosts:
|
for _host in _hosts:
|
||||||
system_user.load_asset_special_auth(_host)
|
system_user.load_asset_special_auth(_host, username=username)
|
||||||
tasks = get_push_system_user_tasks(system_user, platform, username=username)
|
tasks = get_push_system_user_tasks(system_user, platform, username=username)
|
||||||
run_task(tasks, [_host])
|
run_task(tasks, [_host])
|
||||||
|
|
||||||
|
|||||||
@@ -125,6 +125,8 @@ class TreeService(Tree):
|
|||||||
|
|
||||||
def assets(self, nid):
|
def assets(self, nid):
|
||||||
node = self.get_node(nid)
|
node = self.get_node(nid)
|
||||||
|
if not node:
|
||||||
|
return set()
|
||||||
return node.data.get("assets", set())
|
return node.data.get("assets", set())
|
||||||
|
|
||||||
def valid_assets(self, nid):
|
def valid_assets(self, nid):
|
||||||
@@ -132,6 +134,8 @@ class TreeService(Tree):
|
|||||||
|
|
||||||
def all_assets(self, nid):
|
def all_assets(self, nid):
|
||||||
node = self.get_node(nid)
|
node = self.get_node(nid)
|
||||||
|
if not node:
|
||||||
|
return set()
|
||||||
if node.data is None:
|
if node.data is None:
|
||||||
node.data = {}
|
node.data = {}
|
||||||
all_assets = node.data.get("all_assets")
|
all_assets = node.data.get("all_assets")
|
||||||
|
|||||||
@@ -73,12 +73,12 @@ class SSOViewSet(AuthMixin, JmsGenericViewSet):
|
|||||||
token.save()
|
token.save()
|
||||||
except (ValueError, SSOToken.DoesNotExist):
|
except (ValueError, SSOToken.DoesNotExist):
|
||||||
self.send_auth_signal(success=False, reason='authkey_invalid')
|
self.send_auth_signal(success=False, reason='authkey_invalid')
|
||||||
return HttpResponseRedirect(reverse('authentication:login'))
|
return HttpResponseRedirect(next_url)
|
||||||
|
|
||||||
# 判断是否过期
|
# 判断是否过期
|
||||||
if (utcnow().timestamp() - token.date_created.timestamp()) > settings.AUTH_SSO_AUTHKEY_TTL:
|
if (utcnow().timestamp() - token.date_created.timestamp()) > settings.AUTH_SSO_AUTHKEY_TTL:
|
||||||
self.send_auth_signal(success=False, reason='authkey_timeout')
|
self.send_auth_signal(success=False, reason='authkey_timeout')
|
||||||
return HttpResponseRedirect(reverse('authentication:login'))
|
return HttpResponseRedirect(next_url)
|
||||||
|
|
||||||
user = token.user
|
user = token.user
|
||||||
login(self.request, user, 'authentication.backends.api.SSOAuthentication')
|
login(self.request, user, 'authentication.backends.api.SSOAuthentication')
|
||||||
|
|||||||
@@ -241,6 +241,9 @@ CACHES = {
|
|||||||
'host': CONFIG.REDIS_HOST,
|
'host': CONFIG.REDIS_HOST,
|
||||||
'port': CONFIG.REDIS_PORT,
|
'port': CONFIG.REDIS_PORT,
|
||||||
'db': CONFIG.REDIS_DB_CACHE,
|
'db': CONFIG.REDIS_DB_CACHE,
|
||||||
|
},
|
||||||
|
'OPTIONS': {
|
||||||
|
"REDIS_CLIENT_KWARGS": {"health_check_interval": 30}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,6 +65,8 @@ class AdHocResultCallback(CallbackMixin, CallbackModule, CMDCallBackModule):
|
|||||||
"""
|
"""
|
||||||
Task result Callback
|
Task result Callback
|
||||||
"""
|
"""
|
||||||
|
context = None
|
||||||
|
|
||||||
def clean_result(self, t, host, task_name, task_result):
|
def clean_result(self, t, host, task_name, task_result):
|
||||||
contacted = self.results_summary["contacted"]
|
contacted = self.results_summary["contacted"]
|
||||||
dark = self.results_summary["dark"]
|
dark = self.results_summary["dark"]
|
||||||
@@ -133,7 +135,11 @@ class AdHocResultCallback(CallbackMixin, CallbackModule, CMDCallBackModule):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
def set_play_context(self, context):
|
def set_play_context(self, context):
|
||||||
context.ssh_args = '-C -o ControlMaster=no'
|
# for k, v in context._attributes.items():
|
||||||
|
# print("{} ==> {}".format(k, v))
|
||||||
|
if self.context and isinstance(self.context, dict):
|
||||||
|
for k, v in self.context.items():
|
||||||
|
setattr(context, k, v)
|
||||||
|
|
||||||
|
|
||||||
class CommandResultCallback(AdHocResultCallback):
|
class CommandResultCallback(AdHocResultCallback):
|
||||||
|
|||||||
@@ -182,6 +182,13 @@ class AdHocRunner:
|
|||||||
_options.update(options)
|
_options.update(options)
|
||||||
return _options
|
return _options
|
||||||
|
|
||||||
|
def set_control_master_if_need(self, cleaned_tasks):
|
||||||
|
modules = [task.get('action', {}).get('module') for task in cleaned_tasks]
|
||||||
|
if {'ping', 'win_ping'} & set(modules):
|
||||||
|
self.results_callback.context = {
|
||||||
|
'ssh_args': '-C -o ControlMaster=no'
|
||||||
|
}
|
||||||
|
|
||||||
def run(self, tasks, pattern, play_name='Ansible Ad-hoc', gather_facts='no'):
|
def run(self, tasks, pattern, play_name='Ansible Ad-hoc', gather_facts='no'):
|
||||||
"""
|
"""
|
||||||
:param tasks: [{'action': {'module': 'shell', 'args': 'ls'}, ...}, ]
|
:param tasks: [{'action': {'module': 'shell', 'args': 'ls'}, ...}, ]
|
||||||
@@ -193,6 +200,7 @@ class AdHocRunner:
|
|||||||
self.check_pattern(pattern)
|
self.check_pattern(pattern)
|
||||||
self.results_callback = self.get_result_callback()
|
self.results_callback = self.get_result_callback()
|
||||||
cleaned_tasks = self.clean_tasks(tasks)
|
cleaned_tasks = self.clean_tasks(tasks)
|
||||||
|
self.set_control_master_if_need(cleaned_tasks)
|
||||||
context.CLIARGS = ImmutableDict(self.options)
|
context.CLIARGS = ImmutableDict(self.options)
|
||||||
|
|
||||||
play_source = dict(
|
play_source = dict(
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import uuid
|
import uuid
|
||||||
from functools import partial
|
from functools import partial
|
||||||
|
from itertools import chain
|
||||||
|
|
||||||
from django.db import models
|
from django.db import models
|
||||||
from django.db.models import signals
|
from django.db.models import signals
|
||||||
@@ -229,6 +230,16 @@ def _none2list(*args):
|
|||||||
return ([] if v is None else v for v in args)
|
return ([] if v is None else v for v in args)
|
||||||
|
|
||||||
|
|
||||||
|
def _users2pks_if_need(users, admins, auditors):
|
||||||
|
pks = []
|
||||||
|
for user in chain(users, admins, auditors):
|
||||||
|
if hasattr(user, 'pk'):
|
||||||
|
pks.append(user.pk)
|
||||||
|
else:
|
||||||
|
pks.append(user)
|
||||||
|
return pks
|
||||||
|
|
||||||
|
|
||||||
class UserRoleMapper(dict):
|
class UserRoleMapper(dict):
|
||||||
def __init__(self, container=set):
|
def __init__(self, container=set):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
@@ -266,7 +277,7 @@ class OrgMemeberManager(models.Manager):
|
|||||||
users, admins, auditors = _none2list(users, admins, auditors)
|
users, admins, auditors = _none2list(users, admins, auditors)
|
||||||
|
|
||||||
send = partial(signals.m2m_changed.send, sender=self.model, instance=org, reverse=False,
|
send = partial(signals.m2m_changed.send, sender=self.model, instance=org, reverse=False,
|
||||||
model=User, pk_set=[*users, *admins, *auditors], using=self.db)
|
model=User, pk_set=_users2pks_if_need(users, admins, auditors), using=self.db)
|
||||||
|
|
||||||
send(action="pre_remove")
|
send(action="pre_remove")
|
||||||
self.filter(org_id=org.id).filter(
|
self.filter(org_id=org.id).filter(
|
||||||
@@ -290,14 +301,14 @@ class OrgMemeberManager(models.Manager):
|
|||||||
)
|
)
|
||||||
|
|
||||||
oms_add = []
|
oms_add = []
|
||||||
for users, role in add_mapper:
|
for _users, _role in add_mapper:
|
||||||
for user in users:
|
for _user in _users:
|
||||||
if isinstance(user, models.Model):
|
if isinstance(_user, models.Model):
|
||||||
user = user.id
|
_user = _user.id
|
||||||
oms_add.append(self.model(org_id=org.id, user_id=user, role=role))
|
oms_add.append(self.model(org_id=org.id, user_id=_user, role=_role))
|
||||||
|
|
||||||
send = partial(signals.m2m_changed.send, sender=self.model, instance=org, reverse=False,
|
send = partial(signals.m2m_changed.send, sender=self.model, instance=org, reverse=False,
|
||||||
model=User, pk_set=[*users, *admins, *auditors], using=self.db)
|
model=User, pk_set=_users2pks_if_need(users, admins, auditors), using=self.db)
|
||||||
|
|
||||||
send(action='pre_add')
|
send(action='pre_add')
|
||||||
self.bulk_create(oms_add)
|
self.bulk_create(oms_add)
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
|
|
||||||
from common.permissions import IsOrgAdmin
|
from common.permissions import IsOrgAdmin
|
||||||
from orgs.mixins.api import OrgModelViewSet
|
from orgs.mixins.api import OrgBulkModelViewSet
|
||||||
from common.utils import get_object_or_none
|
from common.utils import get_object_or_none
|
||||||
from ..models import AssetPermission
|
from ..models import AssetPermission
|
||||||
from ..hands import (
|
from ..hands import (
|
||||||
@@ -17,7 +17,7 @@ __all__ = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
class AssetPermissionViewSet(OrgModelViewSet):
|
class AssetPermissionViewSet(OrgBulkModelViewSet):
|
||||||
"""
|
"""
|
||||||
资产授权列表的增删改查api
|
资产授权列表的增删改查api
|
||||||
"""
|
"""
|
||||||
|
|||||||
@@ -43,12 +43,14 @@ class AssetPermissionSerializer(BulkOrgResourceModelSerializer):
|
|||||||
model = AssetPermission
|
model = AssetPermission
|
||||||
mini_fields = ['id', 'name']
|
mini_fields = ['id', 'name']
|
||||||
small_fields = mini_fields + [
|
small_fields = mini_fields + [
|
||||||
'is_active', 'is_expired', 'is_valid', 'actions', 'created_by', 'date_created',
|
'is_active', 'is_expired', 'is_valid', 'actions',
|
||||||
'date_expired', 'date_start', 'comment'
|
'created_by', 'date_created', 'date_expired',
|
||||||
|
'date_start', 'comment'
|
||||||
]
|
]
|
||||||
m2m_fields = [
|
m2m_fields = [
|
||||||
'users', 'user_groups', 'assets', 'nodes', 'system_users',
|
'users', 'user_groups', 'assets', 'nodes', 'system_users',
|
||||||
'users_amount', 'user_groups_amount', 'assets_amount', 'nodes_amount', 'system_users_amount',
|
'users_amount', 'user_groups_amount', 'assets_amount',
|
||||||
|
'nodes_amount', 'system_users_amount',
|
||||||
]
|
]
|
||||||
fields = small_fields + m2m_fields
|
fields = small_fields + m2m_fields
|
||||||
read_only_fields = ['created_by', 'date_created']
|
read_only_fields = ['created_by', 'date_created']
|
||||||
|
|||||||
@@ -52,6 +52,19 @@ class UserViewSet(CommonApiMixin, UserQuerysetMixin, BulkModelViewSet):
|
|||||||
for user in users:
|
for user in users:
|
||||||
post_user_create.send(self.__class__, user=user)
|
post_user_create.send(self.__class__, user=user)
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def set_users_to_org(users, org_roles, update=False):
|
||||||
|
# 只有真实存在的组织才真正关联用户
|
||||||
|
if not current_org or not current_org.is_real():
|
||||||
|
return
|
||||||
|
for user, roles in zip(users, org_roles):
|
||||||
|
if update and roles is None:
|
||||||
|
continue
|
||||||
|
if not roles:
|
||||||
|
# 当前组织创建的用户,至少是该组织的`User`
|
||||||
|
roles = [ORG_ROLE.USER]
|
||||||
|
OrganizationMember.objects.set_user_roles(current_org, user, roles)
|
||||||
|
|
||||||
def perform_create(self, serializer):
|
def perform_create(self, serializer):
|
||||||
validated_data = serializer.validated_data
|
validated_data = serializer.validated_data
|
||||||
|
|
||||||
@@ -104,11 +117,7 @@ class UserViewSet(CommonApiMixin, UserQuerysetMixin, BulkModelViewSet):
|
|||||||
users = serializer.save()
|
users = serializer.save()
|
||||||
if isinstance(users, User):
|
if isinstance(users, User):
|
||||||
users = [users]
|
users = [users]
|
||||||
if current_org and current_org.is_real():
|
self.set_users_to_org(users, org_roles, update=True)
|
||||||
for user, roles in zip(users, org_roles):
|
|
||||||
if roles is not None:
|
|
||||||
# roles 是 `Node` 表明不需要更新
|
|
||||||
OrganizationMember.objects.set_user_roles(current_org, user, roles)
|
|
||||||
|
|
||||||
def perform_bulk_update(self, serializer):
|
def perform_bulk_update(self, serializer):
|
||||||
# TODO: 需要测试
|
# TODO: 需要测试
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ python-dateutil==2.6.1
|
|||||||
python-gssapi==0.6.4
|
python-gssapi==0.6.4
|
||||||
pytz==2018.3
|
pytz==2018.3
|
||||||
PyYAML==5.1
|
PyYAML==5.1
|
||||||
redis==3.2.0
|
redis==3.5.3
|
||||||
requests==2.22.0
|
requests==2.22.0
|
||||||
jms-storage==0.0.31
|
jms-storage==0.0.31
|
||||||
s3transfer==0.3.3
|
s3transfer==0.3.3
|
||||||
|
|||||||
Reference in New Issue
Block a user