Compare commits

...

10 Commits
v4.0.1 ... v2.3

13 changed files with 77 additions and 25 deletions

View File

@@ -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()

View File

@@ -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]

View File

@@ -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])

View File

@@ -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")

View File

@@ -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')

View File

@@ -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}
} }
} }
} }

View File

@@ -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):

View File

@@ -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(

View File

@@ -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)

View File

@@ -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
""" """

View File

@@ -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']

View File

@@ -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: 需要测试

View File

@@ -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