Unverified Commit 09fbd3a5 authored by 老广's avatar 老广 Committed by GitHub

Merge pull request #1922 from jumpserver/dev

Dev
parents 0665644f ebecd005
...@@ -74,7 +74,7 @@ class AssetViewSet(IDInFilterMixin, LabelFilter, BulkModelViewSet): ...@@ -74,7 +74,7 @@ class AssetViewSet(IDInFilterMixin, LabelFilter, BulkModelViewSet):
.select_related('admin_user') .select_related('admin_user')
self.filter_admin_user_id() self.filter_admin_user_id()
self.filter_node() self.filter_node()
return self.queryset return self.queryset.distinct()
class AssetListUpdateApi(IDInFilterMixin, ListBulkCreateUpdateDestroyAPIView): class AssetListUpdateApi(IDInFilterMixin, ListBulkCreateUpdateDestroyAPIView):
......
...@@ -26,7 +26,7 @@ class CommandFilterRuleViewSet(BulkModelViewSet): ...@@ -26,7 +26,7 @@ class CommandFilterRuleViewSet(BulkModelViewSet):
fpk = self.kwargs.get('filter_pk') fpk = self.kwargs.get('filter_pk')
if not fpk: if not fpk:
return CommandFilterRule.objects.none() return CommandFilterRule.objects.none()
group = get_object_or_404(CommandFilter, pk=fpk) cmd_filter = get_object_or_404(CommandFilter, pk=fpk)
return group.rules.all().order_by('priority') return cmd_filter.rules.all()
...@@ -150,7 +150,7 @@ class SystemUserForm(OrgModelForm, PasswordAndKeyAuthForm): ...@@ -150,7 +150,7 @@ class SystemUserForm(OrgModelForm, PasswordAndKeyAuthForm):
'name': '* required', 'name': '* required',
'username': '* required', 'username': '* required',
'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, ' 'priority': _('1-100, High level will be using login asset as default, '
'if user was granted more than 2 system user'), 'if user was granted more than 2 system user'),
'login_mode': _('If you choose manual login mode, you do not ' 'login_mode': _('If you choose manual login mode, you do not '
'need to fill in the username and password.') 'need to fill in the username and password.')
......
...@@ -34,7 +34,8 @@ def default_cluster(): ...@@ -34,7 +34,8 @@ def default_cluster():
def default_node(): def default_node():
try: try:
from .node import Node from .node import Node
return Node.root() root = Node.root()
return root
except: except:
return None return None
......
...@@ -44,7 +44,7 @@ class CommandFilterRule(OrgModelMixin): ...@@ -44,7 +44,7 @@ class CommandFilterRule(OrgModelMixin):
id = models.UUIDField(default=uuid.uuid4, primary_key=True) id = models.UUIDField(default=uuid.uuid4, primary_key=True)
filter = models.ForeignKey('CommandFilter', on_delete=models.CASCADE, verbose_name=_("Filter"), related_name='rules') filter = models.ForeignKey('CommandFilter', on_delete=models.CASCADE, verbose_name=_("Filter"), related_name='rules')
type = models.CharField(max_length=16, default=TYPE_COMMAND, choices=TYPE_CHOICES, verbose_name=_("Type")) type = models.CharField(max_length=16, default=TYPE_COMMAND, choices=TYPE_CHOICES, verbose_name=_("Type"))
priority = models.IntegerField(default=50, verbose_name=_("Priority"), help_text=_("1-100, the lower will be match first"), priority = models.IntegerField(default=50, verbose_name=_("Priority"), help_text=_("1-100, the higher will be match first"),
validators=[MinValueValidator(1), MaxValueValidator(100)]) validators=[MinValueValidator(1), MaxValueValidator(100)])
content = models.TextField(max_length=1024, verbose_name=_("Content"), help_text=_("One line one command")) content = models.TextField(max_length=1024, verbose_name=_("Content"), help_text=_("One line one command"))
action = models.IntegerField(default=ACTION_DENY, choices=ACTION_CHOICES, verbose_name=_("Action")) action = models.IntegerField(default=ACTION_DENY, choices=ACTION_CHOICES, verbose_name=_("Action"))
...@@ -54,7 +54,7 @@ class CommandFilterRule(OrgModelMixin): ...@@ -54,7 +54,7 @@ class CommandFilterRule(OrgModelMixin):
created_by = models.CharField(max_length=128, blank=True, default='', verbose_name=_('Created by')) created_by = models.CharField(max_length=128, blank=True, default='', verbose_name=_('Created by'))
class Meta: class Meta:
ordering = ('priority', 'action') ordering = ('-priority', 'action')
def __str__(self): def __str__(self):
return '{} % {}'.format(self.type, self.content) return '{} % {}'.format(self.type, self.content)
...@@ -31,6 +31,8 @@ class Node(OrgModelMixin): ...@@ -31,6 +31,8 @@ class Node(OrgModelMixin):
return self.full_value return self.full_value
def __eq__(self, other): def __eq__(self, other):
if not other:
return False
return self.key == other.key return self.key == other.key
def __gt__(self, other): def __gt__(self, other):
...@@ -136,7 +138,7 @@ class Node(OrgModelMixin): ...@@ -136,7 +138,7 @@ class Node(OrgModelMixin):
args.append(Q(nodes__key__regex=pattern) | Q(nodes=None)) args.append(Q(nodes__key__regex=pattern) | Q(nodes=None))
else: else:
kwargs['nodes__key__regex'] = pattern kwargs['nodes__key__regex'] = pattern
assets = Asset.objects.filter(*args, **kwargs) assets = Asset.objects.filter(*args, **kwargs).distinct()
return assets return assets
def get_all_valid_assets(self): def get_all_valid_assets(self):
...@@ -201,13 +203,16 @@ class Node(OrgModelMixin): ...@@ -201,13 +203,16 @@ class Node(OrgModelMixin):
# 如果使用current_org 在set_current_org时会死循环 # 如果使用current_org 在set_current_org时会死循环
_current_org = get_current_org() _current_org = get_current_org()
with transaction.atomic(): with transaction.atomic():
if _current_org.is_default(): if _current_org.is_root():
key = '0' key = '0'
elif _current_org.is_default():
key = '1'
else: else:
set_current_org(Organization.root()) set_current_org(Organization.root())
org_nodes_roots = cls.objects.filter(key__regex=r'^[0-9]+$') org_nodes_roots = cls.objects.filter(key__regex=r'^[0-9]+$')
org_nodes_roots_keys = org_nodes_roots.values_list('key', flat=True) or [0] org_nodes_roots_keys = org_nodes_roots.values_list('key', flat=True) or ['1']
key = str(max([int(k) for k in org_nodes_roots_keys]) + 1) key = max([int(k) for k in org_nodes_roots_keys])
key = str(key + 1) if key != 0 else '2'
set_current_org(_current_org) set_current_org(_current_org)
root = cls.objects.create(key=key, value=_current_org.name) root = cls.objects.create(key=key, value=_current_org.name)
return root return root
...@@ -223,7 +228,7 @@ class Node(OrgModelMixin): ...@@ -223,7 +228,7 @@ class Node(OrgModelMixin):
@classmethod @classmethod
def default_node(cls): def default_node(cls):
defaults = {'value': 'Default'} defaults = {'value': 'Default'}
return cls.objects.get_or_create(defaults=defaults, key='0') return cls.objects.get_or_create(defaults=defaults, key='1')
@classmethod @classmethod
def get_tree_name_ref(cls): def get_tree_name_ref(cls):
......
...@@ -7,6 +7,7 @@ import logging ...@@ -7,6 +7,7 @@ import logging
from django.core.cache import cache from django.core.cache import cache
from django.db import models from django.db import models
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.core.validators import MinValueValidator, MaxValueValidator
from common.utils import get_signer from common.utils import get_signer
from ..const import SYSTEM_USER_CONN_CACHE_KEY from ..const import SYSTEM_USER_CONN_CACHE_KEY
...@@ -111,7 +112,8 @@ class SystemUser(AssetUser): ...@@ -111,7 +112,8 @@ class SystemUser(AssetUser):
nodes = models.ManyToManyField('assets.Node', blank=True, verbose_name=_("Nodes")) nodes = models.ManyToManyField('assets.Node', blank=True, verbose_name=_("Nodes"))
assets = models.ManyToManyField('assets.Asset', blank=True, verbose_name=_("Assets")) assets = models.ManyToManyField('assets.Asset', blank=True, verbose_name=_("Assets"))
priority = models.IntegerField(default=10, verbose_name=_("Priority")) priority = models.IntegerField(default=20, verbose_name=_("Priority"),
validators=[MinValueValidator(1), MaxValueValidator(100)])
protocol = models.CharField(max_length=16, choices=PROTOCOL_CHOICES, default='ssh', verbose_name=_('Protocol')) protocol = models.CharField(max_length=16, choices=PROTOCOL_CHOICES, default='ssh', verbose_name=_('Protocol'))
auto_push = models.BooleanField(default=True, verbose_name=_('Auto push')) auto_push = models.BooleanField(default=True, verbose_name=_('Auto push'))
sudo = models.TextField(default='/bin/whoami', verbose_name=_('Sudo')) sudo = models.TextField(default='/bin/whoami', verbose_name=_('Sudo'))
...@@ -168,7 +170,7 @@ class SystemUser(AssetUser): ...@@ -168,7 +170,7 @@ class SystemUser(AssetUser):
from .cmd_filter import CommandFilterRule from .cmd_filter import CommandFilterRule
rules = CommandFilterRule.objects.filter( rules = CommandFilterRule.objects.filter(
filter__in=self.cmd_filters.all() filter__in=self.cmd_filters.all()
).order_by('priority').distinct() ).distinct()
return rules return rules
@classmethod @classmethod
......
...@@ -69,6 +69,10 @@ ...@@ -69,6 +69,10 @@
<td>{% trans 'Port' %}:</td> <td>{% trans 'Port' %}:</td>
<td><b>{{ asset.port }}</b></td> <td><b>{{ asset.port }}</b></td>
</tr> </tr>
<tr>
<td>{% trans 'Protocol' %}:</td>
<td><b>{{ asset.protocol }}</b></td>
</tr>
<tr> <tr>
<td>{% trans 'Admin user' %}:</td> <td>{% trans 'Admin user' %}:</td>
<td><b>{{ asset.admin_user }}</b></td> <td><b>{{ asset.admin_user }}</b></td>
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
<div class="alert alert-info help-message"> <div class="alert alert-info help-message">
{% trans 'System user bound some command filter, each command filter has some rules,'%} {% trans 'System user bound some command filter, each command filter has some rules,'%}
{% trans 'When user login asset with this system user, then run a command,' %} {% trans 'When user login asset with this system user, then run a command,' %}
{% trans 'The command will be filter by rules, higher priority(lower number) rule run first,' %} {% trans 'The command will be filter by rules, higher priority rule run first,' %}
{% trans 'When a rule matched, if rule action is allow, then allow command execute,' %} {% trans 'When a rule matched, if rule action is allow, then allow command execute,' %}
{% trans 'else if action is deny, then command with be deny,' %} {% trans 'else if action is deny, then command with be deny,' %}
{% trans 'else match next rule, if none matched, allowed' %} {% trans 'else match next rule, if none matched, allowed' %}
......
...@@ -45,9 +45,6 @@ class AssetListView(AdminUserRequiredMixin, TemplateView): ...@@ -45,9 +45,6 @@ class AssetListView(AdminUserRequiredMixin, TemplateView):
template_name = 'assets/asset_list.html' template_name = 'assets/asset_list.html'
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
if current_org.is_default():
Node.default_node()
else:
Node.root() Node.root()
context = { context = {
'app': _('Assets'), 'app': _('Assets'),
......
...@@ -18,6 +18,9 @@ class BaseForm(forms.Form): ...@@ -18,6 +18,9 @@ class BaseForm(forms.Form):
db_value = getattr(common_settings, name) db_value = getattr(common_settings, name)
django_value = getattr(settings, name) if hasattr(settings, name) else None django_value = getattr(settings, name) if hasattr(settings, name) else None
if db_value is None and django_value is None:
continue
if db_value is False or db_value: if db_value is False or db_value:
if isinstance(db_value, dict): if isinstance(db_value, dict):
db_value = json.dumps(db_value) db_value = json.dumps(db_value)
......
...@@ -106,3 +106,8 @@ def to_dict(data): ...@@ -106,3 +106,8 @@ def to_dict(data):
def sort(data): def sort(data):
print(data) print(data)
return sorted(data) return sorted(data)
@register.filter
def subtract(value, arg):
return value - arg
This diff is collapsed.
...@@ -4,7 +4,6 @@ ...@@ -4,7 +4,6 @@
from werkzeug.local import Local from werkzeug.local import Local
from django.db import models from django.db import models
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.db.models import Q
from django.shortcuts import redirect from django.shortcuts import redirect
from django.forms import ModelForm from django.forms import ModelForm
from django.http.response import HttpResponseForbidden from django.http.response import HttpResponseForbidden
......
...@@ -96,7 +96,7 @@ class UserGrantedNodesApi(ListAPIView): ...@@ -96,7 +96,7 @@ class UserGrantedNodesApi(ListAPIView):
""" """
查询用户授权的所有节点的API, 如果是超级用户或者是 app,切换到root org 查询用户授权的所有节点的API, 如果是超级用户或者是 app,切换到root org
""" """
permission_classes = (IsOrgAdmin,) permission_classes = (IsOrgAdminOrAppUser,)
serializer_class = NodeSerializer serializer_class = NodeSerializer
def change_org_if_need(self): def change_org_if_need(self):
......
...@@ -145,7 +145,14 @@ function activeNav() { ...@@ -145,7 +145,14 @@ function activeNav() {
var resource = url_array[2]; var resource = url_array[2];
if (app === ''){ if (app === ''){
$('#index').addClass('active'); $('#index').addClass('active');
} else { }
else if (app === 'xpack') {
var item = url_array[3];
$("#" + app).addClass('active');
$('#' + app + ' #' + resource).addClass('active');
$('#' + app + ' #' + resource + ' #' + item + ' a').css('color', '#ffffff');
}
else {
$("#" + app).addClass('active'); $("#" + app).addClass('active');
$('#' + app + ' #' + resource).addClass('active'); $('#' + app + ' #' + resource).addClass('active');
} }
......
...@@ -95,7 +95,17 @@ ...@@ -95,7 +95,17 @@
</a> </a>
<ul class="nav nav-second-level"> <ul class="nav nav-second-level">
{% for plugin in XPACK_PLUGINS %} {% for plugin in XPACK_PLUGINS %}
{% ifequal plugin.name 'cloud'%}
<li id="{{ plugin.name }}">
<a href="#"><span class="nav-label">{% trans plugin.verbose_name %}</span><span class="fa arrow"></span></a>
<ul class="nav nav-third-level">
<li id="account"><a href="{% url 'xpack:cloud:account-list' %}">{% trans 'Account list' %}</a></li>
<li id="sync-instance-task"><a href="{% url 'xpack:cloud:sync-instance-task-list' %}">{% trans 'Sync instance' %}</a></li>
</ul>
</li>
{% else %}
<li id="{{ plugin.name }}"><a href="{{ plugin.endpoint }}">{% trans plugin.verbose_name %}</a></li> <li id="{{ plugin.name }}"><a href="{{ plugin.endpoint }}">{% trans plugin.verbose_name %}</a></li>
{% endifequal %}
{% endfor %} {% endfor %}
</ul> </ul>
</li> </li>
......
...@@ -9,8 +9,13 @@ ...@@ -9,8 +9,13 @@
<i class="fa fa-user" style="width: 14px"></i> <span class="nav-label">{% trans 'Profile' %}</span><span class="label label-info pull-right"></span> <i class="fa fa-user" style="width: 14px"></i> <span class="nav-label">{% trans 'Profile' %}</span><span class="label label-info pull-right"></span>
</a> </a>
</li> </li>
<li > <li>
<a href="{% url 'terminal:web-terminal' %}" target="_blank"><i class="fa fa-window-maximize" style="width: 14px"></i> <a href="{% url 'terminal:web-terminal' %}" target="_blank"><i class="fa fa-window-maximize" style="width: 14px"></i>
<span class="nav-label">{% trans 'Web terminal' %}</span> <span class="nav-label">{% trans 'Web terminal' %}</span>
</a> </a>
</li> </li>
<li>
<a href="{% url 'terminal:web-sftp' %}" target="_blank"><i class="fa fa-file" style="width: 14px"></i>
<span class="nav-label">{% trans 'File manager' %}</span>
</a>
</li>
\ No newline at end of file
...@@ -72,3 +72,5 @@ vine==1.1.4 ...@@ -72,3 +72,5 @@ vine==1.1.4
drf-yasg==1.9.1 drf-yasg==1.9.1
Werkzeug==0.14.1 Werkzeug==0.14.1
drf-nested-routers==0.90.2 drf-nested-routers==0.90.2
aliyun-python-sdk-core-v3==2.9.1
aliyun-python-sdk-ecs==4.10.1
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment