Merge remote-tracking branch 'origin/dev' into dev

pull/915/head
q4speed 2018-01-10 16:42:57 +08:00
commit 7f3d32a876
84 changed files with 1096 additions and 1031 deletions

View File

@ -26,7 +26,7 @@ from .hands import IsSuperUser, IsValidUser, IsSuperUserOrAppUser, \
get_user_granted_assets get_user_granted_assets
from .models import AssetGroup, Asset, Cluster, SystemUser, AdminUser from .models import AssetGroup, Asset, Cluster, SystemUser, AdminUser
from . import serializers from . import serializers
from .tasks import update_asset_hardware_info_manual, test_admin_user_connectability_util, \ from .tasks import update_asset_hardware_info_manual, test_admin_user_connectability_manual, \
test_asset_connectability_manual, push_system_user_to_cluster_assets_manual, \ test_asset_connectability_manual, push_system_user_to_cluster_assets_manual, \
test_system_user_connectability_manual test_system_user_connectability_manual
@ -40,18 +40,14 @@ class AssetViewSet(IDInFilterMixin, BulkModelViewSet):
""" """
queryset = Asset.objects.all() queryset = Asset.objects.all()
serializer_class = serializers.AssetSerializer serializer_class = serializers.AssetSerializer
permission_classes = (IsValidUser,) permission_classes = (IsSuperUserOrAppUser,)
def get_queryset(self): def get_queryset(self):
if self.request.user.is_superuser or self.request.user.is_app: queryset = super().get_queryset()
queryset = super().get_queryset()
else:
assets_granted = get_user_granted_assets(self.request.user)
queryset = self.queryset.filter(id__in=[asset.id for asset in assets_granted])
cluster_id = self.request.query_params.get('cluster_id') cluster_id = self.request.query_params.get('cluster_id')
asset_group_id = self.request.query_params.get('asset_group_id') asset_group_id = self.request.query_params.get('asset_group_id')
admin_user_id = self.request.query_params.get('admin_user_id') admin_user_id = self.request.query_params.get('admin_user_id')
system_user_id = self.request.query_params.get('system_user_id')
if cluster_id: if cluster_id:
queryset = queryset.filter(cluster__id=cluster_id) queryset = queryset.filter(cluster__id=cluster_id)
@ -62,6 +58,23 @@ class AssetViewSet(IDInFilterMixin, BulkModelViewSet):
assets_direct = [asset.id for asset in admin_user.asset_set.all()] assets_direct = [asset.id for asset in admin_user.asset_set.all()]
clusters = [cluster.id for cluster in admin_user.cluster_set.all()] clusters = [cluster.id for cluster in admin_user.cluster_set.all()]
queryset = queryset.filter(Q(cluster__id__in=clusters)|Q(id__in=assets_direct)) queryset = queryset.filter(Q(cluster__id__in=clusters)|Q(id__in=assets_direct))
if system_user_id:
system_user = get_object_or_404(SystemUser, id=system_user_id)
clusters = system_user.get_clusters()
queryset = queryset.filter(cluster__in=clusters)
return queryset
class UserAssetListView(generics.ListAPIView):
queryset = Asset.objects.all()
serializer_class = serializers.AssetSerializer
permission_classes = (IsValidUser,)
def get_queryset(self):
assets_granted = get_user_granted_assets(self.request.user)
queryset = self.queryset.filter(
id__in=[asset.id for asset in assets_granted]
)
return queryset return queryset
@ -99,15 +112,6 @@ class GroupAddAssetsApi(generics.UpdateAPIView):
return Response({'error': serializer.errors}, status=400) return Response({'error': serializer.errors}, status=400)
class ClusterUpdateAssetsApi(generics.RetrieveUpdateAPIView):
"""
Cluster update asset member
"""
queryset = Cluster.objects.all()
serializer_class = serializers.ClusterUpdateAssetsSerializer
permission_classes = (IsSuperUser,)
class ClusterViewSet(IDInFilterMixin, BulkModelViewSet): class ClusterViewSet(IDInFilterMixin, BulkModelViewSet):
""" """
Cluster api set, for add,delete,update,list,retrieve resource Cluster api set, for add,delete,update,list,retrieve resource
@ -117,7 +121,6 @@ class ClusterViewSet(IDInFilterMixin, BulkModelViewSet):
permission_classes = (IsSuperUser,) permission_classes = (IsSuperUser,)
# TOdo
class ClusterTestAssetsAliveApi(generics.RetrieveAPIView): class ClusterTestAssetsAliveApi(generics.RetrieveAPIView):
""" """
Test cluster asset can connect using admin user or not Test cluster asset can connect using admin user or not
@ -127,6 +130,9 @@ class ClusterTestAssetsAliveApi(generics.RetrieveAPIView):
def retrieve(self, request, *args, **kwargs): def retrieve(self, request, *args, **kwargs):
cluster = self.get_object() cluster = self.get_object()
admin_user = cluster.admin_user
test_admin_user_connectability_manual.delay(admin_user)
return Response("Task has been send, seen left assets status")
class ClusterAddAssetsApi(generics.UpdateAPIView): class ClusterAddAssetsApi(generics.UpdateAPIView):
@ -256,7 +262,7 @@ class AdminUserTestConnectiveApi(generics.RetrieveAPIView):
def retrieve(self, request, *args, **kwargs): def retrieve(self, request, *args, **kwargs):
admin_user = self.get_object() admin_user = self.get_object()
test_admin_user_connectability_util.delay(admin_user) test_admin_user_connectability_manual.delay(admin_user)
return Response({"msg": "Task created"}) return Response({"msg": "Task created"})

View File

@ -36,7 +36,7 @@ class AssetCreateForm(forms.ModelForm):
def clean_admin_user(self): def clean_admin_user(self):
cluster = self.cleaned_data.get('cluster') cluster = self.cleaned_data.get('cluster')
admin_user = self.cleaned_data.get('admin_user') admin_user = self.cleaned_data.get('admin_user')
if not cluster.admin_user and not admin_user: if not admin_user and (cluster and not cluster.admin_user):
raise forms.ValidationError(_("You need set a admin user if cluster not have")) raise forms.ValidationError(_("You need set a admin user if cluster not have"))
return self.cleaned_data['admin_user'] return self.cleaned_data['admin_user']
@ -64,7 +64,7 @@ class AssetUpdateForm(forms.ModelForm):
def clean_admin_user(self): def clean_admin_user(self):
cluster = self.cleaned_data.get('cluster') cluster = self.cleaned_data.get('cluster')
admin_user = self.cleaned_data.get('admin_user') admin_user = self.cleaned_data.get('admin_user')
if not cluster.admin_user and not admin_user: if not admin_user and (cluster and not cluster.admin_user):
raise forms.ValidationError(_("You need set a admin user if cluster not have")) raise forms.ValidationError(_("You need set a admin user if cluster not have"))
return self.cleaned_data['admin_user'] return self.cleaned_data['admin_user']
@ -124,20 +124,25 @@ class AssetGroupForm(forms.ModelForm):
label=_('Asset'), label=_('Asset'),
required=False, required=False,
widget=forms.SelectMultiple( widget=forms.SelectMultiple(
attrs={'class': 'select2', 'data-placeholder': _('Select assets')}) attrs={'class': 'select2', 'data-placeholder': _('Select assets')}
) )
)
def __init__(self, *args, **kwargs): def __init__(self, **kwargs):
if kwargs.get('instance', None): instance = kwargs.get('instance')
if instance:
initial = kwargs.get('initial', {}) initial = kwargs.get('initial', {})
initial['assets'] = kwargs['instance'].assets.all() initial.update({
super(AssetGroupForm, self).__init__(*args, **kwargs) 'assets': instance.assets.all(),
})
kwargs['initial'] = initial
super().__init__(**kwargs)
def _save_m2m(self): def save(self, commit=True):
super(AssetGroupForm, self)._save_m2m() group = super().save(commit=commit)
assets = self.cleaned_data['assets'] assets= self.cleaned_data['assets']
self.instance.assets.clear() group.assets.set(assets)
self.instance.assets.add(*tuple(assets)) return group
class Meta: class Meta:
model = AssetGroup model = AssetGroup
@ -150,10 +155,19 @@ class AssetGroupForm(forms.ModelForm):
class ClusterForm(forms.ModelForm): class ClusterForm(forms.ModelForm):
system_users = forms.ModelMultipleChoiceField(
queryset=SystemUser.objects.all(),
widget=forms.SelectMultiple(
attrs={'class': 'select2', 'data-placeholder': _('Select system users')}
),
label=_('System users'),
required=False,
help_text=_("Selected system users will be create at cluster assets"),
)
class Meta: class Meta:
model = Cluster model = Cluster
fields = ['name', "bandwidth", "operator", 'contact', 'admin_user', fields = ['name', "bandwidth", "operator", 'contact', 'admin_user', 'system_users',
'phone', 'address', 'intranet', 'extranet', 'comment'] 'phone', 'address', 'intranet', 'extranet', 'comment']
widgets = { widgets = {
'name': forms.TextInput(attrs={'placeholder': _('Name')}), 'name': forms.TextInput(attrs={'placeholder': _('Name')}),
@ -162,9 +176,21 @@ class ClusterForm(forms.ModelForm):
} }
help_texts = { help_texts = {
'name': '* required', 'name': '* required',
'admin_user': 'The assets of this cluster will use this admin user as his admin user', 'admin_user': _("Cluster level admin user"),
} }
def __init__(self, *args, **kwargs):
if kwargs.get('instance', None):
initial = kwargs.get('initial', {})
initial['system_users'] = kwargs['instance'].systemuser_set.all()
super().__init__(*args, **kwargs)
def save(self, commit=True):
instance = super().save(commit=commit)
system_users = self.cleaned_data['system_users']
instance.systemuser_set.set(system_users)
return instance
class AdminUserForm(forms.ModelForm): class AdminUserForm(forms.ModelForm):
# Form field name can not start with `_`, so redefine it, # Form field name can not start with `_`, so redefine it,
@ -172,9 +198,10 @@ class AdminUserForm(forms.ModelForm):
widget=forms.PasswordInput, max_length=128, widget=forms.PasswordInput, max_length=128,
strip=True, required=False, strip=True, required=False,
help_text=_('Password or private key password'), help_text=_('Password or private key password'),
label=_("Password"),
) )
# Need use upload private key file except paste private key content # Need use upload private key file except paste private key content
private_key_file = forms.FileField(required=False) private_key_file = forms.FileField(required=False, label=_("Private key"))
def save(self, commit=True): def save(self, commit=True):
# Because we define custom field, so we need rewrite :method: `save` # Because we define custom field, so we need rewrite :method: `save`
@ -204,12 +231,14 @@ class AdminUserForm(forms.ModelForm):
return private_key_file return private_key_file
def clean(self): def clean(self):
super().clean()
password = self.cleaned_data['password'] password = self.cleaned_data['password']
private_key_file = self.cleaned_data.get('private_key_file', '') private_key_file = self.cleaned_data.get('private_key_file', '')
if not self.instance and not (password or private_key_file): if not password and not private_key_file:
raise forms.ValidationError( raise forms.ValidationError(_(
_('Password and private key file must be input one')) 'Password and private key file must be input one'
))
class Meta: class Meta:
model = AdminUser model = AdminUser
@ -229,9 +258,10 @@ class SystemUserForm(forms.ModelForm):
# Admin user assets define, let user select, save it in form not in view # Admin user assets define, let user select, save it in form not in view
auto_generate_key = forms.BooleanField(initial=True, required=False) auto_generate_key = forms.BooleanField(initial=True, required=False)
# Form field name can not start with `_`, so redefine it, # Form field name can not start with `_`, so redefine it,
password = forms.CharField(widget=forms.PasswordInput, required=False, max_length=128, strip=True) password = forms.CharField(widget=forms.PasswordInput, required=False,
max_length=128, strip=True, label=_("Password"))
# Need use upload private key file except paste private key content # Need use upload private key file except paste private key content
private_key_file = forms.FileField(required=False) private_key_file = forms.FileField(required=False, label=_("Private key"))
def save(self, commit=True): def save(self, commit=True):
# Because we define custom field, so we need rewrite :method: `save` # Because we define custom field, so we need rewrite :method: `save`
@ -278,15 +308,18 @@ class SystemUserForm(forms.ModelForm):
'name': forms.TextInput(attrs={'placeholder': _('Name')}), 'name': forms.TextInput(attrs={'placeholder': _('Name')}),
'username': forms.TextInput(attrs={'placeholder': _('Username')}), 'username': forms.TextInput(attrs={'placeholder': _('Username')}),
'cluster': forms.SelectMultiple( 'cluster': forms.SelectMultiple(
attrs={'class': 'select2', attrs={
'data-placeholder': _(' Select clusters')}), 'class': 'select2',
'data-placeholder': _(' Select clusters')
}
),
} }
help_texts = { help_texts = {
'name': '* required', 'name': '* required',
'username': '* required', 'username': '* required',
'cluster': 'If auto push checked, system user will be create at cluster assets', 'cluster': _('If auto push checked, system user will be create at cluster assets'),
'auto_push': 'Auto push system user to asset', 'auto_push': _('Auto push system user to asset'),
'priority': 'High level will be using login asset as default, if user was granted more than 2 system user', 'priority': _('High level will be using login asset as default, if user was granted more than 2 system user'),
} }

View File

@ -18,6 +18,16 @@ __all__ = ['Asset']
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
def default_cluster():
from .cluster import Cluster
name = "Default"
defaults = {"name": name}
cluster, created = Cluster.objects.get_or_create(
defaults=defaults, name=name
)
return cluster.id
class Asset(models.Model): class Asset(models.Model):
# Todo: Move them to settings # Todo: Move them to settings
STATUS_CHOICES = ( STATUS_CHOICES = (
@ -44,7 +54,7 @@ class Asset(models.Model):
hostname = models.CharField(max_length=128, unique=True, verbose_name=_('Hostname')) hostname = models.CharField(max_length=128, unique=True, verbose_name=_('Hostname'))
port = models.IntegerField(default=22, verbose_name=_('Port')) port = models.IntegerField(default=22, verbose_name=_('Port'))
groups = models.ManyToManyField(AssetGroup, blank=True, related_name='assets', verbose_name=_('Asset groups')) groups = models.ManyToManyField(AssetGroup, blank=True, related_name='assets', verbose_name=_('Asset groups'))
cluster = models.ForeignKey(Cluster, blank=True, null=True, related_name='assets', on_delete=models.SET_NULL, verbose_name=_('Cluster')) cluster = models.ForeignKey(Cluster, related_name='assets', default=default_cluster, on_delete=models.SET_DEFAULT, verbose_name=_('Cluster'))
is_active = models.BooleanField(default=True, verbose_name=_('Is active')) 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'),) 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'),) env = models.CharField(choices=ENV_CHOICES, max_length=8, blank=True, null=True, default='Prod', verbose_name=_('Asset environment'),)
@ -158,6 +168,7 @@ class Asset(models.Model):
class Meta: class Meta:
unique_together = ('ip', 'port') unique_together = ('ip', 'port')
verbose_name = _("Asset")
@classmethod @classmethod
def generate_fake(cls, count=100): def generate_fake(cls, count=100):

View File

@ -37,6 +37,7 @@ class Cluster(models.Model):
class Meta: class Meta:
ordering = ['name'] ordering = ['name']
verbose_name = _("Cluster")
@classmethod @classmethod
def generate_fake(cls, count=5): def generate_fake(cls, count=5):

View File

@ -27,6 +27,7 @@ class AssetGroup(models.Model):
class Meta: class Meta:
ordering = ['name'] ordering = ['name']
verbose_name = _("Asset group")
@classmethod @classmethod
def initial(cls): def initial(cls):

View File

@ -81,7 +81,11 @@ class AssetUser(models.Model):
@property @property
def public_key(self): def public_key(self):
return signer.unsign(self._public_key) key = signer.unsign(self._public_key)
if key:
return key
else:
return None
@property @property
def public_key_obj(self): def public_key_obj(self):
@ -170,7 +174,6 @@ class AdminUser(AssetUser):
info = None info = None
return info return info
def get_related_assets(self): def get_related_assets(self):
assets = [] assets = []
for cluster in self.cluster_set.all(): for cluster in self.cluster_set.all():
@ -184,6 +187,7 @@ class AdminUser(AssetUser):
class Meta: class Meta:
ordering = ['name'] ordering = ['name']
verbose_name = _("Admin user")
@classmethod @classmethod
def generate_fake(cls, count=10): def generate_fake(cls, count=10):
@ -224,7 +228,7 @@ class SystemUser(AssetUser):
def get_clusters_assets(self): def get_clusters_assets(self):
from .asset import Asset from .asset import Asset
clusters = self.cluster.all() clusters = self.get_clusters()
return Asset.objects.filter(cluster__in=clusters) return Asset.objects.filter(cluster__in=clusters)
def get_clusters(self): def get_clusters(self):
@ -262,6 +266,7 @@ class SystemUser(AssetUser):
class Meta: class Meta:
ordering = ['name'] ordering = ['name']
verbose_name = _("System user")
@classmethod @classmethod
def generate_fake(cls, count=10): def generate_fake(cls, count=10):

View File

@ -64,6 +64,7 @@ class AdminUserSerializer(serializers.ModelSerializer):
""" """
assets_amount = serializers.SerializerMethodField() assets_amount = serializers.SerializerMethodField()
unreachable_amount = serializers.SerializerMethodField() unreachable_amount = serializers.SerializerMethodField()
reachable_amount = serializers.SerializerMethodField()
class Meta: class Meta:
model = AdminUser model = AdminUser
@ -75,7 +76,15 @@ class AdminUserSerializer(serializers.ModelSerializer):
if data: if data:
return len(data.get('dark')) return len(data.get('dark'))
else: else:
return 'Unknown' return 0
@staticmethod
def get_reachable_amount(obj):
data = cache.get(ADMIN_USER_CONN_CACHE_KEY.format(obj.name))
if data:
return len(data.get('contacted'))
else:
return 0
@staticmethod @staticmethod
def get_assets_amount(obj): def get_assets_amount(obj):
@ -183,7 +192,7 @@ class AssetGrantedSerializer(serializers.ModelSerializer):
class Meta(object): class Meta(object):
model = Asset model = Asset
fields = ("id", "hostname", "ip", "port", "system_users_granted", fields = ("id", "hostname", "ip", "port", "system_users_granted",
"is_inherited", "is_active", "system_users_join", "is_inherited", "is_active", "system_users_join", "os",
"platform", "comment",) "platform", "comment",)
@staticmethod @staticmethod

View File

@ -27,16 +27,17 @@ def test_asset_conn_on_created(asset):
def push_cluster_system_users_to_asset(asset): def push_cluster_system_users_to_asset(asset):
logger.info("Push cluster system user to asset: {}".format(asset)) if asset.cluster:
task_name = _("Push cluster system users to asset") logger.info("Push cluster system user to asset: {}".format(asset))
system_users = asset.cluster.systemuser_set.all() task_name = _("Push cluster system users to asset")
push_system_user_util.delay(system_users, [asset], task_name) system_users = asset.cluster.systemuser_set.all()
push_system_user_util.delay(system_users, [asset], task_name)
@receiver(post_save, sender=Asset, dispatch_uid="my_unique_identifier") @receiver(post_save, sender=Asset, dispatch_uid="my_unique_identifier")
def on_asset_created(sender, instance=None, created=False, **kwargs): def on_asset_created(sender, instance=None, created=False, **kwargs):
if instance and created: if instance and created:
logger.info("Asset `` create signal received".format(instance)) logger.info("Asset `{}` create signal received".format(instance))
update_asset_hardware_info_on_created(instance) update_asset_hardware_info_on_created(instance)
test_asset_conn_on_created(instance) test_asset_conn_on_created(instance)
push_cluster_system_users_to_asset(instance) push_cluster_system_users_to_asset(instance)
@ -77,7 +78,7 @@ def on_system_user_created_or_updated(sender, instance=None, **kwargs):
@receiver(post_init, sender=Cluster, dispatch_uid="my_unique_identifier") @receiver(post_init, sender=Cluster, dispatch_uid="my_unique_identifier")
def on_cluster_init(sender, instance, **kwargs): def on_cluster_init(sender, instance, **kwargs):
instance.__original_assets = tuple(instance.assets.values_list('pk', flat=True)) instance.__original_assets = tuple(instance.assets.values_list('pk', flat=True))
# instance.__origin_system_users = tuple(instance.systemuser_set.all()) instance.__origin_system_users = tuple(instance.systemuser_set.values_list('pk', flat=True))
@receiver(post_save, sender=Cluster, dispatch_uid="my_unique_identifier") @receiver(post_save, sender=Cluster, dispatch_uid="my_unique_identifier")
@ -103,11 +104,11 @@ def on_cluster_assets_changed(sender, instance, **kwargs):
push_system_user_util.delay(system_users, assets, task_name) push_system_user_util.delay(system_users, assets, task_name)
@receiver(post_save, sender=Cluster, dispatch_uid="my_unique_identifier") @receiver(post_save, sender=Cluster, dispatch_uid="my_unique_identifier2")
def on_cluster_system_user_changed(sender, instance, **kwargs): def on_cluster_system_user_changed(sender, instance, **kwargs):
system_users_origin = instance.__origin_system_users system_users_origin = instance.__origin_system_users
system_user_new = instance.systemuser_set.values_list('pk', flat=True) system_user_new = instance.systemuser_set.values_list('pk', flat=True)
system_users_added = set(system_user_new) - system_users_origin system_users_added = set(system_user_new) - set(system_users_origin)
if system_users_added: if system_users_added:
logger.debug("Receive cluster change system users signal") logger.debug("Receive cluster change system users signal")
system_users = [] system_users = []

View File

@ -25,7 +25,7 @@ disk_pattern = re.compile(r'^hd|sd|xvd')
@shared_task @shared_task
def set_assets_hardware_info(result, **kwargs): def set_assets_hardware_info(result, **kwargs):
""" """
Unsing ops task run result, to update asset info Using ops task run result, to update asset info
@shared_task must be exit, because we using it as a task callback, is must @shared_task must be exit, because we using it as a task callback, is must
be a celery task also be a celery task also
@ -209,8 +209,11 @@ def test_asset_connectability_util(asset, task_name=None):
from ops.utils import update_or_create_ansible_task from ops.utils import update_or_create_ansible_task
if task_name is None: if task_name is None:
task_name = "Test asset connectability" task_name = _("Test asset connectability")
hosts = [asset.hostname] hosts = [asset.hostname]
if not hosts:
logger.info("No hosts, passed")
return {}
tasks = const.TEST_ADMIN_USER_CONN_TASKS tasks = const.TEST_ADMIN_USER_CONN_TASKS
task, created = update_or_create_ansible_task( task, created = update_or_create_ansible_task(
task_name=task_name, hosts=hosts, tasks=tasks, pattern='all', task_name=task_name, hosts=hosts, tasks=tasks, pattern='all',
@ -262,6 +265,9 @@ def test_system_user_connectability_util(system_user, task_name):
assets = system_user.get_clusters_assets() assets = system_user.get_clusters_assets()
hosts = [asset.hostname for asset in assets] hosts = [asset.hostname for asset in assets]
tasks = const.TEST_SYSTEM_USER_CONN_TASKS tasks = const.TEST_SYSTEM_USER_CONN_TASKS
if not hosts:
logger.info("No hosts, passed")
return {}
task, created = update_or_create_ansible_task( task, created = update_or_create_ansible_task(
task_name, hosts=hosts, tasks=tasks, pattern='all', task_name, hosts=hosts, tasks=tasks, pattern='all',
options=const.TASK_OPTIONS, options=const.TASK_OPTIONS,
@ -274,7 +280,7 @@ def test_system_user_connectability_util(system_user, task_name):
@shared_task @shared_task
def test_system_user_connectability_manual(system_user): def test_system_user_connectability_manual(system_user):
task_name = "Test system user connectability: {}".format(system_user) task_name = _("Test system user connectability: {}").format(system_user)
return test_system_user_connectability_util(system_user, task_name) return test_system_user_connectability_util(system_user, task_name)
@ -303,6 +309,10 @@ def test_system_user_connectability_period():
#### Push system user tasks #### #### Push system user tasks ####
def get_push_system_user_tasks(system_user): def get_push_system_user_tasks(system_user):
# Set root as system user is dangerous
if system_user.username == "root":
return []
tasks = [ tasks = [
{ {
'name': 'Add user {}'.format(system_user.username), 'name': 'Add user {}'.format(system_user.username),
@ -310,7 +320,7 @@ def get_push_system_user_tasks(system_user):
'module': 'user', 'module': 'user',
'args': 'name={} shell={} state=present password={}'.format( 'args': 'name={} shell={} state=present password={}'.format(
system_user.username, system_user.shell, system_user.username, system_user.shell,
encrypt_password(system_user.password), encrypt_password(system_user.password, salt="K3mIlKK"),
), ),
} }
}, },
@ -346,11 +356,14 @@ def push_system_user_util(system_users, assets, task_name):
for system_user in system_users: for system_user in system_users:
tasks.extend(get_push_system_user_tasks(system_user)) tasks.extend(get_push_system_user_tasks(system_user))
print("Task: ", tasks)
if not tasks: if not tasks:
return logger.info("Not tasks, passed")
return {}
hosts = [asset.hostname for asset in assets] hosts = [asset.hostname for asset in assets]
if not hosts:
logger.info("Not hosts, passed")
return {}
task, created = update_or_create_ansible_task( task, created = update_or_create_ansible_task(
task_name=task_name, hosts=hosts, tasks=tasks, pattern='all', task_name=task_name, hosts=hosts, tasks=tasks, pattern='all',
options=const.TASK_OPTIONS, run_as_admin=True, created_by='System' options=const.TASK_OPTIONS, run_as_admin=True, created_by='System'
@ -381,8 +394,8 @@ def push_system_user_period():
for system_user in system_users: for system_user in system_users:
tasks.extend(get_push_system_user_tasks(system_user)) tasks.extend(get_push_system_user_tasks(system_user))
task_name = _("Push system user to cluster assets period: {}->{}").format( task_name = _("Push cluster system users to assets period: {}").format(
cluster.name, ', '.join(s.name for s in system_users) cluster.name
) )
hosts = [asset.hostname for asset in cluster.assets.all()] hosts = [asset.hostname for asset in cluster.assets.all()]
update_or_create_ansible_task( update_or_create_ansible_task(

View File

@ -3,8 +3,8 @@
{% load static %} {% load static %}
{% load bootstrap3 %} {% load bootstrap3 %}
{% block custom_head_css_js %} {% block custom_head_css_js %}
<link href="{% static "css/plugins/select2/select2.min.css" %}" rel="stylesheet"> <link href="{% static 'css/plugins/select2/select2.min.css' %}" rel="stylesheet">
<script src="{% static "js/plugins/select2/select2.full.min.js" %}"></script> <script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
{% endblock %} {% endblock %}
{% block content %} {% block content %}

View File

@ -21,11 +21,11 @@
<a href="{% url 'assets:admin-user-assets' pk=admin_user.pk %}" class="text-center"><i class="fa fa-laptop"></i> {% trans 'Assets list' %} </a> <a href="{% url 'assets:admin-user-assets' pk=admin_user.pk %}" class="text-center"><i class="fa fa-laptop"></i> {% trans 'Assets list' %} </a>
</li> </li>
<li class="pull-right"> <li class="pull-right">
<a class="btn btn-outline btn-default" href="{% url 'assets:admin-user-update' pk=admin_user.id %}"><i class="fa fa-edit"></i>Update</a> <a class="btn btn-outline btn-default" href="{% url 'assets:admin-user-update' pk=admin_user.id %}"><i class="fa fa-edit"></i>{% trans 'Update' %}</a>
</li> </li>
<li class="pull-right"> <li class="pull-right">
<a class="btn btn-outline btn-danger btn-delete-admin-user"> <a class="btn btn-outline btn-danger btn-delete-admin-user">
<i class="fa fa-edit"></i>Delete <i class="fa fa-trash-o"></i>{% trans 'Delete' %}
</a> </a>
</li> </li>
</ul> </ul>
@ -60,7 +60,7 @@
<th>{% trans 'IP' %}</th> <th>{% trans 'IP' %}</th>
<th>{% trans 'Port' %}</th> <th>{% trans 'Port' %}</th>
<th>{% trans 'Type' %}</th> <th>{% trans 'Type' %}</th>
<th>{% trans 'Alive' %}</th> <th>{% trans 'Reachable' %}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@ -144,7 +144,7 @@ $(document).ready(function () {
url: the_url, url: the_url,
error: error, error: error,
method: 'GET', method: 'GET',
success_message: "{% trans "Task has been send, seen left asset status" %}" success_message: "{% trans 'Task has been send, seen left asset status' %}"
}); });
}) })
</script> </script>

View File

@ -3,8 +3,8 @@
{% load static %} {% load static %}
{% load bootstrap3 %} {% load bootstrap3 %}
{% block custom_head_css_js %} {% block custom_head_css_js %}
<link href="{% static "css/plugins/select2/select2.min.css" %}" rel="stylesheet"> <link href="{% static 'css/plugins/select2/select2.min.css' %}" rel="stylesheet">
<script src="{% static "js/plugins/select2/select2.full.min.js" %}"></script> <script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
{% endblock %} {% endblock %}
{% block content %} {% block content %}

View File

@ -21,11 +21,11 @@
<a href="{% url 'assets:admin-user-assets' pk=admin_user.pk %}" class="text-center"><i class="fa fa-laptop"></i> {% trans 'Assets list' %} </a> <a href="{% url 'assets:admin-user-assets' pk=admin_user.pk %}" class="text-center"><i class="fa fa-laptop"></i> {% trans 'Assets list' %} </a>
</li> </li>
<li class="pull-right"> <li class="pull-right">
<a class="btn btn-outline btn-default" href="{% url 'assets:admin-user-update' pk=admin_user.id %}"><i class="fa fa-edit"></i>Update</a> <a class="btn btn-outline btn-default" href="{% url 'assets:admin-user-update' pk=admin_user.id %}"><i class="fa fa-edit"></i>{% trans 'Update' %}</a>
</li> </li>
<li class="pull-right"> <li class="pull-right">
<a class="btn btn-outline btn-danger btn-delete-admin-user"> <a class="btn btn-outline btn-danger btn-delete-admin-user">
<i class="fa fa-edit"></i>Delete <i class="fa fa-trash-o"></i>{% trans 'Delete' %}
</a> </a>
</li> </li>
</ul> </ul>
@ -133,7 +133,6 @@ function bindToCluster(clusters) {
$('.select2-selection__rendered').empty(); $('.select2-selection__rendered').empty();
$('#cluster_selected').val(''); $('#cluster_selected').val('');
$.map(jumpserver.cluster_selected, function(cluster_name, index) { $.map(jumpserver.cluster_selected, function(cluster_name, index) {
console.log(index);
$('#opt_' + index).remove(); $('#opt_' + index).remove();
// change tr html of user groups. // change tr html of user groups.
$('#table-clusters tbody').append( $('#table-clusters tbody').append(

View File

@ -21,8 +21,10 @@
</th> </th>
<th class="text-center">{% trans 'Name' %}</th> <th class="text-center">{% trans 'Name' %}</th>
<th class="text-center">{% trans 'Username' %}</th> <th class="text-center">{% trans 'Username' %}</th>
<th class="text-center">{% trans 'Asset num' %}</th> <th class="text-center">{% trans 'Asset' %}</th>
<th class="text-center">{% trans 'Reachable' %}</th>
<th class="text-center">{% trans 'Unreachable' %}</th> <th class="text-center">{% trans 'Unreachable' %}</th>
<th class="text-center">{% trans 'Ratio' %}</th>
<th class="text-center">{% trans 'Comment' %}</th> <th class="text-center">{% trans 'Comment' %}</th>
<th class="text-center">{% trans 'Action' %}</th> <th class="text-center">{% trans 'Action' %}</th>
</tr> </tr>
@ -41,17 +43,50 @@ $(document).ready(function(){
{targets: 1, createdCell: function (td, cellData, rowData) { {targets: 1, createdCell: function (td, cellData, rowData) {
var detail_btn = '<a href="{% url "assets:admin-user-detail" pk=DEFAULT_PK %}">' + cellData + '</a>'; var detail_btn = '<a href="{% url "assets:admin-user-detail" pk=DEFAULT_PK %}">' + cellData + '</a>';
$(td).html(detail_btn.replace('{{ DEFAULT_PK }}', rowData.id)); $(td).html(detail_btn.replace('{{ DEFAULT_PK }}', rowData.id));
}}, }},
{targets: 4, createdCell: function (td, cellData) {
var innerHtml = "";
if (cellData !== 0) {
innerHtml = "<span class='text-navy'>" + cellData + "</span>";
} else {
innerHtml = "<span>" + cellData + "</span>";
}
$(td).html('<span href="javascript:void(0);" data-toggle="tooltip" title="' + cellData +'">' + innerHtml + '</span>');
}},
{targets: 5, createdCell: function (td, cellData) {
var innerHtml = "";
if (cellData !== 0) {
innerHtml = "<span class='text-danger'>" + cellData + "</span>";
} else {
innerHtml = "<span>" + cellData + "</span>";
}
$(td).html('<span href="javascript:void(0);" data-toggle="tooltip" title="' + cellData + '">' + innerHtml + '</span>');
}},
{targets: 6, createdCell: function (td, cellData, rowData) { {targets: 6, createdCell: function (td, cellData, rowData) {
{# var script_btn = '<a href="{% url "assets:admin-user-update" pk=DEFAULT_PK %}" class="btn btn-xs btn-primary">{% trans "Script" %}</a>'.replace('{{ DEFAULT_PK }}', cellData);#} var val = 0;
var innerHtml = "";
var total = rowData.assets_amount;
var reachable = rowData.reachable_amount;
if (total !== 0) {
val = reachable/total * 100;
}
if (val === 100) {
innerHtml = "<span class='text-navy'>" + val + "% </span>";
} else {
innerHtml = "<span class='text-danger'>" + val + "% </span>";
}
$(td).html('<span href="javascript:void(0);" data-toggle="tooltip" title="' + cellData + '">' + innerHtml + '</span>');
}},
{targets: 8, createdCell: function (td, cellData, rowData) {
var update_btn = '<a href="{% url "assets:admin-user-update" pk=DEFAULT_PK %}" class="btn btn-xs m-l-xs btn-info">{% trans "Update" %}</a>'.replace('{{ DEFAULT_PK }}', cellData); var update_btn = '<a href="{% url "assets:admin-user-update" pk=DEFAULT_PK %}" class="btn btn-xs m-l-xs btn-info">{% trans "Update" %}</a>'.replace('{{ DEFAULT_PK }}', cellData);
var del_btn = '<a class="btn btn-xs btn-danger m-l-xs btn_admin_user_delete" data-uid="{{ DEFAULT_PK }}">{% trans "Delete" %}</a>'.replace('{{ DEFAULT_PK }}', cellData); var del_btn = '<a class="btn btn-xs btn-danger m-l-xs btn_admin_user_delete" data-uid="{{ DEFAULT_PK }}">{% trans "Delete" %}</a>'.replace('{{ DEFAULT_PK }}', cellData);
{# $(td).html(script_btn + update_btn + del_btn)#}
$(td).html(update_btn + del_btn) $(td).html(update_btn + del_btn)
}}], }}],
ajax_url: '{% url "api-assets:admin-user-list" %}', ajax_url: '{% url "api-assets:admin-user-list" %}',
columns: [{data: function(){return ""}}, {data: "name" }, {data: "username" }, {data: "assets_amount" }, columns: [{data: function(){return ""}}, {data: "name" }, {data: "username" }, {data: "assets_amount" },
{data: "unreachable_amount"}, {data: "comment" }, {data: "id" }] {data: "reachable_amount"}, {data: "unreachable_amount"}, {data: "id"}, {data: "comment" }, {data: "id" }]
}; };
jumpserver.initDataTable(options); jumpserver.initDataTable(options);
}) })

View File

@ -21,11 +21,11 @@
</li> </li>
{% if user.is_superuser %} {% if user.is_superuser %}
<li class="pull-right"> <li class="pull-right">
<a class="btn btn-outline btn-default" href="{% url 'assets:asset-update' pk=asset.id %}"><i class="fa fa-edit"></i>Update</a> <a class="btn btn-outline btn-default" href="{% url 'assets:asset-update' pk=asset.id %}"><i class="fa fa-edit"></i>{% trans 'Update' %}</a>
</li> </li>
<li class="pull-right"> <li class="pull-right">
<a class="btn btn-outline btn-danger btn-delete-asset"> <a class="btn btn-outline btn-danger btn-delete-asset">
<i class="fa fa-edit"></i>Delete <i class="fa fa-trash-o"></i>{% trans 'Delete' %}
</a> </a>
</li> </li>
{% endif %} {% endif %}

View File

@ -1,119 +1,31 @@
{% extends 'base.html' %} {% extends '_base_create_update.html' %}
{% load i18n %}
{% load static %} {% load static %}
{% load bootstrap3 %} {% load bootstrap3 %}
{% block custom_head_css_js %} {% load i18n %}
<link href="{% static 'css/plugins/select2/select2.min.css' %}" rel="stylesheet">
<script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
{% endblock %}
{% block content %}
<div class="wrapper wrapper-content animated fadeInRight">
<div class="row">
<div class="col-sm-10">
<div class="ibox float-e-margins">
<div id="ibox-content" class="ibox-title">
<h5> {{ action }}</h5>
<div class="ibox-tools">
<a class="collapse-link">
<i class="fa fa-chevron-up"></i>
</a>
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<i class="fa fa-wrench"></i>
</a>
<a class="close-link">
<i class="fa fa-times"></i>
</a>
</div>
</div>
<div class="ibox-content"> {% block form %}
<div class="panel blank-panel"> <form id="groupForm" method="post" class="form-horizontal">
<div class="panel-body"> {% csrf_token %}
<div class="tab-content"> {% bootstrap_field form.name layout="horizontal" %}
<form id="groupForm" method="post" class="form-horizontal"> {% bootstrap_field form.assets layout="horizontal" %}
{% csrf_token %} {% bootstrap_field form.comment layout="horizontal" %}
<h3 class="widget-head-color-box">资产组信息</h3>
{% bootstrap_field form.name layout="horizontal" %} <div class="hr-line-dashed"></div>
{% bootstrap_field form.comment layout="horizontal" %} <div class="form-group">
{# <div class="hr-line-dashed"></div>#} <div class="col-sm-4 col-sm-offset-2">
{# <h3 class="widget-head-color-box">用户选择的资产</h3>#} <button class="btn btn-default" type="reset"> {% trans 'Reset' %}</button>
{# <div class="form-group">#} <button id="submit_button" class="btn btn-primary" type="submit">{% trans 'Submit' %}</button>
{# <label class="col-sm-2 control-label" id="asset_on_count">已选({{ assets_count }}</label>#}
{# <div class="col-sm-9" id="asset_sed">#}
{# <div class="form-asset-on" id="add_asset">#}
{# <p id="asset_on_p">#}
{# {% for asset in assets_on_list %}#}
{# <button name='asset_hostname' title='{{ asset.ip }}' type='button' class='btn btn-default btn-xs'>{{ asset.hostname }}</button>#}
{# {% endfor %}#}
{# </p>#}
{# </div>#}
{# </div>#}
{# </div>#}
<div class="hr-line-dashed"></div>
<div class="form-group">
<div class="col-sm-4 col-sm-offset-5">
<button class="btn btn-white" type="reset"> 重置 </button>
<button class="btn btn-primary" type="submit"> 提交 </button>
<div id='box2'> </div>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
</div> </div>
</div> </div>
</div> </form>
<!-- 模态框Modal -->
<div class="modal fade" id="modal" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">
<div class="modal-dialog modal-lg">
<div class="modal-content" id="box">
<!--此部分为主体内容,将远程加载进来-->
</div>
</div>
</div>
{% endblock %} {% endblock %}
{% block custom_foot_js %} {% block custom_foot_js %}
<script type="text/javascript"> <script type="text/javascript">
$(document).ready(function () { $(document).ready(function () {
$('.select2').select2(); $('.select2').select2({
$('.select2-system-user').select2(); closeOnSelect: false
});
}); });
$('#add_asset').on('click',function(){
$('#modal').modal('show');
});
$('#modal').modal({
show: false,
backdrop: 'static',
keyboard: 'false',
remote:"{% url 'assets:asset-modal-list' %}?group_id={{ group_id }}"
});
$('#modal').on('show.bs.modal',function(){
//alert('当调用show方法时立即触发')
});
$('#modal').on('shown.bs.modal',function(){
//alert('当弹窗完全加载完后,再触发;')
});
$('#modal').on('hide.bs.modal',function(){
//alert('当关闭时,立即触发;')
});
$('#modal').on('hidden.bs.modal',function(){
//alert('当关完全关闭后,再触发;')
});
$('#modal').on('loaded.bs.modal',function(){
//alert('当远程数据加载完毕后,再触发;')
});
</script> </script>
{% endblock %} {% endblock %}

View File

@ -3,8 +3,8 @@
{% load i18n %} {% load i18n %}
{% block custom_head_css_js %} {% block custom_head_css_js %}
<link href="{% static "css/plugins/select2/select2.min.css" %}" rel="stylesheet"> <link href="{% static 'css/plugins/select2/select2.min.css' %}" rel="stylesheet">
<script src="{% static "js/plugins/select2/select2.full.min.js" %}"></script> <script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<div class="wrapper wrapper-content animated fadeInRight"> <div class="wrapper wrapper-content animated fadeInRight">
@ -15,7 +15,12 @@
<ul class="nav nav-tabs"> <ul class="nav nav-tabs">
<li class="active"><a href="" class="text-center"><i class="fa fa-laptop"></i> {% trans 'Group assets' %} </a></li> <li class="active"><a href="" class="text-center"><i class="fa fa-laptop"></i> {% trans 'Group assets' %} </a></li>
<li class="pull-right"> <li class="pull-right">
<a class="btn btn-outline btn-default" href="{% url 'assets:asset-group-update' pk=asset_group.id %}"><i class="fa fa-edit"></i>Update</a> <a class="btn btn-outline btn-default" href="{% url 'assets:asset-group-update' pk=asset_group.id %}"><i class="fa fa-edit"></i>{% trans 'Update' %}</a>
</li>
<li class="pull-right">
<a class="btn btn-outline btn-danger btn-del">
<i class="fa fa-trash-o"></i>{% trans 'Delete' %}
</a>
</li> </li>
</ul> </ul>
</div> </div>
@ -212,7 +217,6 @@ $(document).ready(function () {
addAssets(assets_id); addAssets(assets_id);
}) })
.on('click', '.btn-leave-group', function () { .on('click', '.btn-leave-group', function () {
var $this = $(this); var $this = $(this);
var the_url = "{% url 'api-assets:group-update-assets' pk=asset_group.id %}"; var the_url = "{% url 'api-assets:group-update-assets' pk=asset_group.id %}";
@ -223,9 +227,15 @@ $(document).ready(function () {
}); });
var delete_asset_id = $(this).data('aid'); var delete_asset_id = $(this).data('aid');
assets.remove(delete_asset_id); assets.remove(delete_asset_id);
console.log(assets);
var data = {"assets": assets}; var data = {"assets": assets};
leaveGroup($this, name, the_url, data); leaveGroup($this, name, the_url, data);
}).on('click', '.btn-del', function () {
var $this = $(this);
var name = "{{ asset_group.name}}";
var uid = "{{ asset_group.id }}";
var the_url = '{% url "api-assets:asset-group-detail" pk=DEFAULT_PK %}'.replace('{{ DEFAULT_PK }}', uid);
var redirect_url = "{% url 'assets:asset-group-list' %}";
objectDelete($this, name, the_url, redirect_url);
}) })

View File

@ -75,8 +75,6 @@ $(document).ready(function(){
return false; return false;
} }
var the_url = '{% url "api-assets:asset-group-list" %}'; var the_url = '{% url "api-assets:asset-group-list" %}';
console.log(plain_id_list);
console.log(the_url);
function doDelete() { function doDelete() {
swal({ swal({
title: "{% trans 'Are you sure?' %}", title: "{% trans 'Are you sure?' %}",

View File

@ -109,7 +109,7 @@ function initTable() {
$(document).ready(function(){ $(document).ready(function(){
initTable(); initTable();
}) })
.on('click', '#btn_export', function () { .on('click', '.btn_export', function () {
var $data_table = $('#asset_list_table').DataTable(); var $data_table = $('#asset_list_table').DataTable();
var rows = $data_table.rows('.selected').data(); var rows = $data_table.rows('.selected').data();
var assets = []; var assets = [];
@ -129,7 +129,7 @@ $(document).ready(function(){
} }
}) })
}) })
.on('click', '#btn_import', function () { .on('click', '#btn_asset_import', function () {
var $form = $('#fm_asset_import'); var $form = $('#fm_asset_import');
$form.find('.help-block').remove(); $form.find('.help-block').remove();
function success (data) { function success (data) {

View File

@ -46,7 +46,6 @@
<div class="hr-line-dashed"></div> <div class="hr-line-dashed"></div>
<h3>{% trans 'Other' %}</h3> <h3>{% trans 'Other' %}</h3>
{% bootstrap_field form.status layout="horizontal" %} {% bootstrap_field form.status layout="horizontal" %}
{% bootstrap_field form.env layout="horizontal" %}
{% bootstrap_field form.comment layout="horizontal" %} {% bootstrap_field form.comment layout="horizontal" %}
{% bootstrap_field form.is_active layout="horizontal" %} {% bootstrap_field form.is_active layout="horizontal" %}

View File

@ -3,8 +3,8 @@
{% load i18n %} {% load i18n %}
{% block custom_head_css_js %} {% block custom_head_css_js %}
<link href="{% static "css/plugins/select2/select2.min.css" %}" rel="stylesheet"> <link href="{% static 'css/plugins/select2/select2.min.css' %}" rel="stylesheet">
<script src="{% static "js/plugins/select2/select2.full.min.js" %}"></script> <script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
<style type="text/css"> <style type="text/css">
</style> </style>
@ -94,7 +94,7 @@
<td colspan="2"> <td colspan="2">
<select data-placeholder="{% trans 'Select asset' %}" class="select2" style="width: 100%" multiple="" tabindex="4"> <select data-placeholder="{% trans 'Select asset' %}" class="select2" style="width: 100%" multiple="" tabindex="4">
{% for asset in assets_remain %} {% for asset in assets_remain %}
<option value="{{ asset.id }}" id="opt_{{ asset.id }}">{{ asset.ip}}:{{ asset.port }}</option> <option value="{{ asset.id }}" id="opt_{{ asset.id }}">{{ asset.hostname}}</option>
{% endfor %} {% endfor %}
</select> </select>
</td> </td>
@ -204,7 +204,12 @@ $(document).ready(function () {
addAssets(assets_id); addAssets(assets_id);
}) })
.on('click', '#btn-test-assets', function () { .on('click', '#btn-test-assets', function () {
console.log('ok'); var the_url = "{% url 'api-assets:cluster-test-connective' pk=cluster.id %}";
APIUpdateAttr({
url: the_url,
method: 'GET',
success_message: "{% trans 'Task has been send, seen left assets status' %}"
});
}) })
</script> </script>
{% endblock %} {% endblock %}

View File

@ -3,8 +3,8 @@
{% load static %} {% load static %}
{% load bootstrap3 %} {% load bootstrap3 %}
{% block custom_head_css_js %} {% block custom_head_css_js %}
<link href="{% static "css/plugins/select2/select2.min.css" %}" rel="stylesheet"> <link href="{% static 'css/plugins/select2/select2.min.css' %}" rel="stylesheet">
<script src="{% static "js/plugins/select2/select2.full.min.js" %}"></script> <script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<div class="wrapper wrapper-content animated fadeInRight"> <div class="wrapper wrapper-content animated fadeInRight">
@ -40,6 +40,7 @@
<h3 class="widget-head-color-box">{% trans 'Settings' %}</h3> <h3 class="widget-head-color-box">{% trans 'Settings' %}</h3>
{% bootstrap_field form.admin_user layout="horizontal" %} {% bootstrap_field form.admin_user layout="horizontal" %}
{% bootstrap_field form.system_users layout="horizontal" %}
<div class="hr-line-dashed"></div> <div class="hr-line-dashed"></div>
<h3 class="widget-head-color-box">{% trans 'Other' %}</h3> <h3 class="widget-head-color-box">{% trans 'Other' %}</h3>
@ -69,10 +70,7 @@
{% block custom_foot_js %} {% block custom_foot_js %}
<script> <script>
$(document).ready(function () { $(document).ready(function () {
$('.select2').select2({ $('.select2').select2();
dropdownAutoWidth : true, });
width: 'auto'
});
})
</script> </script>
{% endblock %} {% endblock %}

View File

@ -3,8 +3,8 @@
{% load i18n %} {% load i18n %}
{% block custom_head_css_js %} {% block custom_head_css_js %}
<link href="{% static "css/plugins/select2/select2.min.css" %}" rel="stylesheet"> <link href="{% static 'css/plugins/select2/select2.min.css' %}" rel="stylesheet">
<script src="{% static "js/plugins/select2/select2.full.min.js" %}"></script> <script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<div class="wrapper wrapper-content animated fadeInRight"> <div class="wrapper wrapper-content animated fadeInRight">
@ -22,11 +22,11 @@
</a> </a>
</li> </li>
<li class="pull-right"> <li class="pull-right">
<a class="btn btn-outline btn-default" href="{% url 'assets:cluster-update' pk=cluster.id %}"><i class="fa fa-edit"></i>Update</a> <a class="btn btn-outline btn-default" href="{% url 'assets:cluster-update' pk=cluster.id %}"><i class="fa fa-edit"></i>{% trans 'Update' %}</a>
</li> </li>
<li class="pull-right"> <li class="pull-right">
<a class="btn btn-outline btn-danger btn-delete-cluster"> <a class="btn btn-outline btn-danger btn-delete-cluster">
<i class="fa fa-edit"></i>Delete <i class="fa fa-trash-o"></i>{% trans 'Delete' %}
</a> </a>
</li> </li>
</ul> </ul>

View File

@ -8,7 +8,7 @@
{% block table_search %}{% endblock %} {% block table_search %}{% endblock %}
{% block table_container %} {% block table_container %}
<div class="uc pull-left m-r-5"> <div class="uc pull-left m-r-5">
<a href="{% url "assets:cluster-create" %}" class="btn btn-sm btn-primary"> {% trans "Create Cluster" %} </a> <a href="{% url "assets:cluster-create" %}" class="btn btn-sm btn-primary"> {% trans "Create cluster" %} </a>
</div> </div>
<table class="table table-striped table-bordered table-hover " id="cluster_list_table" > <table class="table table-striped table-bordered table-hover " id="cluster_list_table" >
<thead> <thead>

View File

@ -3,8 +3,8 @@
{% load i18n %} {% load i18n %}
{% block custom_head_css_js %} {% block custom_head_css_js %}
<link href="{% static "css/plugins/select2/select2.min.css" %}" rel="stylesheet"> <link href="{% static 'css/plugins/select2/select2.min.css' %}" rel="stylesheet">
<script src="{% static "js/plugins/select2/select2.full.min.js" %}"></script> <script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<div class="wrapper wrapper-content animated fadeInRight"> <div class="wrapper wrapper-content animated fadeInRight">
@ -22,7 +22,7 @@
</a> </a>
</li> </li>
<li class="pull-right"> <li class="pull-right">
<a class="btn btn-outline btn-default" href="{% url 'assets:system-user-update' pk=system_user.id %}"><i class="fa fa-edit"></i>Update</a> <a class="btn btn-outline btn-default" href="{% url 'assets:system-user-update' pk=system_user.id %}"><i class="fa fa-edit"></i>{% trans 'Update' %}</a>
</li> </li>
</ul> </ul>
</div> </div>

View File

@ -23,7 +23,12 @@
</a> </a>
</li> </li>
<li class="pull-right"> <li class="pull-right">
<a class="btn btn-outline btn-default" href="{% url 'assets:system-user-update' pk=system_user.id %}"><i class="fa fa-edit"></i>Update</a> <a class="btn btn-outline btn-default" href="{% url 'assets:system-user-update' pk=system_user.id %}"><i class="fa fa-edit"></i>{% trans 'Update' %}</a>
</li>
<li class="pull-right">
<a class="btn btn-outline btn-danger btn-del">
<i class="fa fa-trash-o"></i>{% trans 'Delete' %}
</a>
</li> </li>
</ul> </ul>
</div> </div>
@ -259,6 +264,13 @@ $(document).ready(function () {
return $(this).data('gid'); return $(this).data('gid');
}).get(); }).get();
updateSystemUserCluster(clusters); updateSystemUserCluster(clusters);
}).on('click', '.btn-del', function () {
var $this = $(this);
var name = "{{ system_user.name}}";
var uid = "{{ system_user.id }}";
var the_url = '{% url "api-assets:system-user-detail" pk=DEFAULT_PK %}'.replace('{{ DEFAULT_PK }}', uid);
var redirect_url = "{% url 'assets:system-user-list' %}";
objectDelete($this, name, the_url, redirect_url);
}) })
</script> </script>
{% endblock %} {% endblock %}

View File

@ -42,7 +42,6 @@ function initTable() {
columnDefs: [ columnDefs: [
{targets: 1, createdCell: function (td, cellData, rowData) { {targets: 1, createdCell: function (td, cellData, rowData) {
{% url 'assets:asset-detail' pk=DEFAULT_PK as the_url %} {% url 'assets:asset-detail' pk=DEFAULT_PK as the_url %}
console.log('{{ the_url }}');
var detail_btn = '<a href="{{ the_url }}">' + cellData + '</a>'; var detail_btn = '<a href="{{ the_url }}">' + cellData + '</a>';
$(td).html(detail_btn.replace('{{ DEFAULT_PK }}', rowData.id)); $(td).html(detail_btn.replace('{{ DEFAULT_PK }}', rowData.id));
}}, }},
@ -67,7 +66,7 @@ function initTable() {
$(td).html(conn_btn) $(td).html(conn_btn)
}} }}
], ],
ajax_url: '{% url "api-assets:asset-list" %}', ajax_url: '{% url "api-assets:user-asset-list" %}',
columns: [ columns: [
{data: "id"}, {data: "hostname" }, {data: "ip" }, {data: "port" }, {data: "id"}, {data: "hostname" }, {data: "ip" }, {data: "port" },
{data: "get_type_display" }, {data: "get_env_display"}, {data: "hardware_info"}, {data: "get_type_display" }, {data: "get_env_display"}, {data: "hardware_info"},

View File

@ -21,16 +21,18 @@ urlpatterns = [
api.AssetRefreshHardwareApi.as_view(), name='asset-refresh'), api.AssetRefreshHardwareApi.as_view(), name='asset-refresh'),
url(r'^v1/assets/(?P<pk>[0-9a-zA-Z\-]{36})/alive/$', url(r'^v1/assets/(?P<pk>[0-9a-zA-Z\-]{36})/alive/$',
api.AssetAdminUserTestApi.as_view(), name='asset-alive-test'), api.AssetAdminUserTestApi.as_view(), name='asset-alive-test'),
url(r'^v1/assets/user-assets/$',
api.UserAssetListView.as_view(), name='user-asset-list'),
# update the asset group, which add or delete the asset to the group # update the asset group, which add or delete the asset to the group
url(r'^v1/groups/(?P<pk>[0-9a-zA-Z\-]{36})/assets/$', url(r'^v1/groups/(?P<pk>[0-9a-zA-Z\-]{36})/assets/$',
api.GroupUpdateAssetsApi.as_view(), name='group-update-assets'), api.GroupUpdateAssetsApi.as_view(), name='group-update-assets'),
url(r'^v1/groups/(?P<pk>[0-9a-zA-Z\-]{36})/assets/add/$', url(r'^v1/groups/(?P<pk>[0-9a-zA-Z\-]{36})/assets/add/$',
api.GroupAddAssetsApi.as_view(), name='group-add-assets'), api.GroupAddAssetsApi.as_view(), name='group-add-assets'),
# update the Cluster, and add or delete the assets to the Cluster # update the Cluster, and add or delete the assets to the Cluster
url(r'^v1/cluster/(?P<pk>[0-9a-zA-Z\-]{36})/assets/$',
api.ClusterUpdateAssetsApi.as_view(), name='cluster-update-assets'),
url(r'^v1/cluster/(?P<pk>[0-9a-zA-Z\-]{36})/assets/$', url(r'^v1/cluster/(?P<pk>[0-9a-zA-Z\-]{36})/assets/$',
api.ClusterAddAssetsApi.as_view(), name='cluster-add-assets'), api.ClusterAddAssetsApi.as_view(), name='cluster-add-assets'),
url(r'^v1/cluster/(?P<pk>[0-9a-zA-Z\-]{36})/assets/connective/$',
api.ClusterTestAssetsAliveApi.as_view(), name='cluster-test-connective'),
url(r'^v1/admin-user/(?P<pk>[0-9a-zA-Z\-]{36})/clusters/$', url(r'^v1/admin-user/(?P<pk>[0-9a-zA-Z\-]{36})/clusters/$',
api.AdminUserAddClustersApi.as_view(), name='admin-user-add-clusters'), api.AdminUserAddClustersApi.as_view(), name='admin-user-add-clusters'),
url(r'^v1/admin-user/(?P<pk>[0-9a-zA-Z\-]{36})/connective/$', url(r'^v1/admin-user/(?P<pk>[0-9a-zA-Z\-]{36})/connective/$',

View File

@ -2,20 +2,22 @@
from __future__ import absolute_import, unicode_literals from __future__ import absolute_import, unicode_literals
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.conf import settings from django.conf import settings
from django.views.generic import TemplateView, ListView, View
from django.views.generic.edit import CreateView, DeleteView, FormView, UpdateView
from django.urls import reverse_lazy from django.urls import reverse_lazy
from django.views.generic import TemplateView, ListView
from django.views.generic.edit import CreateView, DeleteView, UpdateView
from django.contrib.messages.views import SuccessMessageMixin from django.contrib.messages.views import SuccessMessageMixin
from django.views.generic.detail import DetailView, SingleObjectMixin from django.views.generic.detail import DetailView, SingleObjectMixin
from common.const import create_success_msg, update_success_msg
from .. import forms from .. import forms
from ..models import Asset, AssetGroup, AdminUser, Cluster, SystemUser from ..models import AdminUser, Cluster
from ..hands import AdminUserRequiredMixin from ..hands import AdminUserRequiredMixin
__all__ = ['AdminUserCreateView', 'AdminUserDetailView', __all__ = [
'AdminUserDeleteView', 'AdminUserListView', 'AdminUserCreateView', 'AdminUserDetailView',
'AdminUserUpdateView', 'AdminUserAssetsView', 'AdminUserDeleteView', 'AdminUserListView',
] 'AdminUserUpdateView', 'AdminUserAssetsView',
]
class AdminUserListView(AdminUserRequiredMixin, TemplateView): class AdminUserListView(AdminUserRequiredMixin, TemplateView):
@ -38,6 +40,7 @@ class AdminUserCreateView(AdminUserRequiredMixin,
form_class = forms.AdminUserForm form_class = forms.AdminUserForm
template_name = 'assets/admin_user_create_update.html' template_name = 'assets/admin_user_create_update.html'
success_url = reverse_lazy('assets:admin-user-list') success_url = reverse_lazy('assets:admin-user-list')
success_message = create_success_msg
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = { context = {
@ -47,20 +50,13 @@ class AdminUserCreateView(AdminUserRequiredMixin,
kwargs.update(context) kwargs.update(context)
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)
def get_success_message(self, cleaned_data):
success_message = _(
'Create admin user <a href="{url}">{name}</a> successfully.'.format(
url=reverse_lazy('assets:admin-user-detail',
kwargs={'pk': self.object.pk}),
name=self.object.name,
))
return success_message
class AdminUserUpdateView(AdminUserRequiredMixin, SuccessMessageMixin, UpdateView):
class AdminUserUpdateView(AdminUserRequiredMixin, UpdateView):
model = AdminUser model = AdminUser
form_class = forms.AdminUserForm form_class = forms.AdminUserForm
template_name = 'assets/admin_user_create_update.html' template_name = 'assets/admin_user_create_update.html'
success_url = reverse_lazy('assets:admin-user-list')
success_message = update_success_msg
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = { context = {
@ -70,11 +66,6 @@ class AdminUserUpdateView(AdminUserRequiredMixin, UpdateView):
kwargs.update(context) kwargs.update(context)
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)
def get_success_url(self):
success_url = reverse_lazy('assets:admin-user-detail',
kwargs={'pk': self.object.pk})
return success_url
class AdminUserDetailView(AdminUserRequiredMixin, DetailView): class AdminUserDetailView(AdminUserRequiredMixin, DetailView):
model = AdminUser model = AdminUser

View File

@ -21,10 +21,11 @@ from django.core.cache import cache
from django.utils import timezone from django.utils import timezone
from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.auth.mixins import LoginRequiredMixin
from django.shortcuts import redirect from django.shortcuts import redirect
from django.contrib.messages.views import SuccessMessageMixin
from common.mixins import JSONResponseMixin from common.mixins import JSONResponseMixin
from common.utils import get_object_or_none, get_logger, is_uuid from common.utils import get_object_or_none, get_logger, is_uuid
from common.const import create_success_msg, update_success_msg
from .. import forms from .. import forms
from ..models import Asset, AssetGroup, AdminUser, Cluster, SystemUser from ..models import Asset, AssetGroup, AdminUser, Cluster, SystemUser
from ..hands import AdminUserRequiredMixin from ..hands import AdminUserRequiredMixin
@ -46,7 +47,6 @@ class AssetListView(AdminUserRequiredMixin, TemplateView):
context = { context = {
'app': _('Assets'), 'app': _('Assets'),
'action': _('Asset list'), 'action': _('Asset list'),
# 'groups': AssetGroup.objects.all(),
'system_users': SystemUser.objects.all(), 'system_users': SystemUser.objects.all(),
} }
kwargs.update(context) kwargs.update(context)
@ -66,7 +66,7 @@ class UserAssetListView(LoginRequiredMixin, TemplateView):
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)
class AssetCreateView(AdminUserRequiredMixin, CreateView): class AssetCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateView):
model = Asset model = Asset
form_class = forms.AssetCreateForm form_class = forms.AssetCreateForm
template_name = 'assets/asset_create.html' template_name = 'assets/asset_create.html'
@ -87,6 +87,9 @@ class AssetCreateView(AdminUserRequiredMixin, CreateView):
kwargs.update(context) kwargs.update(context)
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)
def get_success_message(self, cleaned_data):
return create_success_msg % ({"name": cleaned_data["hostname"]})
class AssetModalListView(AdminUserRequiredMixin, ListView): class AssetModalListView(AdminUserRequiredMixin, ListView):
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
@ -147,7 +150,7 @@ class AssetBulkUpdateView(AdminUserRequiredMixin, ListView):
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)
class AssetUpdateView(AdminUserRequiredMixin, UpdateView): class AssetUpdateView(AdminUserRequiredMixin, SuccessMessageMixin, UpdateView):
model = Asset model = Asset
form_class = forms.AssetUpdateForm form_class = forms.AssetUpdateForm
template_name = 'assets/asset_update.html' template_name = 'assets/asset_update.html'
@ -159,7 +162,10 @@ class AssetUpdateView(AdminUserRequiredMixin, UpdateView):
'action': _('Update asset'), 'action': _('Update asset'),
} }
kwargs.update(context) kwargs.update(context)
return super(AssetUpdateView, self).get_context_data(**kwargs) return super().get_context_data(**kwargs)
def get_success_message(self, cleaned_data):
return update_success_msg % ({"name": cleaned_data["hostname"]})
class AssetDeleteView(AdminUserRequiredMixin, DeleteView): class AssetDeleteView(AdminUserRequiredMixin, DeleteView):
@ -184,14 +190,14 @@ class AssetDetailView(DetailView):
'system_users_all': SystemUser.objects.all(), 'system_users_all': SystemUser.objects.all(),
} }
kwargs.update(context) kwargs.update(context)
return super(AssetDetailView, self).get_context_data(**kwargs) return super().get_context_data(**kwargs)
@method_decorator(csrf_exempt, name='dispatch') @method_decorator(csrf_exempt, name='dispatch')
class AssetExportView(View): class AssetExportView(View):
def get(self, request): def get(self, request):
spm = request.GET.get('spm', '') spm = request.GET.get('spm', '')
assets_id_default = [Asset.objects.first().id] if Asset.objects.first() else [1] assets_id_default = [Asset.objects.first().id] if Asset.objects.first() else []
assets_id = cache.get(spm, assets_id_default) assets_id = cache.get(spm, assets_id_default)
fields = [ fields = [
field for field in Asset._meta.fields field for field in Asset._meta.fields

View File

@ -1,17 +1,21 @@
# coding:utf-8 # coding:utf-8
from __future__ import absolute_import, unicode_literals
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.views.generic import TemplateView, ListView, View from django.views.generic import TemplateView, ListView, View
from django.views.generic.edit import CreateView, DeleteView, FormView, UpdateView from django.views.generic.edit import CreateView, DeleteView, FormView, UpdateView
from django.urls import reverse_lazy from django.urls import reverse_lazy
from django.views.generic.detail import DetailView, SingleObjectMixin from django.views.generic.detail import DetailView, SingleObjectMixin
from django.contrib.messages.views import SuccessMessageMixin
from common.const import create_success_msg, update_success_msg
from .. import forms from .. import forms
from ..models import Asset, AssetGroup, AdminUser, Cluster, SystemUser from ..models import Asset, AssetGroup, AdminUser, Cluster, SystemUser
from ..hands import AdminUserRequiredMixin from ..hands import AdminUserRequiredMixin
__all__ = ['ClusterListView', 'ClusterCreateView', 'ClusterUpdateView', __all__ = [
'ClusterDetailView', 'ClusterDeleteView', 'ClusterAssetsView'] 'ClusterListView', 'ClusterCreateView', 'ClusterUpdateView',
'ClusterDetailView', 'ClusterDeleteView', 'ClusterAssetsView',
]
class ClusterListView(AdminUserRequiredMixin, TemplateView): class ClusterListView(AdminUserRequiredMixin, TemplateView):
@ -21,39 +25,40 @@ class ClusterListView(AdminUserRequiredMixin, TemplateView):
context = { context = {
'app': _('Assets'), 'app': _('Assets'),
'action': _('Cluster list'), 'action': _('Cluster list'),
# 'keyword': self.request.GET.get('keyword', '')
} }
kwargs.update(context) kwargs.update(context)
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)
class ClusterCreateView(AdminUserRequiredMixin, CreateView): class ClusterCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateView):
model = Cluster model = Cluster
form_class = forms.ClusterForm form_class = forms.ClusterForm
template_name = 'assets/cluster_create_update.html' template_name = 'assets/cluster_create_update.html'
success_url = reverse_lazy('assets:cluster-list') success_url = reverse_lazy('assets:cluster-list')
success_message = create_success_msg
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = { context = {
'app': _('assets'), 'app': _('assets'),
'action': _('Create Cluster'), 'action': _('Create cluster'),
} }
kwargs.update(context) kwargs.update(context)
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)
def form_valid(self, form): def form_valid(self, form):
cluster = form.save(commit=False) cluster = form.save(commit=False)
cluster.created_by = self.request.user.username or 'System' cluster.created_by = self.request.user.username
cluster.save() cluster.save()
return super().form_valid(form) return super().form_valid(form)
class ClusterUpdateView(AdminUserRequiredMixin, UpdateView): class ClusterUpdateView(AdminUserRequiredMixin, SuccessMessageMixin, UpdateView):
model = Cluster model = Cluster
form_class = forms.ClusterForm form_class = forms.ClusterForm
template_name = 'assets/cluster_create_update.html' template_name = 'assets/cluster_create_update.html'
context_object_name = 'cluster' context_object_name = 'cluster'
success_url = reverse_lazy('assets:cluster-list') success_url = reverse_lazy('assets:cluster-list')
success_message = update_success_msg
def form_valid(self, form): def form_valid(self, form):
cluster = form.save(commit=False) cluster = form.save(commit=False)

View File

@ -7,42 +7,41 @@ from django.views.generic.edit import CreateView, DeleteView, FormView, UpdateVi
from django.urls import reverse_lazy from django.urls import reverse_lazy
from django.views.generic.detail import DetailView, SingleObjectMixin from django.views.generic.detail import DetailView, SingleObjectMixin
from django.shortcuts import get_object_or_404, reverse, redirect from django.shortcuts import get_object_or_404, reverse, redirect
from django.contrib.messages.views import SuccessMessageMixin
from common.const import create_success_msg, update_success_msg
from .. import forms from .. import forms
from ..models import Asset, AssetGroup, AdminUser, Cluster, SystemUser from ..models import Asset, AssetGroup, AdminUser, Cluster, SystemUser
from ..hands import AdminUserRequiredMixin from ..hands import AdminUserRequiredMixin
__all__ = ['AssetGroupCreateView', 'AssetGroupDetailView', __all__ = [
'AssetGroupUpdateView', 'AssetGroupListView', 'AssetGroupCreateView', 'AssetGroupDetailView',
'AssetGroupDeleteView', 'AssetGroupUpdateView', 'AssetGroupListView',
] 'AssetGroupDeleteView',
]
class AssetGroupCreateView(AdminUserRequiredMixin, CreateView): class AssetGroupCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateView):
model = AssetGroup model = AssetGroup
form_class = forms.AssetGroupForm form_class = forms.AssetGroupForm
template_name = 'assets/asset_group_create.html' template_name = 'assets/asset_group_create.html'
success_url = reverse_lazy('assets:asset-group-list') success_url = reverse_lazy('assets:asset-group-list')
success_message = create_success_msg
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = { context = {
'app': _('Assets'), 'app': _('Assets'),
'action': _('Create asset group'), 'action': _('Create asset group'),
'assets_count': 0,
} }
kwargs.update(context) kwargs.update(context)
return super(AssetGroupCreateView, self).get_context_data(**kwargs) return super().get_context_data(**kwargs)
def form_valid(self, form): def form_valid(self, form):
asset_group = form.save() group = form.save()
assets_id_list = self.request.POST.getlist('assets', []) group.created_by = self.request.user.username
assets = [get_object_or_404(Asset, id=int(asset_id)) group.save()
for asset_id in assets_id_list] return super().form_valid(form)
asset_group.created_by = self.request.user.username or 'Admin'
asset_group.assets.add(*tuple(assets))
asset_group.save()
return super(AssetGroupCreateView, self).form_valid(form)
class AssetGroupListView(AdminUserRequiredMixin, TemplateView): class AssetGroupListView(AdminUserRequiredMixin, TemplateView):
@ -54,7 +53,6 @@ class AssetGroupListView(AdminUserRequiredMixin, TemplateView):
'action': _('Asset group list'), 'action': _('Asset group list'),
'assets': Asset.objects.all(), 'assets': Asset.objects.all(),
'system_users': SystemUser.objects.all(), 'system_users': SystemUser.objects.all(),
'keyword': self.request.GET.get('keyword', '')
} }
kwargs.update(context) kwargs.update(context)
return super(AssetGroupListView, self).get_context_data(**kwargs) return super(AssetGroupListView, self).get_context_data(**kwargs)
@ -77,27 +75,20 @@ class AssetGroupDetailView(AdminUserRequiredMixin, DetailView):
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)
class AssetGroupUpdateView(AdminUserRequiredMixin, UpdateView): class AssetGroupUpdateView(AdminUserRequiredMixin, SuccessMessageMixin, UpdateView):
model = AssetGroup model = AssetGroup
form_class = forms.AssetGroupForm form_class = forms.AssetGroupForm
template_name = 'assets/asset_group_create.html' template_name = 'assets/asset_group_create.html'
success_url = reverse_lazy('assets:asset-group-list') success_url = reverse_lazy('assets:asset-group-list')
success_message = update_success_msg
def get(self, request, *args, **kwargs):
self.object = self.get_object(queryset=AssetGroup.objects.all())
return super(AssetGroupUpdateView, self).get(request, *args, **kwargs)
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
assets_all = self.object.assets.all()
context = { context = {
'app': _('Assets'), 'app': _('Assets'),
'action': _('Create asset group'), 'action': _('Create asset group'),
'assets_on_list': assets_all,
'assets_count': len(assets_all),
'group_id': self.object.id,
} }
kwargs.update(context) kwargs.update(context)
return super(AssetGroupUpdateView, self).get_context_data(**kwargs) return super().get_context_data(**kwargs)
class AssetGroupDeleteView(AdminUserRequiredMixin, DeleteView): class AssetGroupDeleteView(AdminUserRequiredMixin, DeleteView):

View File

@ -1,24 +1,23 @@
# ~*~ coding: utf-8 ~*~ # ~*~ coding: utf-8 ~*~
from django.contrib import messages
from django.shortcuts import redirect, reverse
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.db import transaction from django.views.generic import TemplateView
from django.views.generic import TemplateView, ListView, FormView
from django.views.generic.edit import CreateView, DeleteView, UpdateView from django.views.generic.edit import CreateView, DeleteView, UpdateView
from django.urls import reverse_lazy from django.urls import reverse_lazy
from django.contrib.messages.views import SuccessMessageMixin from django.contrib.messages.views import SuccessMessageMixin
from django.views.generic.detail import DetailView, SingleObjectMixin from django.views.generic.detail import DetailView
from ..forms import SystemUserForm, SystemUserUpdateForm, SystemUserAuthForm from common.const import create_success_msg, update_success_msg
from ..forms import SystemUserForm, SystemUserUpdateForm
from ..models import SystemUser, Cluster from ..models import SystemUser, Cluster
from ..hands import AdminUserRequiredMixin from ..hands import AdminUserRequiredMixin
__all__ = ['SystemUserCreateView', 'SystemUserUpdateView', __all__ = [
'SystemUserDetailView', 'SystemUserDeleteView', 'SystemUserCreateView', 'SystemUserUpdateView',
'SystemUserAssetView', 'SystemUserListView', 'SystemUserDetailView', 'SystemUserDeleteView',
] 'SystemUserAssetView', 'SystemUserListView',
]
class SystemUserListView(AdminUserRequiredMixin, TemplateView): class SystemUserListView(AdminUserRequiredMixin, TemplateView):
@ -38,10 +37,7 @@ class SystemUserCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateVi
form_class = SystemUserForm form_class = SystemUserForm
template_name = 'assets/system_user_create.html' template_name = 'assets/system_user_create.html'
success_url = reverse_lazy('assets:system-user-list') success_url = reverse_lazy('assets:system-user-list')
success_message = create_success_msg
@transaction.atomic
def post(self, request, *args, **kwargs):
return super(SystemUserCreateView, self).post(request, *args, **kwargs)
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = { context = {
@ -51,20 +47,13 @@ class SystemUserCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateVi
kwargs.update(context) kwargs.update(context)
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)
def get_success_message(self, cleaned_data):
url = reverse('assets:system-user-detail', kwargs={'pk': self.object.pk})
success_message = _(
'Create system user <a href="{url}">{name}</a> '
'successfully.'.format(url=url, name=self.object.name)
)
return success_message class SystemUserUpdateView(AdminUserRequiredMixin, SuccessMessageMixin, UpdateView):
class SystemUserUpdateView(AdminUserRequiredMixin, UpdateView):
model = SystemUser model = SystemUser
form_class = SystemUserUpdateForm form_class = SystemUserUpdateForm
template_name = 'assets/system_user_update.html' template_name = 'assets/system_user_update.html'
success_url = reverse_lazy('assets:system-user-list')
success_message = update_success_msg
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = { context = {
@ -74,11 +63,6 @@ class SystemUserUpdateView(AdminUserRequiredMixin, UpdateView):
kwargs.update(context) kwargs.update(context)
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)
def get_success_url(self):
success_url = reverse_lazy('assets:system-user-detail',
kwargs={'pk': self.object.pk})
return success_url
class SystemUserDetailView(AdminUserRequiredMixin, DetailView): class SystemUserDetailView(AdminUserRequiredMixin, DetailView):
template_name = 'assets/system_user_detail.html' template_name = 'assets/system_user_detail.html'
@ -109,8 +93,8 @@ class SystemUserAssetView(AdminUserRequiredMixin, DetailView):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = { context = {
'app': 'assets', 'app': _('assets'),
'action': 'System user asset', 'action': _('System user asset'),
} }
kwargs.update(context) kwargs.update(context)
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)

7
apps/common/const.py Normal file
View File

@ -0,0 +1,7 @@
# -*- coding: utf-8 -*-
#
from django.utils.translation import ugettext as _
create_success_msg = _("<b>%(name)s</b> was created successfully")
update_success_msg = _("<b>%(name)s</b> was updated successfully")

View File

@ -287,10 +287,10 @@ def make_signature(access_key_secret, date=None):
return content_md5(data) return content_md5(data)
def encrypt_password(password): def encrypt_password(password, salt=None):
from passlib.hash import sha512_crypt from passlib.hash import sha512_crypt
if password: if password:
return sha512_crypt.using(rounds=5000).hash(password) return sha512_crypt.using(rounds=5000).hash(password, salt=salt)
return None return None

View File

@ -4,24 +4,44 @@ import os
import re import re
import pytz import pytz
from django.utils import timezone from django.utils import timezone
from django.utils.deprecation import MiddlewareMixin
from django.shortcuts import HttpResponse from django.shortcuts import HttpResponse
DEMO_MODE = os.environ.get("DEMO_MODE", "") class TimezoneMiddleware:
SAFE_URL = r'^/users/login|^/api/terminal/v1/.*|/api/terminal/.*|/api/users/v1/auth/|/api/users/v1/profile/' def __init__(self, get_response):
self.get_response = get_response
def __call__(self, request):
class TimezoneMiddleware(MiddlewareMixin):
def process_request(self, request):
tzname = request.META.get('TZ') tzname = request.META.get('TZ')
if tzname: if tzname:
timezone.activate(pytz.timezone(tzname)) timezone.activate(pytz.timezone(tzname))
else: else:
timezone.deactivate() timezone.deactivate()
response = self.get_response(request)
return response
class DemoMiddleware(MiddlewareMixin): class DemoMiddleware:
def process_request(self, request): DEMO_MODE_ENABLED = os.environ.get("DEMO_MODE", "") in ("1", "ok", "True")
if DEMO_MODE and request.method not in ["GET", "HEAD"] and not re.match(SAFE_URL, request.path): SAFE_URL_PATTERN = re.compile(
return HttpResponse("Demo mode, only get request accept", status=403) r'^/users/login|'
r'^/api/terminal/v1/.*|'
r'^/api/terminal/.*|'
r'^/api/users/v1/auth/|'
r'^/api/users/v1/profile/'
)
SAFE_METHOD = ("GET", "HEAD")
def __init__(self, get_response):
self.get_response = get_response
if self.DEMO_MODE_ENABLED:
print("Demo mode enabled, reject unsafe method and url")
def __call__(self, request):
if self.DEMO_MODE_ENABLED and request.method not in self.SAFE_METHOD \
and not self.SAFE_URL_PATTERN.match(request.path):
return HttpResponse("Demo mode, only safe request accepted", status=403)
else:
response = self.get_response(request)
return response

View File

@ -240,9 +240,8 @@ LOGGING = {
# Internationalization # Internationalization
# https://docs.djangoproject.com/en/1.10/topics/i18n/ # https://docs.djangoproject.com/en/1.10/topics/i18n/
LANGUAGE_CODE = 'en-us' LANGUAGE_CODE = 'zh-cn'
# TIME_ZONE = 'UTC'
TIME_ZONE = 'Asia/Shanghai' TIME_ZONE = 'Asia/Shanghai'
USE_I18N = True USE_I18N = True
@ -300,7 +299,8 @@ REST_FRAMEWORK = {
'users.authentication.SessionAuthentication', 'users.authentication.SessionAuthentication',
), ),
'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',), 'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',),
'DATETIME_FORMAT': '%Y-%m-%d %H:%M:%S', 'DATETIME_FORMAT': '%Y-%m-%d %H:%M:%S %z',
'DATETIME_INPUT_FORMATS': ['%Y-%m-%d %H:%M:%S %z'],
} }
AUTHENTICATION_BACKENDS = [ AUTHENTICATION_BACKENDS = [
@ -374,4 +374,5 @@ BOOTSTRAP3 = {
'horizontal_field_class': 'col-md-9', 'horizontal_field_class': 'col-md-9',
# Set placeholder attributes to label if no placeholder is provided # Set placeholder attributes to label if no placeholder is provided
'set_placeholder': True, 'set_placeholder': True,
'success_css_class': '',
} }

View File

@ -36,6 +36,6 @@ urlpatterns = [
if settings.DEBUG: if settings.DEBUG:
urlpatterns += [ urlpatterns += [
url(r'^docs/', schema_view, name="docs"), url(r'^docs/', schema_view, name="docs"),
] + static(settings.STATIC_URL, document_root=settings.STATIC_DIR) \ ] + static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) \
+ static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@ -2,11 +2,13 @@
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
from rest_framework import viewsets from rest_framework import viewsets, generics
from rest_framework.views import Response
from .hands import IsSuperUser from .hands import IsSuperUser
from .models import Task, AdHoc, AdHocRunHistory from .models import Task, AdHoc, AdHocRunHistory
from .serializers import TaskSerializer, AdHocSerializer, AdHocRunHistorySerializer from .serializers import TaskSerializer, AdHocSerializer, AdHocRunHistorySerializer
from .tasks import run_ansible_task
class TaskViewSet(viewsets.ModelViewSet): class TaskViewSet(viewsets.ModelViewSet):
@ -15,6 +17,17 @@ class TaskViewSet(viewsets.ModelViewSet):
permission_classes = (IsSuperUser,) permission_classes = (IsSuperUser,)
class TaskRun(generics.RetrieveAPIView):
queryset = Task.objects.all()
serializer_class = TaskViewSet
permission_classes = (IsSuperUser,)
def retrieve(self, request, *args, **kwargs):
task = self.get_object()
run_ansible_task.delay(str(task.id))
return Response({"msg": "start"})
class AdHocViewSet(viewsets.ModelViewSet): class AdHocViewSet(viewsets.ModelViewSet):
queryset = AdHoc.objects.all() queryset = AdHoc.objects.all()
serializer_class = AdHocSerializer serializer_class = AdHocSerializer

View File

@ -1,4 +1,4 @@
# ~*~ coding: utf-8 ~*~ # ~*~ coding: utf-8 ~*~
from users.permissions import IsSuperUser from users.permissions import IsSuperUser
from users.utils import AdminUserRequiredMixin

View File

@ -235,6 +235,7 @@ class AdHoc(models.Model):
return result.results_raw, result.results_summary return result.results_raw, result.results_summary
except AnsibleError as e: except AnsibleError as e:
logger.error("Failed run adhoc {}, {}".format(self.task.name, e)) logger.error("Failed run adhoc {}, {}".format(self.task.name, e))
pass
@become.setter @become.setter
def become(self, item): def become(self, item):

View File

@ -3,9 +3,9 @@
{% load i18n %} {% load i18n %}
{% block custom_head_css_js %} {% block custom_head_css_js %}
<link href="{% static "css/plugins/select2/select2.min.css" %}" rel="stylesheet"> <link href="{% static 'css/plugins/select2/select2.min.css' %}" rel="stylesheet">
<link href="{% static "css/plugins/sweetalert/sweetalert.css" %}" rel="stylesheet"> <link href="{% static "css/plugins/sweetalert/sweetalert.css" %}" rel="stylesheet">
<script src="{% static "js/plugins/select2/select2.full.min.js" %}"></script> <script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
<script src="{% static "js/plugins/sweetalert/sweetalert.min.js" %}"></script> <script src="{% static "js/plugins/sweetalert/sweetalert.min.js" %}"></script>
{% endblock %} {% endblock %}
{% block content %} {% block content %}

View File

@ -3,9 +3,9 @@
{% load i18n %} {% load i18n %}
{% block custom_head_css_js %} {% block custom_head_css_js %}
<link href="{% static "css/plugins/select2/select2.min.css" %}" rel="stylesheet"> <link href="{% static 'css/plugins/select2/select2.min.css' %}" rel="stylesheet">
<link href="{% static "css/plugins/sweetalert/sweetalert.css" %}" rel="stylesheet"> <link href="{% static "css/plugins/sweetalert/sweetalert.css" %}" rel="stylesheet">
<script src="{% static "js/plugins/select2/select2.full.min.js" %}"></script> <script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
<script src="{% static "js/plugins/sweetalert/sweetalert.min.js" %}"></script> <script src="{% static "js/plugins/sweetalert/sweetalert.min.js" %}"></script>
{% endblock %} {% endblock %}
{% block content %} {% block content %}

View File

@ -3,9 +3,9 @@
{% load i18n %} {% load i18n %}
{% block custom_head_css_js %} {% block custom_head_css_js %}
<link href="{% static "css/plugins/select2/select2.min.css" %}" rel="stylesheet"> <link href="{% static 'css/plugins/select2/select2.min.css' %}" rel="stylesheet">
<link href="{% static "css/plugins/sweetalert/sweetalert.css" %}" rel="stylesheet"> <link href="{% static "css/plugins/sweetalert/sweetalert.css" %}" rel="stylesheet">
<script src="{% static "js/plugins/select2/select2.full.min.js" %}"></script> <script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
<script src="{% static "js/plugins/sweetalert/sweetalert.min.js" %}"></script> <script src="{% static "js/plugins/sweetalert/sweetalert.min.js" %}"></script>
{% endblock %} {% endblock %}
{% block content %} {% block content %}

View File

@ -3,9 +3,9 @@
{% load i18n %} {% load i18n %}
{% block custom_head_css_js %} {% block custom_head_css_js %}
<link href="{% static "css/plugins/select2/select2.min.css" %}" rel="stylesheet"> <link href="{% static 'css/plugins/select2/select2.min.css' %}" rel="stylesheet">
<link href="{% static "css/plugins/sweetalert/sweetalert.css" %}" rel="stylesheet"> <link href="{% static "css/plugins/sweetalert/sweetalert.css" %}" rel="stylesheet">
<script src="{% static "js/plugins/select2/select2.full.min.js" %}"></script> <script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
<script src="{% static "js/plugins/sweetalert/sweetalert.min.js" %}"></script> <script src="{% static "js/plugins/sweetalert/sweetalert.min.js" %}"></script>
{% endblock %} {% endblock %}
{% block content %} {% block content %}

View File

@ -3,9 +3,9 @@
{% load i18n %} {% load i18n %}
{% block custom_head_css_js %} {% block custom_head_css_js %}
<link href="{% static "css/plugins/select2/select2.min.css" %}" rel="stylesheet"> <link href="{% static 'css/plugins/select2/select2.min.css' %}" rel="stylesheet">
<link href="{% static "css/plugins/sweetalert/sweetalert.css" %}" rel="stylesheet"> <link href="{% static "css/plugins/sweetalert/sweetalert.css" %}" rel="stylesheet">
<script src="{% static "js/plugins/select2/select2.full.min.js" %}"></script> <script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
<script src="{% static "js/plugins/sweetalert/sweetalert.min.js" %}"></script> <script src="{% static "js/plugins/sweetalert/sweetalert.min.js" %}"></script>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
@ -89,7 +89,7 @@
<td> <td>
<b> <b>
{% for task in object.latest_adhoc.tasks %} {% for task in object.latest_adhoc.tasks %}
{{ forloop.counter }}. {{ task.name }} : {{ task.action.module }} <br/> {{ forloop.counter }}. {{ task.name }} ::: {{ task.action.module }} <br/>
{% endfor %} {% endfor %}
</b> </b>
</td> </td>

View File

@ -3,9 +3,9 @@
{% load i18n %} {% load i18n %}
{% block custom_head_css_js %} {% block custom_head_css_js %}
<link href="{% static "css/plugins/select2/select2.min.css" %}" rel="stylesheet"> <link href="{% static 'css/plugins/select2/select2.min.css' %}" rel="stylesheet">
<link href="{% static "css/plugins/sweetalert/sweetalert.css" %}" rel="stylesheet"> <link href="{% static "css/plugins/sweetalert/sweetalert.css" %}" rel="stylesheet">
<script src="{% static "js/plugins/select2/select2.full.min.js" %}"></script> <script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
<script src="{% static "js/plugins/sweetalert/sweetalert.min.js" %}"></script> <script src="{% static "js/plugins/sweetalert/sweetalert.min.js" %}"></script>
{% endblock %} {% endblock %}
{% block content %} {% block content %}

View File

@ -67,6 +67,7 @@
{% endif %} {% endif %}
</td> </td>
<td class="text-center"> <td class="text-center">
<a data-uid="{{ object.id }}" class="btn btn-xs btn-primary btn-run">{% trans "Run" %}</a>
<a data-uid="{{ object.id }}" class="btn btn-xs btn-danger btn-del">{% trans "Delete" %}</a> <a data-uid="{{ object.id }}" class="btn btn-xs btn-danger btn-del">{% trans "Delete" %}</a>
</td> </td>
</tr> </tr>
@ -98,10 +99,32 @@ $(document).ready(function() {
}).on('click', '.btn-del', function () { }).on('click', '.btn-del', function () {
var $this = $(this); var $this = $(this);
var name = $(this).closest("tr").find(":nth-child(2)").children('a').html(); var name = $this.closest("tr").find(":nth-child(2)").children('a').html();
var uid = $this.data('uid'); var uid = $this.data('uid');
var the_url = '{% url "api-ops:task-detail" pk=DEFAULT_PK %}'.replace('{{ DEFAULT_PK }}', uid); var the_url = '{% url "api-ops:task-detail" pk=DEFAULT_PK %}'.replace('{{ DEFAULT_PK }}', uid);
objectDelete($this, name, the_url); objectDelete($this, name, the_url);
}).on('click', '.btn-run', function () {
var $this = $(this);
var name = $this.closest("tr").find(":nth-child(2)").children('a').html();
var uid = $this.data('uid');
var the_url = '{% url "api-ops:task-run" pk=DEFAULT_PK %}'.replace('{{ DEFAULT_PK }}', uid);
var error = function (data) {
alert(data)
};
var success = function () {
setTimeout(function () {
console.log("ok")
}, 1000);
window.location = "{% url 'ops:task-detail' pk=DEFAULT_PK %}".replace('{{ DEFAULT_PK }}', uid);
};
APIUpdateAttr({
url: the_url,
error: error,
method: 'GET',
success: success,
success_message: "{% trans 'Task start: ' %}" + " " + name
});
}) })
</script> </script>
{% endblock %} {% endblock %}

View File

@ -1,6 +1,7 @@
# ~*~ coding: utf-8 ~*~ # ~*~ coding: utf-8 ~*~
from __future__ import unicode_literals from __future__ import unicode_literals
from django.conf.urls import url
from rest_framework.routers import DefaultRouter from rest_framework.routers import DefaultRouter
from .. import api from .. import api
@ -12,6 +13,8 @@ router.register(r'v1/tasks', api.TaskViewSet, 'task')
router.register(r'v1/adhoc', api.AdHocViewSet, 'adhoc') router.register(r'v1/adhoc', api.AdHocViewSet, 'adhoc')
router.register(r'v1/history', api.AdHocRunHistorySet, 'history') router.register(r'v1/history', api.AdHocRunHistorySet, 'history')
urlpatterns = [] urlpatterns = [
url(r'^v1/tasks/(?P<pk>[0-9a-zA-Z\-]{36})/run/$', api.TaskRun.as_view(), name='task-run'),
]
urlpatterns += router.urls urlpatterns += router.urls

View File

@ -6,9 +6,10 @@ from django.views.generic import ListView, DetailView
from common.mixins import DatetimeSearchMixin from common.mixins import DatetimeSearchMixin
from .models import Task, AdHoc, AdHocRunHistory from .models import Task, AdHoc, AdHocRunHistory
from .hands import AdminUserRequiredMixin
class TaskListView(DatetimeSearchMixin, ListView): class TaskListView(AdminUserRequiredMixin, DatetimeSearchMixin, ListView):
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
model = Task model = Task
ordering = ('-date_created',) ordering = ('-date_created',)
@ -42,7 +43,7 @@ class TaskListView(DatetimeSearchMixin, ListView):
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)
class TaskDetailView(DetailView): class TaskDetailView(AdminUserRequiredMixin, DetailView):
model = Task model = Task
template_name = 'ops/task_detail.html' template_name = 'ops/task_detail.html'
@ -55,7 +56,7 @@ class TaskDetailView(DetailView):
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)
class TaskAdhocView(DetailView): class TaskAdhocView(AdminUserRequiredMixin, DetailView):
model = Task model = Task
template_name = 'ops/task_adhoc.html' template_name = 'ops/task_adhoc.html'
@ -68,7 +69,7 @@ class TaskAdhocView(DetailView):
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)
class TaskHistoryView(DetailView): class TaskHistoryView(AdminUserRequiredMixin, DetailView):
model = Task model = Task
template_name = 'ops/task_history.html' template_name = 'ops/task_history.html'
@ -81,7 +82,7 @@ class TaskHistoryView(DetailView):
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)
class AdHocDetailView(DetailView): class AdHocDetailView(AdminUserRequiredMixin, DetailView):
model = AdHoc model = AdHoc
template_name = 'ops/adhoc_detail.html' template_name = 'ops/adhoc_detail.html'
@ -94,7 +95,7 @@ class AdHocDetailView(DetailView):
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)
class AdHocHistoryView(DetailView): class AdHocHistoryView(AdminUserRequiredMixin, DetailView):
model = AdHoc model = AdHoc
template_name = 'ops/adhoc_history.html' template_name = 'ops/adhoc_history.html'
@ -107,7 +108,7 @@ class AdHocHistoryView(DetailView):
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)
class AdHocHistoryDetailView(DetailView): class AdHocHistoryDetailView(AdminUserRequiredMixin, DetailView):
model = AdHocRunHistory model = AdHocRunHistory
template_name = 'ops/adhoc_history_detail.html' template_name = 'ops/adhoc_history_detail.html'

View File

@ -15,7 +15,8 @@ class AssetPermissionForm(forms.ModelForm):
widget=forms.SelectMultiple( widget=forms.SelectMultiple(
attrs={'class': 'select2', 'data-placeholder': _('Select users')}, attrs={'class': 'select2', 'data-placeholder': _('Select users')},
), ),
label=_("User") label=_("User"),
required=False,
) )
class Meta: class Meta:
@ -41,35 +42,54 @@ class AssetPermissionForm(forms.ModelForm):
help_texts = { help_texts = {
'name': '* required', 'name': '* required',
'system_users': '* required', 'system_users': '* required',
'user_groups': _('User or user group at least one required'),
'asset_groups': _('Asset or Asset group at least one required'),
} }
def clean_user_groups(self):
users = self.cleaned_data.get('users')
user_groups = self.cleaned_data.get('user_groups')
if not users and not user_groups:
raise forms.ValidationError(_("User or group at least one required"))
return self.cleaned_data["user_groups"]
def clean_asset_groups(self):
assets = self.cleaned_data.get('assets')
asset_groups = self.cleaned_data.get('asset_groups')
if not assets and not asset_groups:
raise forms.ValidationError(_("Asset or group at least one required"))
return self.cleaned_data["asset_groups"]
def clean_system_users(self): def clean_system_users(self):
from assets.utils import check_assets_have_system_user from assets.utils import check_assets_have_system_user
errors = [] errors = []
assets = self.cleaned_data['assets'] assets = self.cleaned_data['assets']
asset_groups = self.cleaned_data['asset_groups'] asset_groups = self.cleaned_data.get('asset_groups')
system_users = self.cleaned_data['system_users'] system_users = self.cleaned_data.get('system_users')
if not asset_groups and not assets:
return self.cleaned_data.get("system_users")
error_data = check_assets_have_system_user(assets, system_users) error_data = check_assets_have_system_user(assets, system_users)
if error_data: if error_data:
for asset, system_users in error_data.items(): for asset, system_users in error_data.items():
msg = _("Asset {} not have [{}] system users, please check \n") msg = _("Asset {} of cluster {} not have [{}] system users, please check \n")
error = forms.ValidationError(msg.format( error = forms.ValidationError(msg.format(
asset.hostname, asset.hostname,
asset.cluster.name,
", ".join(system_user.name for system_user in system_users) ", ".join(system_user.name for system_user in system_users)
)) ))
errors.append(error) errors.append(error)
for group in asset_groups: for group in asset_groups:
msg = _("Asset {}: {} not have [{}] system users, please check") msg = _("Asset {}(group {}) of cluster {} not have [{}] system users, please check \n")
assets = group.assets.all() assets = group.assets.all()
error_data = check_assets_have_system_user(assets, system_users) error_data = check_assets_have_system_user(assets, system_users)
for asset, system_users in error_data.items(): for asset, system_users in error_data.items():
errors.append(msg.format( errors.append(msg.format(
group.name, asset.hostname, asset.hostname, group.name, asset.cluster.name,
", ".join(system_user.name for system_user in system_users) ", ".join(system_user.name for system_user in system_users)
)) ))
if errors: if errors:

View File

@ -3,8 +3,8 @@
{% load i18n %} {% load i18n %}
{% block custom_head_css_js %} {% block custom_head_css_js %}
<link href="{% static "css/plugins/select2/select2.min.css" %}" rel="stylesheet"> <link href="{% static 'css/plugins/select2/select2.min.css' %}" rel="stylesheet">
<script src="{% static "js/plugins/select2/select2.full.min.js" %}"></script> <script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<div class="wrapper wrapper-content animated fadeInRight"> <div class="wrapper wrapper-content animated fadeInRight">
@ -53,8 +53,6 @@
<tr> <tr>
<th>{% trans 'Hostname' %}</th> <th>{% trans 'Hostname' %}</th>
<th>{% trans 'IP' %}</th> <th>{% trans 'IP' %}</th>
<th>{% trans 'Port' %}</th>
<th>{% trans 'Is valid' %}</th>
<th></th> <th></th>
</tr> </tr>
</thead> </thead>
@ -63,15 +61,6 @@
<tr> <tr>
<td>{{ asset.hostname }}</td> <td>{{ asset.hostname }}</td>
<td>{{ asset.ip }}</td> <td>{{ asset.ip }}</td>
<td>{{ user.port }}</td>
<td>
{% if asset.is_active %}
<i class="fa fa-times text-danger"></i>
{% else %}
<i class="fa fa-check text-navy"></i>
{% endif %}
</td>
<td> <td>
<button title="{{ asset.inherit_from_asset_groups }}" data-gid="{{ asset.id }}" class="btn btn-danger btn-xs btn-remove-asset {% if asset.is_inherit_from_asset_groups %} disabled {% endif %}" type="button" style="float: right;"><i class="fa fa-minus"></i></button> <button title="{{ asset.inherit_from_asset_groups }}" data-gid="{{ asset.id }}" class="btn btn-danger btn-xs btn-remove-asset {% if asset.is_inherit_from_asset_groups %} disabled {% endif %}" type="button" style="float: right;"><i class="fa fa-minus"></i></button>
</td> </td>

View File

@ -3,8 +3,8 @@
{% load i18n %} {% load i18n %}
{% block custom_head_css_js %} {% block custom_head_css_js %}
<link href="{% static "css/plugins/select2/select2.min.css" %}" rel="stylesheet"> <link href="{% static 'css/plugins/select2/select2.min.css' %}" rel="stylesheet">
<script src="{% static "js/plugins/select2/select2.full.min.js" %}"></script> <script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
@ -27,11 +27,11 @@
<i class="fa fa-bar-chart-o"></i> {% trans 'Assets and asset groups' %}</a> <i class="fa fa-bar-chart-o"></i> {% trans 'Assets and asset groups' %}</a>
</li> </li>
<li class="pull-right"> <li class="pull-right">
<a class="btn btn-outline btn-default" href="{% url 'perms:asset-permission-update' pk=asset_permission.id %}"><i class="fa fa-edit"></i>Update</a> <a class="btn btn-outline btn-default" href="{% url 'perms:asset-permission-update' pk=asset_permission.id %}"><i class="fa fa-edit"></i>{% trans 'Update' %}</a>
</li> </li>
<li class="pull-right"> <li class="pull-right">
<a class="btn btn-outline btn-danger btn-delete-perm"> <a class="btn btn-outline btn-danger btn-delete-perm">
<i class="fa fa-edit"></i>Delete <i class="fa fa-trash-o"></i>{% trans 'Delete' %}
</a> </a>
</li> </li>
</ul> </ul>
@ -113,7 +113,7 @@
<table class="table"> <table class="table">
<tbody> <tbody>
<tr class="no-borders-tr"> <tr class="no-borders-tr">
<td width="50%">Active:</td> <td width="50%">{% trans 'Active' %} :</td>
<td><span style="float: right"> <td><span style="float: right">
<div class="switch"> <div class="switch">
<div class="onoffswitch"> <div class="onoffswitch">
@ -139,8 +139,8 @@
<table class="table" id="system-user-table"> <table class="table" id="system-user-table">
<tbody> <tbody>
<form> <form>
<tr> <tr class="no-borders-tr">
<td colspan="2" class="no-borders"> <td colspan="2">
<select data-placeholder="{% trans 'Select system users' %}" class="select2" style="width: 100%" multiple="" tabindex="4"> <select data-placeholder="{% trans 'Select system users' %}" class="select2" style="width: 100%" multiple="" tabindex="4">
{% for system_user in system_users_remain %} {% for system_user in system_users_remain %}
<option value="{{ system_user.id }}" id="opt_{{ system_user.id }}">{{ system_user.name }}</option> <option value="{{ system_user.id }}" id="opt_{{ system_user.id }}">{{ system_user.name }}</option>
@ -148,15 +148,15 @@
</select> </select>
</td> </td>
</tr> </tr>
<tr> <tr class="no-borders-tr">
<td colspan="2" class="no-borders"> <td colspan="2">
<button type="button" class="btn btn-info btn-small" id="btn-add-system-user">{% trans 'Add' %}</button> <button type="button" class="btn btn-info btn-small" id="btn-add-system-user">{% trans 'Add' %}</button>
</td> </td>
</tr> </tr>
</form> </form>
{% for system_user in system_users %} {% for system_user in system_users %}
<tr> <tr {% if forloop.counter == 1 %} class="no-borders-tr" {% endif %} >
<td ><b class="bdg-system-user" data-uid={{ system_user.id }}>{{ system_user.name }}</b></td> <td ><b class="bdg-system-user" data-uid={{ system_user.id }}>{{ system_user.name }}</b></td>
<td> <td>
<button class="btn btn-danger btn-xs btn-remove-user" data-uid="{{ system_user.id }}" type="button" style="float: right;"><i class="fa fa-minus"></i></button> <button class="btn btn-danger btn-xs btn-remove-user" data-uid="{{ system_user.id }}" type="button" style="float: right;"><i class="fa fa-minus"></i></button>
@ -237,6 +237,16 @@ $(document).ready(function () {
}).get(); }).get();
updateSystemUser(system_users); updateSystemUser(system_users);
$tr.remove() $tr.remove()
}).on('click', '#is_active', function () {
var the_url = '{% url "api-perms:asset-permission-detail" pk=asset_permission.id %}';
var checked = $(this).prop('checked');
var body = {
'is_active': checked
};
APIUpdateAttr({
url: the_url,
body: JSON.stringify(body),
});
}) })
</script> </script>
{% endblock %} {% endblock %}

View File

@ -69,9 +69,11 @@ function initTable() {
$(td).html('<i class="fa fa-check text-navy"></i>') $(td).html('<i class="fa fa-check text-navy"></i>')
} }
}}, }},
{targets: 8, createdCell: function (td, cellData) { {targets: 8, createdCell: function (td, cellData, rowData) {
var update_btn = '<a href="{% url "perms:asset-permission-update" pk=DEFAULT_PK %}" class="btn btn-xs m-l-xs btn-info">{% trans "Update" %}</a>'.replace('{{ DEFAULT_PK }}', cellData); var update_btn = '<a href="{% url "perms:asset-permission-update" pk=DEFAULT_PK %}" class="btn btn-xs m-l-xs btn-info">{% trans "Update" %}</a>'.replace('{{ DEFAULT_PK }}', cellData);
var del_btn = '<a class="btn btn-xs btn-danger m-l-xs btn-del-permission" data-uid="{{ DEFAULT_PK }}">{% trans "Delete" %}</a>'.replace('{{ DEFAULT_PK }}', cellData); var del_btn = '<a class="btn btn-xs btn-danger m-l-xs btn-del" data-uid="{{ DEFAULT_PK }}" data-name="99991938">{% trans "Delete" %}</a>'
.replace('{{ DEFAULT_PK }}', cellData)
.replace('99991938', rowData.name);
$(td).html(update_btn + del_btn); $(td).html(update_btn + del_btn);
}} }}

View File

@ -3,8 +3,8 @@
{% load i18n %} {% load i18n %}
{% block custom_head_css_js %} {% block custom_head_css_js %}
<link href="{% static "css/plugins/select2/select2.min.css" %}" rel="stylesheet"> <link href="{% static 'css/plugins/select2/select2.min.css' %}" rel="stylesheet">
<script src="{% static "js/plugins/select2/select2.full.min.js" %}"></script> <script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<div class="wrapper wrapper-content animated fadeInRight"> <div class="wrapper wrapper-content animated fadeInRight">
@ -53,8 +53,6 @@
<tr> <tr>
<th>{% trans 'Name' %}</th> <th>{% trans 'Name' %}</th>
<th>{% trans 'Username' %}</th> <th>{% trans 'Username' %}</th>
<th>{% trans 'Email' %}</th>
<th>{% trans 'Is valid' %}</th>
<th></th> <th></th>
</tr> </tr>
</thead> </thead>
@ -63,15 +61,6 @@
<tr> <tr>
<td>{{ user.name }}</td> <td>{{ user.name }}</td>
<td>{{ user.username }}</td> <td>{{ user.username }}</td>
<td>{{ user.email }}</td>
<td>
{% if user.is_expired and user.is_active %}
<i class="fa fa-times text-danger"></i>
{% else %}
<i class="fa fa-check text-navy"></i>
{% endif %}
</td>
<td> <td>
<button class="btn btn-danger btn-xs btn-remove-user {% if user.is_inherit_from_user_groups %} disabled {% endif %}" data-gid="{{ user.id }}" type="button" style="float: right;"><i class="fa fa-minus"></i></button> <button class="btn btn-danger btn-xs btn-remove-user {% if user.is_inherit_from_user_groups %} disabled {% endif %}" data-gid="{{ user.id }}" type="button" style="float: right;"><i class="fa fa-minus"></i></button>
</td> </td>
@ -231,7 +220,6 @@ $(document).ready(function () {
$.map(jumpserver.users_selected, function(value, index) { $.map(jumpserver.users_selected, function(value, index) {
users_id.push(index); users_id.push(index);
}); });
console.log(users_id);
addUsers(users_id); addUsers(users_id);
}).on('click', '.btn-remove-user', function () { }).on('click', '.btn-remove-user', function () {
var user_id = $(this).data("gid"); var user_id = $(this).data("gid");

View File

@ -7,9 +7,7 @@ from .. import api
app_name = 'perms' app_name = 'perms'
router = routers.DefaultRouter() router = routers.DefaultRouter()
router.register('v1/asset-permissions', router.register('v1/asset-permissions', api.AssetPermissionViewSet, 'asset-permission')
api.AssetPermissionViewSet,
'asset-permission')
urlpatterns = [ urlpatterns = [
# 用户可以使用自己的Token或其它认证查看自己授权的资产,资产组等 # 用户可以使用自己的Token或其它认证查看自己授权的资产,资产组等

View File

@ -11,6 +11,7 @@ from django.contrib.messages.views import SuccessMessageMixin
from django.views.generic.detail import DetailView, SingleObjectMixin from django.views.generic.detail import DetailView, SingleObjectMixin
from django.contrib import messages from django.contrib import messages
from common.const import create_success_msg, update_success_msg
from .hands import AdminUserRequiredMixin, User, UserGroup, SystemUser, \ from .hands import AdminUserRequiredMixin, User, UserGroup, SystemUser, \
Asset, AssetGroup Asset, AssetGroup
from .models import AssetPermission from .models import AssetPermission
@ -31,46 +32,12 @@ class AssetPermissionListView(AdminUserRequiredMixin, ListView):
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)
class MessageMixin:
def form_valid(self, form):
response = super().form_valid(form)
errors = self.object.check_system_user_in_assets()
if errors:
message = self.get_warning_messages(errors)
messages.warning(self.request, message)
else:
message = self.get_success_message(form.cleaned_data)
messages.success(self.request, message)
success_message = self.get_success_message(form.cleaned_data)
if success_message:
messages.success(self.request, success_message)
return response
@staticmethod
def get_warning_messages(errors):
message = "<b><i class='fa fa-warning'></i>WARNING: System user " \
"should in behind clusters, so that " \
"system user cat auto push to the cluster assets:</b> <br>"
for system_user, clusters in errors.items():
message += " >>> {}: {} ".format(system_user.name, ", ".join((cluster.name for cluster in clusters)))
return message
def get_success_message(self, cleaned_data):
url = reverse_lazy('perms:asset-permission-detail',
kwargs={'pk': self.object.pk})
success_message = _(
'Create asset permission <a href="{url}"> {name} </a> '
'successfully.'.format(url=url, name=self.object.name))
return success_message
class AssetPermissionCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateView): class AssetPermissionCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateView):
model = AssetPermission model = AssetPermission
form_class = AssetPermissionForm form_class = AssetPermissionForm
template_name = 'perms/asset_permission_create_update.html' template_name = 'perms/asset_permission_create_update.html'
success_url = reverse_lazy('perms:asset-permission-list') success_url = reverse_lazy('perms:asset-permission-list')
warning = None success_message = create_success_msg
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = { context = {
@ -80,23 +47,13 @@ class AssetPermissionCreateView(AdminUserRequiredMixin, SuccessMessageMixin, Cre
kwargs.update(context) kwargs.update(context)
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)
def get_success_message(self, cleaned_data):
url = reverse_lazy(
'perms:asset-permission-detail',
kwargs={'pk': self.object.pk}
)
success_message = _(
'Create asset permission <a href="{url}"> {name} </a> '
'success.'.format(url=url, name=self.object.name)
)
return success_message
class AssetPermissionUpdateView(AdminUserRequiredMixin, SuccessMessageMixin, UpdateView): class AssetPermissionUpdateView(AdminUserRequiredMixin, SuccessMessageMixin, UpdateView):
model = AssetPermission model = AssetPermission
form_class = AssetPermissionForm form_class = AssetPermissionForm
template_name = 'perms/asset_permission_create_update.html' template_name = 'perms/asset_permission_create_update.html'
success_url = reverse_lazy("perms:asset-permission-list") success_url = reverse_lazy("perms:asset-permission-list")
success_message = update_success_msg
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = { context = {
@ -106,17 +63,6 @@ class AssetPermissionUpdateView(AdminUserRequiredMixin, SuccessMessageMixin, Upd
kwargs.update(context) kwargs.update(context)
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)
def get_success_message(self, cleaned_data):
url = reverse_lazy(
'perms:asset-permission-detail',
kwargs={'pk': self.object.pk}
)
success_message = _(
'Update asset permission <a href="{url}"> {name} </a> '
'success.'.format(url=url, name=self.object.name)
)
return success_message
class AssetPermissionDetailView(AdminUserRequiredMixin, DetailView): class AssetPermissionDetailView(AdminUserRequiredMixin, DetailView):
template_name = 'perms/asset_permission_detail.html' template_name = 'perms/asset_permission_detail.html'

View File

@ -61,7 +61,6 @@ function GetTableDataBox() {
id_list.push(i); id_list.push(i);
} }
} }
console.log(id_list);
for (i in id_list) { for (i in id_list) {
console.log(tabProduct); console.log(tabProduct);
tableData.push(GetRowData(tabProduct.rows[id_list[i]])); tableData.push(GetRowData(tabProduct.rows[id_list[i]]));
@ -357,5 +356,15 @@ String.prototype.format = function(args) {
function setCookie(key, value) { function setCookie(key, value) {
var expires = new Date(); var expires = new Date();
expires.setTime(expires.getTime() + (24 * 60 * 60 * 1000)); expires.setTime(expires.getTime() + (24 * 60 * 60 * 1000));
document.cookie = key + '=' + value + ';expires=' + expires.toUTCString(); document.cookie = key + '=' + value + ';expires=' + expires.toUTCString() + ';path=/';
}
function delCookie(key) {
var expires = new Date();
expires.setTime(expires.getTime() - 1);
var val = getCookie(key);
if (val !== null) {
document.cookie = key + '=' + val + ";expires" + expires.toUTCString() + ';path=/';
}
} }

View File

@ -3,8 +3,8 @@
{% load static %} {% load static %}
{% load bootstrap3 %} {% load bootstrap3 %}
{% block custom_head_css_js %} {% block custom_head_css_js %}
<link href="{% static "css/plugins/select2/select2.min.css" %}" rel="stylesheet"> <link href="{% static 'css/plugins/select2/select2.min.css' %}" rel="stylesheet">
<script src="{% static "js/plugins/select2/select2.full.min.js" %}"></script> <script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
{% block custom_head_css_js_create %} {% endblock %} {% block custom_head_css_js_create %} {% endblock %}
{% endblock %} {% endblock %}

View File

@ -3,8 +3,8 @@
{% load i18n %} {% load i18n %}
{% block custom_head_css_js %} {% block custom_head_css_js %}
<link href="{% static "css/plugins/datatables/datatables.min.css" %}" rel="stylesheet"> <link href="{% static "css/plugins/datatables/datatables.min.css" %}" rel="stylesheet">
<link href="{% static "css/plugins/select2/select2.min.css" %}" rel="stylesheet"> <link href="{% static 'css/plugins/select2/select2.min.css' %}" rel="stylesheet">
<script src="{% static "js/plugins/select2/select2.full.min.js" %}"></script> <script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
<script src="{% static "js/plugins/datatables/datatables.min.js" %}"></script> <script src="{% static "js/plugins/datatables/datatables.min.js" %}"></script>
{% endblock %} {% endblock %}
{% block content %} {% block content %}

View File

@ -3,11 +3,11 @@
<nav class="navbar navbar-static-top white-bg" role="navigation" style="margin-bottom: 0"> <nav class="navbar navbar-static-top white-bg" role="navigation" style="margin-bottom: 0">
<div class="navbar-header"> <div class="navbar-header">
<a class="navbar-minimalize minimalize-styl-2 btn btn-primary " href="#"><i class="fa fa-bars"></i> </a> <a class="navbar-minimalize minimalize-styl-2 btn btn-primary " href="#"><i class="fa fa-bars"></i> </a>
<form role="search" class="navbar-form-custom" method="get" action=""> <!--<form role="search" class="navbar-form-custom" method="get" action="">-->
<div class="form-group"> <!--<div class="form-group">-->
<input type="text" placeholder="{% trans 'Search' %}..." class="form-control" name="search" id="top-search"> <!--<input type="text" placeholder="{% trans 'Search' %}..." class="form-control" name="search" id="top-search">-->
</div> <!--</div>-->
</form> <!--</form>-->
</div> </div>
<ul class="nav navbar-top-links navbar-right"> <ul class="nav navbar-top-links navbar-right">
<li> <li>
@ -19,7 +19,7 @@
</a> </a>
</li> </li>
<li class="dropdown"> <li class="dropdown">
{% if user.is_authenticated %} {% if request.user.is_authenticated %}
<a data-toggle="dropdown" class="dropdown-toggle" href="#"> <a data-toggle="dropdown" class="dropdown-toggle" href="#">
<span class="m-r-sm text-muted welcome-message"> <span class="m-r-sm text-muted welcome-message">
<img alt="image" class="img-circle" width="40" height="40" src="{{ request.user.avatar_url }}"/> <img alt="image" class="img-circle" width="40" height="40" src="{{ request.user.avatar_url }}"/>
@ -30,17 +30,16 @@
</span> </span>
</a> </a>
<ul class="dropdown-menu animated fadeInRight m-t-xs"> <ul class="dropdown-menu animated fadeInRight m-t-xs">
<li><a href="{% url 'users:user-profile' %}">{% trans 'Profile' %}</a></li> <li><a href="{% url 'users:user-profile' %}"><i class="fa fa-cogs"> </i><span> {% trans 'Profile' %}</span></a></li>
<li><a href="{% url 'users:user-profile-update' %}">{% trans 'Profile settings' %}</a></li>
<li class="divider"></li> <li class="divider"></li>
{% if request.user.is_superuser %} {% if request.user.is_superuser %}
{% if request.COOKIES.IN_ADMIN_PAGE == 'No' %} {% if request.COOKIES.IN_ADMIN_PAGE == 'No' %}
<li><a id="switch_admin">{% trans 'Admin page' %}</a></li> <li><a id="switch_admin"><i class="fa fa-exchange"></i><span> {% trans 'Admin page' %}</span></a></li>
{% else %} {% else %}
<li><a id="switch_user">{% trans 'User page' %}</a></li> <li><a id="switch_user"><i class="fa fa-exchange"></i><span> {% trans 'User page' %}</span></a></li>
{% endif %}
{% endif %} {% endif %}
{% endif %} <li><a href="{% url 'users:logout' %}"><i class="fa fa-sign-out"></i> {% trans 'Logout' %}</a></li>
<li><a href="{% url 'users:logout' %}">{% trans 'Logout' %}</a></li>
</ul> </ul>
{% else %} {% else %}
<a href="{% url 'users:login' %}"> <a href="{% url 'users:login' %}">

View File

@ -18,16 +18,19 @@
$(document).ready(function () { $(document).ready(function () {
}) })
.on('click', '#switch_admin', function () { .on('click', '#switch_admin', function () {
var cookieName = "IN_ADMIN_PAGE";
setTimeout(function () { setTimeout(function () {
setCookie("IN_ADMIN_PAGE", "Yes"); delCookie(cookieName);
setCookie(cookieName, "Yes");
window.location = "/" window.location = "/"
}, 100) }, 100)
}) })
.on('click', '#switch_user', function () { .on('click', '#switch_user', function () {
var cookieName = "IN_ADMIN_PAGE";
setTimeout(function () { setTimeout(function () {
console.log("Set to No"); delCookie(cookieName);
setCookie("IN_ADMIN_PAGE", "No"); setCookie(cookieName, "No");
window.location = "/" window.location = "{% url 'assets:user-asset-list' %}"
}, 100); }, 100);
}) })
</script> </script>

View File

@ -110,7 +110,6 @@ class StatusViewSet(viewsets.ModelViewSet):
def handle_sessions(self): def handle_sessions(self):
sessions_active = [] sessions_active = []
for session_data in self.request.data.get("sessions", []): for session_data in self.request.data.get("sessions", []):
self.create_or_update_session(session_data) self.create_or_update_session(session_data)
if not session_data["is_finished"]: if not session_data["is_finished"]:
@ -165,7 +164,7 @@ class StatusViewSet(viewsets.ModelViewSet):
class SessionViewSet(viewsets.ModelViewSet): class SessionViewSet(viewsets.ModelViewSet):
queryset = Session.objects.all() queryset = Session.objects.all()
serializers_class = SessionSerializer serializer_class = SessionSerializer
permission_classes = (IsSuperUserOrAppUser,) permission_classes = (IsSuperUserOrAppUser,)
def get_queryset(self): def get_queryset(self):

View File

@ -12,7 +12,6 @@ class TerminalForm(forms.ModelForm):
model = Terminal model = Terminal
fields = ['name', 'remote_addr', 'ssh_port', 'http_port', 'comment'] fields = ['name', 'remote_addr', 'ssh_port', 'http_port', 'comment']
help_texts = { help_texts = {
'remote_addr': _('A unique addr of every terminal, user browser can arrive it'),
'ssh_port': _("Coco ssh listen port"), 'ssh_port': _("Coco ssh listen port"),
'http_port': _("Coco http/ws listen port"), 'http_port': _("Coco http/ws listen port"),
} }

View File

@ -93,6 +93,7 @@ class Session(models.Model):
asset = models.CharField(max_length=1024, verbose_name=_("Asset")) asset = models.CharField(max_length=1024, verbose_name=_("Asset"))
system_user = models.CharField(max_length=128, verbose_name=_("System user")) system_user = models.CharField(max_length=128, verbose_name=_("System user"))
login_from = models.CharField(max_length=2, choices=LOGIN_FROM_CHOICES, default="ST") login_from = models.CharField(max_length=2, choices=LOGIN_FROM_CHOICES, default="ST")
remote_addr = models.CharField(max_length=15, verbose_name=_("Remote addr"), blank=True, null=True)
is_finished = models.BooleanField(default=False) is_finished = models.BooleanField(default=False)
has_replay = models.BooleanField(default=False, verbose_name=_("Replay")) has_replay = models.BooleanField(default=False, verbose_name=_("Replay"))
has_command = models.BooleanField(default=False, verbose_name=_("Command")) has_command = models.BooleanField(default=False, verbose_name=_("Command"))

View File

@ -45,9 +45,9 @@
<thead> <thead>
<tr> <tr>
<th data-toggle="true">ID</th> <th data-toggle="true">ID</th>
<th>Command</th> <th>{% trans 'Command' %}</th>
<th data-hide="all"></th> <th data-hide="all"></th>
<th>Datetime</th> <th>{% trans 'Datetime' %}</th>
</tr> </tr>
</thead> </thead>
<tbody> <tbody>
@ -84,24 +84,24 @@
<table class="table"> <table class="table">
<tbody> <tbody>
{% if object.is_finished %} {% if object.is_finished %}
<tr> <tr class="no-borders-tr">
<td class="no-borders">{% trans 'Replay session' %}:</td> <td>{% trans 'Replay session' %}:</td>
<td class="no-borders"> <td>
<span class="pull-right"> <span class="pull-right">
<button type="button" class="btn btn-primary btn-xs" id="btn_reset_password" style="width: 54px">{% trans 'Go' %}</button> <button type="button" onclick="window.open('/luna/replay/{{ object.id }}','luna', 'height=600, width=800, top=0, left=0, toolbar=no, menubar=no, scrollbars=no, location=no, status=no')" class="btn btn-primary btn-xs" id="btn_reset_password" style="width: 54px">{% trans 'Go' %}</button>
</span> </span>
</td> </td>
</tr> </tr>
{% else %} {% else %}
<tr> <!--<tr>-->
<td class="no-borders" >{% trans 'Monitor session' %}:</td> <!--<td class="no-borders" >{% trans 'Monitor session' %}:</td>-->
<td class="no-borders" > <!--<td class="no-borders" >-->
<span class="pull-right"> <!--<span class="pull-right">-->
<button type="button" class="btn btn-primary btn-xs " style="width: 54px">{% trans 'Go' %}</button> <!--<button type="button" class="btn btn-primary btn-xs " style="width: 54px">{% trans 'Go' %}</button>-->
</span> <!--</span>-->
</td> <!--</td>-->
</tr> <!--</tr>-->
<tr> <tr class="no-borders-tr">
<td>{% trans 'Terminate session' %}:</td> <td>{% trans 'Terminate session' %}:</td>
<td> <td>
<span class="pull-right"> <span class="pull-right">

View File

@ -71,6 +71,7 @@
<th class="text-center">{% trans 'User' %}</th> <th class="text-center">{% trans 'User' %}</th>
<th class="text-center">{% trans 'Asset' %}</th> <th class="text-center">{% trans 'Asset' %}</th>
<th class="text-center">{% trans 'System user' %}</th> <th class="text-center">{% trans 'System user' %}</th>
<th class="text-center">{% trans 'Remote addr' %}</th>
<th class="text-center">{% trans 'Terminal' %}</th> <th class="text-center">{% trans 'Terminal' %}</th>
<th class="text-center">{% trans 'Command' %}</th> <th class="text-center">{% trans 'Command' %}</th>
<th class="text-center">{% trans 'Date start' %}</th> <th class="text-center">{% trans 'Date start' %}</th>
@ -88,6 +89,7 @@
<td class="text-center">{{ session.user }}</td> <td class="text-center">{{ session.user }}</td>
<td class="text-center">{{ session.asset }}</td> <td class="text-center">{{ session.asset }}</td>
<td class="text-center">{{ session.system_user }}</td> <td class="text-center">{{ session.system_user }}</td>
<td class="text-center">{{ session.remote_addr|default:"" }}</td>
<td class="text-center">{{ session.terminal.name }}</td> <td class="text-center">{{ session.terminal.name }}</td>
<td class="text-center">{{ session.id | get_session_command_amount }}</td> <td class="text-center">{{ session.id | get_session_command_amount }}</td>
@ -97,7 +99,7 @@
{% if session.is_finished %} {% if session.is_finished %}
<a onclick="window.open('/luna/replay/{{ session.id }}','luna', 'height=600, width=800, top=0, left=0, toolbar=no, menubar=no, scrollbars=no, location=no, status=no')" class="btn btn-xs btn-warning btn-replay" >{% trans "Replay" %}</a> <a onclick="window.open('/luna/replay/{{ session.id }}','luna', 'height=600, width=800, top=0, left=0, toolbar=no, menubar=no, scrollbars=no, location=no, status=no')" class="btn btn-xs btn-warning btn-replay" >{% trans "Replay" %}</a>
{% else %} {% else %}
<a onclick="window.open('/luna/monitor/{{ session.id }}','luna', 'height=600, width=800, top=0, left=0, toolbar=no, menubar=no, scrollbars=no, location=no, status=no')" class="btn btn-xs btn-warning btn-monitor" >{% trans "Monitor" %}</a> <!--<a onclick="window.open('/luna/monitor/{{ session.id }}','luna', 'height=600, width=800, top=0, left=0, toolbar=no, menubar=no, scrollbars=no, location=no, status=no')" class="btn btn-xs btn-warning btn-monitor" >{% trans "Monitor" %}</a>-->
<a class="btn btn-xs btn-danger btn-term" value="{{ session.id }}" terminal="{{ session.terminal.id }}" >{% trans "Terminate" %}</a> <a class="btn btn-xs btn-danger btn-term" value="{{ session.id }}" terminal="{{ session.terminal.id }}" >{% trans "Terminate" %}</a>
{% endif %} {% endif %}
</td> </td>

View File

@ -13,7 +13,7 @@
<a href="" class="text-center"><i class="fa fa-laptop"></i> {% trans 'Terminal detail' %} </a> <a href="" class="text-center"><i class="fa fa-laptop"></i> {% trans 'Terminal detail' %} </a>
</li> </li>
<li class="pull-right"> <li class="pull-right">
<a class="btn btn-outline btn-default" href="{% url 'terminal:terminal-update' pk=terminal.id %}"><i class="fa fa-edit"></i>Update</a> <a class="btn btn-outline btn-default" href="{% url 'terminal:terminal-update' pk=terminal.id %}"><i class="fa fa-edit"></i>{% trans 'Update' %}</a>
</li> </li>
</ul> </ul>
</div> </div>
@ -44,7 +44,7 @@
<td><b>{{ terminal.name }}</b></td> <td><b>{{ terminal.name }}</b></td>
</tr> </tr>
<tr> <tr>
<td>{% trans 'Remote address' %}:</td> <td>{% trans 'Remote addr' %}:</td>
<td><b>{{ terminal.remote_addr }}</b></td> <td><b>{{ terminal.remote_addr }}</b></td>
</tr> </tr>
<tr> <tr>

View File

@ -136,7 +136,6 @@ $(document).ready(function(){
}).on('click', '.btn-connect', function () { }).on('click', '.btn-connect', function () {
var $this = $(this); var $this = $(this);
var id = $this.data('id'); var id = $this.data('id');
console.log(id)
}) })
</script> </script>
{% endblock %} {% endblock %}

View File

@ -3,8 +3,8 @@
{% load static %} {% load static %}
{% load bootstrap3 %} {% load bootstrap3 %}
{% block custom_head_css_js %} {% block custom_head_css_js %}
<link href="{% static "css/plugins/select2/select2.min.css" %}" rel="stylesheet"> <link href="{% static 'css/plugins/select2/select2.min.css' %}" rel="stylesheet">
<script src="{% static "js/plugins/select2/select2.full.min.js" %}"></script> <script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
<link href="{% static "css/plugins/datepicker/datepicker3.css" %}" rel="stylesheet"> <link href="{% static "css/plugins/datepicker/datepicker3.css" %}" rel="stylesheet">
{% endblock %} {% endblock %}

View File

@ -1,7 +1,7 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
from django.views.generic import ListView, UpdateView, DeleteView, DetailView, TemplateView from django.views.generic import ListView
from django.views.generic.edit import SingleObjectMixin from django.views.generic.edit import SingleObjectMixin
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.utils import timezone from django.utils import timezone
@ -51,9 +51,9 @@ class SessionListView(AdminUserRequiredMixin, DatetimeSearchMixin, ListView):
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = { context = {
'user_list': utils.get_user_list_from_cache(), 'user_list': utils.get_session_user_list(),
'asset_list': utils.get_asset_list_from_cache(), 'asset_list': utils.get_session_asset_list(),
'system_user_list': utils.get_system_user_list_from_cache(), 'system_user_list': utils.get_session_system_user_list(),
'date_from': self.date_from, 'date_from': self.date_from,
'date_to': self.date_to, 'date_to': self.date_to,
'user': self.user, 'user': self.user,

View File

@ -128,7 +128,7 @@ class UserAuthApi(APIView):
user_agent = request.data.get('HTTP_USER_AGENT', '') user_agent = request.data.get('HTTP_USER_AGENT', '')
if not login_ip: if not login_ip:
login_ip = request.META.get("REMOTE_ADDR") login_ip = request.META.get('HTTP_X_FORWARDED_FOR') or request.META.get("REMOTE_ADDR")
user, msg = check_user_valid( user, msg = check_user_valid(
username=username, password=password, username=username, password=password,

View File

@ -29,7 +29,7 @@ class UserCreateUpdateForm(forms.ModelForm):
model = User model = User
fields = [ fields = [
'username', 'name', 'email', 'groups', 'wechat', 'username', 'name', 'email', 'groups', 'wechat',
'phone', 'role', 'date_expired', 'comment', 'password' 'phone', 'role', 'date_expired', 'comment',
] ]
help_texts = { help_texts = {
'username': '* required', 'username': '* required',
@ -38,13 +38,16 @@ class UserCreateUpdateForm(forms.ModelForm):
} }
widgets = { widgets = {
'groups': forms.SelectMultiple( 'groups': forms.SelectMultiple(
attrs={'class': 'select2', attrs={
'data-placeholder': _('Join user groups')}), 'class': 'select2',
'data-placeholder': _('Join user groups')
}
),
} }
def save(self, commit=True): def save(self, commit=True):
user = super().save(commit=commit)
password = self.cleaned_data.get('password') password = self.cleaned_data.get('password')
user = super().save(commit=commit)
if password: if password:
user.set_password(password) user.set_password(password)
user.save() user.save()
@ -184,12 +187,14 @@ class UserBulkUpdateForm(forms.ModelForm):
class UserGroupForm(forms.ModelForm): class UserGroupForm(forms.ModelForm):
users = forms.ModelMultipleChoiceField( users = forms.ModelMultipleChoiceField(
queryset=User.objects.all(), queryset=User.objects.all(),
label=_("User"),
widget=forms.SelectMultiple( widget=forms.SelectMultiple(
attrs={ attrs={
'class': 'select2', 'class': 'select2',
'data-placeholder': _('Select users') 'data-placeholder': _('Select users')
} }
) ),
required=False,
) )
def __init__(self, **kwargs): def __init__(self, **kwargs):

View File

@ -168,6 +168,11 @@ class User(AbstractUser):
token = PrivateToken.objects.create(user=self) token = PrivateToken.objects.create(user=self)
return token.key return token.key
def create_access_key(self):
from . import AccessKey
access_key = AccessKey.objects.create(user=self)
return access_key
def refresh_private_token(self): def refresh_private_token(self):
from .authentication import PrivateToken from .authentication import PrivateToken
PrivateToken.objects.filter(user=self).delete() PrivateToken.objects.filter(user=self).delete()
@ -214,13 +219,12 @@ class User(AbstractUser):
@classmethod @classmethod
def create_app_user(cls, name, comment): def create_app_user(cls, name, comment):
from . import AccessKey
app = cls.objects.create( app = cls.objects.create(
username=name, name=name, email='{}@local.domain'.format(name), username=name, name=name, email='{}@local.domain'.format(name),
is_active=False, role='App', enable_otp=False, comment=comment, is_active=False, role='App', enable_otp=False, comment=comment,
is_first_login=False, created_by='System' is_first_login=False, created_by='System'
) )
access_key = AccessKey.objects.create(user=app) access_key = app.create_access_key()
return app, access_key return app, access_key
@classmethod @classmethod

View File

@ -3,9 +3,9 @@
{% load i18n %} {% load i18n %}
{% block custom_head_css_js %} {% block custom_head_css_js %}
<link href="{% static "css/plugins/select2/select2.min.css" %}" rel="stylesheet"> <link href="{% static 'css/plugins/select2/select2.min.css' %}" rel="stylesheet">
<link href="{% static "css/plugins/sweetalert/sweetalert.css" %}" rel="stylesheet"> <link href="{% static "css/plugins/sweetalert/sweetalert.css" %}" rel="stylesheet">
<script src="{% static "js/plugins/select2/select2.full.min.js" %}"></script> <script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
<script src="{% static "js/plugins/sweetalert/sweetalert.min.js" %}"></script> <script src="{% static "js/plugins/sweetalert/sweetalert.min.js" %}"></script>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
@ -22,11 +22,11 @@
<a href="{% url 'users:user-granted-asset' pk=user_object.id %}" class="text-center"><i class="fa fa-cubes"></i> {% trans 'Asset granted' %}</a> <a href="{% url 'users:user-granted-asset' pk=user_object.id %}" class="text-center"><i class="fa fa-cubes"></i> {% trans 'Asset granted' %}</a>
</li> </li>
<li class="pull-right"> <li class="pull-right">
<a class="btn btn-outline btn-default" href="{% url 'users:user-update' pk=user_object.id %}"><i class="fa fa-edit"></i>Update</a> <a class="btn btn-outline btn-default" href="{% url 'users:user-update' pk=user_object.id %}"><i class="fa fa-edit"></i>{% trans 'Update' %}</a>
</li> </li>
<li class="pull-right"> <li class="pull-right">
<a class="btn btn-outline btn-danger btn-delete-user"> <a class="btn btn-outline btn-danger btn-delete-user">
<i class="fa fa-edit"></i>Delete <i class="fa fa-trash-o"></i>{% trans 'Delete' %}
</a> </a>
</li> </li>
</ul> </ul>

View File

@ -3,8 +3,8 @@
{% load i18n %} {% load i18n %}
{% load bootstrap3 %} {% load bootstrap3 %}
{% block custom_head_css_js %} {% block custom_head_css_js %}
<link href="{% static "css/plugins/select2/select2.min.css" %}" rel="stylesheet"> <link href="{% static 'css/plugins/select2/select2.min.css' %}" rel="stylesheet">
<script src="{% static "js/plugins/select2/select2.full.min.js" %}"></script> <script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
@ -25,20 +25,6 @@
{% csrf_token %} {% csrf_token %}
{% bootstrap_field form.name layout="horizontal" %} {% bootstrap_field form.name layout="horizontal" %}
{% bootstrap_field form.users layout="horizontal" %} {% bootstrap_field form.users layout="horizontal" %}
{# <div class="form-group">#}
{# <label for="users" class="col-sm-2 control-label">{% trans 'Users' %}</label>#}
{# <div class="col-sm-9">#}
{# <select name="users" id="id_users" data-placeholder="{% trans 'Select User' %}" class="select2 form-control m-b" multiple tabindex="2">#}
{# {% for user in users %}#}
{# {% if user.id in group_users %}#}
{# <option value="{{ user.id }}" selected>{{ user.name }}</option>#}
{# {% else %}#}
{# <option value="{{ user.id }}">{{ user.name }}</option>#}
{# {% endif %}#}
{# {% endfor %}#}
{# </select>#}
{# </div>#}
{# </div>#}
{% bootstrap_field form.comment layout="horizontal" %} {% bootstrap_field form.comment layout="horizontal" %}
<div class="form-group"> <div class="form-group">
<div class="col-sm-4 col-sm-offset-2"> <div class="col-sm-4 col-sm-offset-2">
@ -57,7 +43,9 @@
{% block custom_foot_js %} {% block custom_foot_js %}
<script> <script>
$(document).ready(function () { $(document).ready(function () {
$('.select2').select2(); $('.select2').select2({
closeOnSelect: false
});
}) })
</script> </script>
{% endblock %} {% endblock %}

View File

@ -3,11 +3,11 @@
{% load i18n %} {% load i18n %}
{% block custom_head_css_js %} {% block custom_head_css_js %}
<link href="{% static "css/plugins/select2/select2.min.css" %}" rel="stylesheet"> <link href="{% static 'css/plugins/select2/select2.min.css' %}" rel="stylesheet">
<link href="{% static "css/plugins/sweetalert/sweetalert.css" %}" rel="stylesheet"> <link href="{% static "css/plugins/sweetalert/sweetalert.css" %}" rel="stylesheet">
<link href="{% static "css/plugins/datatables/datatables.min.css" %}" rel="stylesheet"> <link href="{% static "css/plugins/datatables/datatables.min.css" %}" rel="stylesheet">
<link href="{% static "css/plugins/awesome-bootstrap-checkbox/awesome-bootstrap-checkbox.css" %}" rel="stylesheet"> <link href="{% static "css/plugins/awesome-bootstrap-checkbox/awesome-bootstrap-checkbox.css" %}" rel="stylesheet">
<script src="{% static "js/plugins/select2/select2.full.min.js" %}"></script> <script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
<script src="{% static "js/plugins/sweetalert/sweetalert.min.js" %}"></script> <script src="{% static "js/plugins/sweetalert/sweetalert.min.js" %}"></script>
<script src="{% static "js/plugins/datatables/datatables.min.js" %}"></script> <script src="{% static "js/plugins/datatables/datatables.min.js" %}"></script>
{% endblock %} {% endblock %}
@ -25,11 +25,11 @@
{# <a href="{% url 'users:user-group-granted-asset' pk=user_group.id %}" class="text-center"><i class="fa fa-cubes"></i> {% trans 'Asset granted' %}</a>#} {# <a href="{% url 'users:user-group-granted-asset' pk=user_group.id %}" class="text-center"><i class="fa fa-cubes"></i> {% trans 'Asset granted' %}</a>#}
{# </li>#} {# </li>#}
<li class="pull-right"> <li class="pull-right">
<a class="btn btn-outline btn-default" href="{% url 'users:user-group-update' pk=user_group.id %}"><i class="fa fa-edit"></i>Update</a> <a class="btn btn-outline btn-default" href="{% url 'users:user-group-update' pk=user_group.id %}"><i class="fa fa-edit"></i>{% trans 'Update' %}</a>
</li> </li>
<li class="pull-right"> <li class="pull-right">
<a class="btn btn-outline btn-danger btn-delete-user-group"> <a class="btn btn-outline btn-danger btn-delete-user-group">
<i class="fa fa-edit"></i>Delete <i class="fa fa-trash-o"></i>{% trans 'Delete' %}
</a> </a>
</li> </li>
</ul> </ul>
@ -171,7 +171,6 @@ $(document).ready(function () {
var users = $('.bdg_user').map(function() { var users = $('.bdg_user').map(function() {
return $(this).data('uid'); return $(this).data('uid');
}).get(); }).get();
console.log(users);
updateGroupMember(users) updateGroupMember(users)
}).on('click', '#btn_add_user', function() { }).on('click', '#btn_add_user', function() {
if (Object.keys(jumpserver.users_selected).length === 0) { if (Object.keys(jumpserver.users_selected).length === 0) {

View File

@ -4,8 +4,8 @@
{% load i18n %} {% load i18n %}
{% block custom_head_css_js %} {% block custom_head_css_js %}
<link href="{% static "css/plugins/select2/select2.min.css" %}" rel="stylesheet"> <link href="{% static 'css/plugins/select2/select2.min.css' %}" rel="stylesheet">
<script src="{% static "js/plugins/select2/select2.full.min.js" %}"></script> <script src="{% static 'js/plugins/select2/select2.full.min.js' %}"></script>
{% endblock %} {% endblock %}
{% block content %} {% block content %}
<div class="wrapper wrapper-content animated fadeInRight"> <div class="wrapper wrapper-content animated fadeInRight">

View File

@ -223,7 +223,7 @@ $(document).ready(function(){
var $this = $(this); var $this = $(this);
var name = $this.data('name'); var name = $this.data('name');
var uid = $this.data('uid'); var uid = $this.data('uid');
var the_url = '{% url "api-users:user-detail" pk='00000000-0000-0000-0000-000000000000' %}'.replace('00000000-0000-0000-0000-000000000000', uid); var the_url = '{% url "api-users:user-detail" pk=DEFAULT_PK %}'.replace("{{ DEFAULT_PK }}", uid);
objectDelete($this, name, the_url); objectDelete($this, name, the_url);
}) })
</script> </script>

View File

@ -2,17 +2,15 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from django import forms from django import forms
from django.shortcuts import reverse, redirect
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from django.urls import reverse_lazy from django.urls import reverse_lazy
from django.views.generic import ListView
from django.views.generic.base import TemplateView from django.views.generic.base import TemplateView
from django.views.generic.edit import CreateView, UpdateView, FormMixin from django.views.generic.edit import CreateView, UpdateView
from django.views.generic.detail import DetailView, SingleObjectMixin from django.views.generic.detail import DetailView
from django.contrib.messages.views import SuccessMessageMixin from django.contrib.messages.views import SuccessMessageMixin
from common.utils import get_logger from common.utils import get_logger
from perms.models import AssetPermission from common.const import create_success_msg, update_success_msg
from ..models import User, UserGroup from ..models import User, UserGroup
from ..utils import AdminUserRequiredMixin from ..utils import AdminUserRequiredMixin
from .. import forms from .. import forms
@ -39,7 +37,7 @@ class UserGroupCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateVie
form_class = forms.UserGroupForm form_class = forms.UserGroupForm
template_name = 'users/user_group_create_update.html' template_name = 'users/user_group_create_update.html'
success_url = reverse_lazy('users:user-group-list') success_url = reverse_lazy('users:user-group-list')
success_message = '<a href={url}> {name} </a> was created successfully' success_message = create_success_msg
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = { context = {
@ -49,21 +47,13 @@ class UserGroupCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateVie
kwargs.update(context) kwargs.update(context)
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)
def get_success_message(self, cleaned_data):
url = reverse_lazy(
'users:user-group-detail',
kwargs={'pk': self.object.id}
)
return self.success_message.format(
url=url, name=self.object.name
)
class UserGroupUpdateView(AdminUserRequiredMixin, SuccessMessageMixin, UpdateView):
class UserGroupUpdateView(AdminUserRequiredMixin, UpdateView):
model = UserGroup model = UserGroup
form_class = forms.UserGroupForm form_class = forms.UserGroupForm
template_name = 'users/user_group_create_update.html' template_name = 'users/user_group_create_update.html'
success_url = reverse_lazy('users:user-group-list') success_url = reverse_lazy('users:user-group-list')
success_message = update_success_msg
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
users = User.objects.all() users = User.objects.all()

View File

@ -53,7 +53,8 @@ class UserLoginView(FormView):
if not self.request.session.test_cookie_worked(): if not self.request.session.test_cookie_worked():
return HttpResponse(_("Please enable cookies and try again.")) return HttpResponse(_("Please enable cookies and try again."))
auth_login(self.request, form.get_user()) auth_login(self.request, form.get_user())
login_ip = self.request.META.get('REMOTE_ADDR', '') login_ip = self.request.META.get('HTTP_X_FORWARDED_FOR') or \
self.request.META.get('REMOTE_ADDR', '')
user_agent = self.request.META.get('HTTP_USER_AGENT', '') user_agent = self.request.META.get('HTTP_USER_AGENT', '')
write_login_log_async.delay( write_login_log_async.delay(
self.request.user.username, type='W', self.request.user.username, type='W',
@ -82,6 +83,7 @@ class UserLogoutView(TemplateView):
context = { context = {
'title': _('Logout success'), 'title': _('Logout success'),
'messages': _('Logout success, return login page'), 'messages': _('Logout success, return login page'),
'interval': 1,
'redirect_url': reverse('users:login'), 'redirect_url': reverse('users:login'),
'auto_redirect': True, 'auto_redirect': True,
} }

View File

@ -27,12 +27,14 @@ from django.views.generic.detail import DetailView, SingleObjectMixin
from django.views.decorators.csrf import csrf_exempt from django.views.decorators.csrf import csrf_exempt
from django.contrib.auth import logout as auth_logout from django.contrib.auth import logout as auth_logout
from common.const import create_success_msg, update_success_msg
from common.mixins import JSONResponseMixin
from common.utils import get_logger, get_object_or_none, is_uuid
from .. import forms from .. import forms
from ..models import User, UserGroup from ..models import User, UserGroup
from ..utils import AdminUserRequiredMixin from ..utils import AdminUserRequiredMixin
from ..signals import on_user_created from ..signals import on_user_created
from common.mixins import JSONResponseMixin
from common.utils import get_logger, get_object_or_none, is_uuid
__all__ = [ __all__ = [
'UserListView', 'UserCreateView', 'UserDetailView', 'UserListView', 'UserCreateView', 'UserDetailView',
@ -63,7 +65,7 @@ class UserCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateView):
form_class = forms.UserCreateUpdateForm form_class = forms.UserCreateUpdateForm
template_name = 'users/user_create.html' template_name = 'users/user_create.html'
success_url = reverse_lazy('users:user-list') success_url = reverse_lazy('users:user-list')
success_message = _('Create user <a href="{url}">{name}</a> successfully.') success_message = create_success_msg
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs) context = super().get_context_data(**kwargs)
@ -77,19 +79,14 @@ class UserCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateView):
on_user_created.send(self.__class__, user=user) on_user_created.send(self.__class__, user=user)
return super().form_valid(form) return super().form_valid(form)
def get_success_message(self, cleaned_data):
url = reverse_lazy('users:user-detail', kwargs={'pk': self.object.pk})
return self.success_message.format(
url=url, name=self.object.name
)
class UserUpdateView(AdminUserRequiredMixin, SuccessMessageMixin, UpdateView):
class UserUpdateView(AdminUserRequiredMixin, UpdateView):
model = User model = User
form_class = forms.UserCreateUpdateForm form_class = forms.UserCreateUpdateForm
template_name = 'users/user_update.html' template_name = 'users/user_update.html'
context_object_name = 'user_object' context_object_name = 'user_object'
success_url = reverse_lazy('users:user-list') success_url = reverse_lazy('users:user-list')
success_message = update_success_msg
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = {'app': _('Users'), 'action': _('Update user')} context = {'app': _('Users'), 'action': _('Update user')}
@ -332,17 +329,10 @@ class UserProfileUpdateView(LoginRequiredMixin, UpdateView):
model = User model = User
form_class = forms.UserProfileForm form_class = forms.UserProfileForm
success_url = reverse_lazy('users:user-profile') success_url = reverse_lazy('users:user-profile')
success_message = _('Create user <a href="{url}">{name}</a> successfully.')
def get_object(self, queryset=None): def get_object(self, queryset=None):
return self.request.user return self.request.user
def get_success_message(self, cleaned_data):
url = reverse_lazy('users:user-detail', kwargs={'pk': self.object.pk})
return self.success_message.format(
url=url, name=self.object.name
)
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
context = { context = {
'app': _('User'), 'app': _('User'),