Unverified Commit 904a0f67 authored by 老广's avatar 老广 Committed by GitHub

Merge pull request #2422 from jumpserver/dev

Dev
parents 16db2abc 81e1ce26
# Generated by Django 2.1.7 on 2019-02-21 11:02
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('assets', '0024_auto_20181219_1614'),
]
operations = [
migrations.AlterModelOptions(
name='commandfilter',
options={'verbose_name': 'Command filter'},
),
migrations.AlterModelOptions(
name='commandfilterrule',
options={'ordering': ('-priority', 'action'), 'verbose_name': 'Command filter rule'},
),
]
...@@ -27,6 +27,9 @@ class CommandFilter(OrgModelMixin): ...@@ -27,6 +27,9 @@ class CommandFilter(OrgModelMixin):
def __str__(self): def __str__(self):
return self.name return self.name
class Meta:
verbose_name = _("Command filter")
class CommandFilterRule(OrgModelMixin): class CommandFilterRule(OrgModelMixin):
TYPE_REGEX = 'regex' TYPE_REGEX = 'regex'
...@@ -58,6 +61,7 @@ class CommandFilterRule(OrgModelMixin): ...@@ -58,6 +61,7 @@ class CommandFilterRule(OrgModelMixin):
class Meta: class Meta:
ordering = ('-priority', 'action') ordering = ('-priority', 'action')
verbose_name = _("Command filter rule")
@property @property
def _pattern(self): def _pattern(self):
......
...@@ -6,14 +6,17 @@ from django.dispatch import receiver ...@@ -6,14 +6,17 @@ from django.dispatch import receiver
from django.db import transaction from django.db import transaction
from jumpserver.utils import current_request from jumpserver.utils import current_request
from common.utils import get_request_ip from common.utils import get_request_ip, get_logger
from users.models import User from users.models import User
from .models import OperateLog, PasswordChangeLog from .models import OperateLog, PasswordChangeLog
logger = get_logger(__name__)
MODELS_NEED_RECORD = ( MODELS_NEED_RECORD = (
'User', 'UserGroup', 'Asset', 'Node', 'AdminUser', 'SystemUser', 'User', 'UserGroup', 'Asset', 'Node', 'AdminUser', 'SystemUser',
'Domain', 'Gateway', 'Organization', 'AssetPermission', 'Domain', 'Gateway', 'Organization', 'AssetPermission', 'CommandFilter',
'CommandFilterRule', 'License', 'Setting', 'Account', 'SyncInstanceTask',
) )
...@@ -26,11 +29,16 @@ def create_operate_log(action, sender, resource): ...@@ -26,11 +29,16 @@ def create_operate_log(action, sender, resource):
return return
resource_type = sender._meta.verbose_name resource_type = sender._meta.verbose_name
remote_addr = get_request_ip(current_request) remote_addr = get_request_ip(current_request)
data = {
"user": str(user), 'action': action, 'resource_type': resource_type,
'resource': str(resource), 'remote_addr': remote_addr,
}
with transaction.atomic(): with transaction.atomic():
OperateLog.objects.create( try:
user=user, action=action, resource_type=resource_type, OperateLog.objects.create(**data)
resource=resource, remote_addr=remote_addr except Exception as e:
) logger.error("Create operate log error: {}".format(e))
@receiver(post_save, dispatch_uid="my_unique_identifier") @receiver(post_save, dispatch_uid="my_unique_identifier")
......
...@@ -14,13 +14,16 @@ from .models import FTPLog, OperateLog, PasswordChangeLog, UserLoginLog ...@@ -14,13 +14,16 @@ from .models import FTPLog, OperateLog, PasswordChangeLog, UserLoginLog
def get_resource_type_list(): def get_resource_type_list():
from users.models import User, UserGroup from users.models import User, UserGroup
from assets.models import Asset, Node, AdminUser, SystemUser, Domain, Gateway from assets.models import (
Asset, Node, AdminUser, SystemUser, Domain, Gateway, CommandFilter,
CommandFilterRule,
)
from orgs.models import Organization from orgs.models import Organization
from perms.models import AssetPermission from perms.models import AssetPermission
models = [ models = [
User, UserGroup, Asset, Node, AdminUser, SystemUser, Domain, User, UserGroup, Asset, Node, AdminUser, SystemUser, Domain,
Gateway, Organization, AssetPermission Gateway, Organization, AssetPermission, CommandFilter, CommandFilterRule
] ]
return [model._meta.verbose_name for model in models] return [model._meta.verbose_name for model in models]
......
...@@ -16,13 +16,8 @@ class LDAPAuthorizationBackend(LDAPBackend): ...@@ -16,13 +16,8 @@ class LDAPAuthorizationBackend(LDAPBackend):
""" """
def authenticate(self, request=None, username=None, password=None, **kwargs): def authenticate(self, request=None, username=None, password=None, **kwargs):
if password or self.settings.PERMIT_EMPTY_PASSWORD: ldap_user = LDAPUser(self, username=username.strip(), request=request)
ldap_user = LDAPUser(self, username=username.strip(), request=request) user = self.authenticate_ldap_user(ldap_user, password)
user = self.authenticate_ldap_user(ldap_user, password)
else:
logger.debug('Rejecting empty password for {}'.format(username))
user = None
return user return user
def get_user(self, user_id): def get_user(self, user_id):
......
...@@ -96,7 +96,7 @@ class LDAPSettingForm(BaseForm): ...@@ -96,7 +96,7 @@ class LDAPSettingForm(BaseForm):
label=_("LDAP server"), label=_("LDAP server"),
) )
AUTH_LDAP_BIND_DN = forms.CharField( AUTH_LDAP_BIND_DN = forms.CharField(
label=_("Bind DN"), required=False, label=_("Bind DN"),
) )
AUTH_LDAP_BIND_PASSWORD = FormEncryptCharField( AUTH_LDAP_BIND_PASSWORD = FormEncryptCharField(
label=_("Password"), label=_("Password"),
......
# Generated by Django 2.1.7 on 2019-02-21 11:02
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('common', '0004_setting_encrypted'),
]
operations = [
migrations.AlterModelOptions(
name='setting',
options={'verbose_name': 'Setting'},
),
]
...@@ -123,3 +123,4 @@ class Setting(models.Model): ...@@ -123,3 +123,4 @@ class Setting(models.Model):
class Meta: class Meta:
db_table = "settings" db_table = "settings"
verbose_name = _("Setting")
...@@ -223,7 +223,7 @@ $(document).ready(function() { ...@@ -223,7 +223,7 @@ $(document).ready(function() {
field_of_all = [name_id, host_id, port_id, bucket_id, access_key_id, secret_key_id, container_name_id, account_name_id, account_key_id, endpoint_id, endpoint_suffix_id, region_id]; field_of_all = [name_id, host_id, port_id, bucket_id, access_key_id, secret_key_id, container_name_id, account_name_id, account_key_id, endpoint_id, endpoint_suffix_id, region_id];
need_get_field_of_server = [name_id]; need_get_field_of_server = [name_id];
need_get_field_of_s3 = [name_id, bucket_id, access_key_id, secret_key_id, region_id, endpoint_id]; need_get_field_of_s3 = [name_id, bucket_id, access_key_id, secret_key_id, endpoint_id];
need_get_field_of_oss = [name_id, bucket_id, access_key_id, secret_key_id, endpoint_id]; need_get_field_of_oss = [name_id, bucket_id, access_key_id, secret_key_id, endpoint_id];
need_get_field_of_azure = [name_id, container_name_id, account_name_id, account_key_id, endpoint_suffix_id]; need_get_field_of_azure = [name_id, container_name_id, account_name_id, account_key_id, endpoint_suffix_id];
need_get_field_of_ceph = [name_id, host_id, port_id, bucket_id, access_key_id, secret_key_id, region_id]; need_get_field_of_ceph = [name_id, host_id, port_id, bucket_id, access_key_id, secret_key_id, region_id];
...@@ -239,6 +239,11 @@ $(document).ready(function() { ...@@ -239,6 +239,11 @@ $(document).ready(function() {
}) })
.on('click', '#id_submit_button', function(){ .on('click', '#id_submit_button', function(){
$('#id_error').html('');
var submitBtn = $("#id_submit_button");
var origin_text = submitBtn.html();
submitBtn.addClass('disabled');
submitBtn.html("{% trans 'Submitting' %}");
var type = $('.selector').val(); var type = $('.selector').val();
var field = getFieldByType(type); var field = getFieldByType(type);
var data = {'TYPE': type}; var data = {'TYPE': type};
...@@ -249,13 +254,16 @@ $(document).ready(function() { ...@@ -249,13 +254,16 @@ $(document).ready(function() {
var url = "{% url 'api-common:replay-storage-create' %}"; var url = "{% url 'api-common:replay-storage-create' %}";
var success = function(data, textStatus) { var success = function(data, textStatus) {
location = "{% url 'common:terminal-setting' %}"; location = "{% url 'common:terminal-setting' %}";
submitBtn.removeClass('disabled');
submitBtn.html(origin_text);
}; };
var error = function(data, textStatus) { var error = function(data, textStatus) {
var error_msg = data.responseJSON.error; var error_msg = data.responseJSON.error;
$('#id_error').html(error_msg) $('#id_error').html(error_msg);
submitBtn.removeClass('disabled');
submitBtn.html(origin_text);
}; };
ajaxAPI(url, JSON.stringify(data), success, error) ajaxAPI(url, JSON.stringify(data), success, error);
}) })
</script> </script>
{% endblock %} {% endblock %}
...@@ -97,6 +97,14 @@ def is_bool_field(field): ...@@ -97,6 +97,14 @@ def is_bool_field(field):
return False return False
@register.filter
def is_image_field(field):
if isinstance(field, forms.ImageField):
return True
else:
return False
@register.filter @register.filter
def to_dict(data): def to_dict(data):
return dict(data) return dict(data)
......
...@@ -108,7 +108,6 @@ MIDDLEWARE = [ ...@@ -108,7 +108,6 @@ MIDDLEWARE = [
ROOT_URLCONF = 'jumpserver.urls' ROOT_URLCONF = 'jumpserver.urls'
TEMPLATES = [ TEMPLATES = [
{ {
'BACKEND': 'django.template.backends.django.DjangoTemplates', 'BACKEND': 'django.template.backends.django.DjangoTemplates',
...@@ -526,6 +525,7 @@ TERMINAL_ASSET_LIST_SORT_BY = CONFIG.TERMINAL_ASSET_LIST_SORT_BY ...@@ -526,6 +525,7 @@ TERMINAL_ASSET_LIST_SORT_BY = CONFIG.TERMINAL_ASSET_LIST_SORT_BY
TERMINAL_ASSET_LIST_PAGE_SIZE = CONFIG.TERMINAL_ASSET_LIST_PAGE_SIZE TERMINAL_ASSET_LIST_PAGE_SIZE = CONFIG.TERMINAL_ASSET_LIST_PAGE_SIZE
TERMINAL_SESSION_KEEP_DURATION = CONFIG.TERMINAL_SESSION_KEEP_DURATION TERMINAL_SESSION_KEEP_DURATION = CONFIG.TERMINAL_SESSION_KEEP_DURATION
TERMINAL_HOST_KEY = CONFIG.TERMINAL_HOST_KEY TERMINAL_HOST_KEY = CONFIG.TERMINAL_HOST_KEY
TERMINAL_HEADER_TITLE = CONFIG.TERMINAL_HEADER_TITLE
# Django bootstrap3 setting, more see http://django-bootstrap3.readthedocs.io/en/latest/settings.html # Django bootstrap3 setting, more see http://django-bootstrap3.readthedocs.io/en/latest/settings.html
BOOTSTRAP3 = { BOOTSTRAP3 = {
......
This diff is collapsed.
...@@ -16,7 +16,9 @@ from common.tree import TreeNode, TreeNodeSerializer ...@@ -16,7 +16,9 @@ from common.tree import TreeNode, TreeNodeSerializer
from common.utils import get_object_or_none from common.utils import get_object_or_none
from orgs.mixins import RootOrgViewMixin from orgs.mixins import RootOrgViewMixin
from orgs.utils import set_to_root_org from orgs.utils import set_to_root_org
from .utils import AssetPermissionUtil from .utils import (
AssetPermissionUtil, parse_asset_to_tree_node, parse_node_to_tree_node
)
from .models import AssetPermission from .models import AssetPermission
from .hands import ( from .hands import (
AssetGrantedSerializer, User, UserGroup, Asset, Node, AssetGrantedSerializer, User, UserGroup, Asset, Node,
...@@ -298,71 +300,6 @@ class UserGrantedNodesWithAssetsAsTreeApi(ListAPIView): ...@@ -298,71 +300,6 @@ class UserGrantedNodesWithAssetsAsTreeApi(ListAPIView):
self.system_user_id = request.query_params.get('system_user') self.system_user_id = request.query_params.get('system_user')
return super().get(request, *args, **kwargs) return super().get(request, *args, **kwargs)
@staticmethod
def parse_node_to_tree_node(node):
name = '{} ({})'.format(node.value, node.assets_amount)
node_serializer = serializers.GrantedNodeSerializer(node)
data = {
'id': node.key,
'name': name,
'title': name,
'pId': node.parent_key,
'isParent': True,
'open': node.is_root(),
'meta': {
'node': node_serializer.data,
'type': 'node'
}
}
tree_node = TreeNode(**data)
return tree_node
@staticmethod
def parse_asset_to_tree_node(node, asset, system_users):
system_users_protocol_matched = [s for s in system_users if s.protocol == asset.protocol]
icon_skin = 'file'
if asset.platform.lower() == 'windows':
icon_skin = 'windows'
elif asset.platform.lower() == 'linux':
icon_skin = 'linux'
system_users = []
for system_user in system_users_protocol_matched:
system_users.append({
'id': system_user.id,
'name': system_user.name,
'username': system_user.username,
'protocol': system_user.protocol,
'priority': system_user.priority,
'login_mode': system_user.login_mode,
'comment': system_user.comment,
})
data = {
'id': str(asset.id),
'name': asset.hostname,
'title': asset.ip,
'pId': node.key,
'isParent': False,
'open': False,
'iconSkin': icon_skin,
'meta': {
'system_users': system_users,
'type': 'asset',
'asset': {
'id': asset.id,
'hostname': asset.hostname,
'ip': asset.ip,
'port': asset.port,
'protocol': asset.protocol,
'platform': asset.platform,
'domain': None if not asset.domain else asset.domain.id,
'is_active': asset.is_active,
'comment': asset.comment
},
}
}
tree_node = TreeNode(**data)
return tree_node
def get_permissions(self): def get_permissions(self):
if self.kwargs.get('pk') is None: if self.kwargs.get('pk') is None:
self.permission_classes = (IsValidUser,) self.permission_classes = (IsValidUser,)
...@@ -381,12 +318,12 @@ class UserGrantedNodesWithAssetsAsTreeApi(ListAPIView): ...@@ -381,12 +318,12 @@ class UserGrantedNodesWithAssetsAsTreeApi(ListAPIView):
util.filter_permission_with_system_user(system_user=self.system_user_id) util.filter_permission_with_system_user(system_user=self.system_user_id)
nodes = util.get_nodes_with_assets() nodes = util.get_nodes_with_assets()
for node, assets in nodes.items(): for node, assets in nodes.items():
data = self.parse_node_to_tree_node(node) data = parse_node_to_tree_node(node)
queryset.append(data) queryset.append(data)
if not self.show_assets: if not self.show_assets:
continue continue
for asset, system_users in assets.items(): for asset, system_users in assets.items():
data = self.parse_asset_to_tree_node(node, asset, system_users) data = parse_asset_to_tree_node(node, asset, system_users)
queryset.append(data) queryset.append(data)
queryset = sorted(queryset) queryset = sorted(queryset)
return queryset return queryset
...@@ -490,6 +427,44 @@ class UserGroupGrantedNodesWithAssetsApi(ListAPIView): ...@@ -490,6 +427,44 @@ class UserGroupGrantedNodesWithAssetsApi(ListAPIView):
return queryset return queryset
class UserGroupGrantedNodesWithAssetsAsTreeApi(ListAPIView):
serializer_class = TreeNodeSerializer
permission_classes = (IsOrgAdminOrAppUser,)
show_assets = True
system_user_id = None
def change_org_if_need(self):
if self.request.user.is_superuser or \
self.request.user.is_app or \
self.kwargs.get('pk') is None:
set_to_root_org()
def get(self, request, *args, **kwargs):
self.show_assets = request.query_params.get('show_assets', '1') == '1'
self.system_user_id = request.query_params.get('system_user')
return super().get(request, *args, **kwargs)
def get_queryset(self):
self.change_org_if_need()
user_group_id = self.kwargs.get('pk', '')
queryset = []
group = get_object_or_404(UserGroup, id=user_group_id)
util = AssetPermissionUtil(group)
if self.system_user_id:
util.filter_permission_with_system_user(system_user=self.system_user_id)
nodes = util.get_nodes_with_assets()
for node, assets in nodes.items():
data = parse_node_to_tree_node(node)
queryset.append(data)
if not self.show_assets:
continue
for asset, system_users in assets.items():
data = parse_asset_to_tree_node(node, asset, system_users)
queryset.append(data)
queryset = sorted(queryset)
return queryset
class UserGroupGrantedNodeAssetsApi(ListAPIView): class UserGroupGrantedNodeAssetsApi(ListAPIView):
permission_classes = (IsOrgAdminOrAppUser,) permission_classes = (IsOrgAdminOrAppUser,)
serializer_class = AssetGrantedSerializer serializer_class = AssetGrantedSerializer
......
...@@ -43,6 +43,9 @@ urlpatterns = [ ...@@ -43,6 +43,9 @@ urlpatterns = [
path('user-group/<uuid:pk>/nodes-assets/', path('user-group/<uuid:pk>/nodes-assets/',
api.UserGroupGrantedNodesWithAssetsApi.as_view(), api.UserGroupGrantedNodesWithAssetsApi.as_view(),
name='user-group-nodes-assets'), name='user-group-nodes-assets'),
path('user-group/<uuid:pk>/nodes-assets/tree/',
api.UserGroupGrantedNodesWithAssetsAsTreeApi.as_view(),
name='user-group-nodes-assets-as-tree'),
path('user-group/<uuid:pk>/nodes/<uuid:node_id>/assets/', path('user-group/<uuid:pk>/nodes/<uuid:node_id>/assets/',
api.UserGroupGrantedNodeAssetsApi.as_view(), api.UserGroupGrantedNodeAssetsApi.as_view(),
name='user-group-node-assets'), name='user-group-node-assets'),
......
...@@ -5,6 +5,7 @@ from collections import defaultdict ...@@ -5,6 +5,7 @@ from collections import defaultdict
from django.db.models import Q from django.db.models import Q
from common.utils import get_logger from common.utils import get_logger
from common.tree import TreeNode
from .models import AssetPermission from .models import AssetPermission
from .hands import Node from .hands import Node
...@@ -193,3 +194,69 @@ def sort_assets(assets, order_by='hostname', reverse=False): ...@@ -193,3 +194,69 @@ def sort_assets(assets, order_by='hostname', reverse=False):
else: else:
assets = sorted(assets, key=lambda asset: getattr(asset, order_by), reverse=reverse) assets = sorted(assets, key=lambda asset: getattr(asset, order_by), reverse=reverse)
return assets return assets
def parse_node_to_tree_node(node):
from . import serializers
name = '{} ({})'.format(node.value, node.assets_amount)
node_serializer = serializers.GrantedNodeSerializer(node)
data = {
'id': node.key,
'name': name,
'title': name,
'pId': node.parent_key,
'isParent': True,
'open': node.is_root(),
'meta': {
'node': node_serializer.data,
'type': 'node'
}
}
tree_node = TreeNode(**data)
return tree_node
def parse_asset_to_tree_node(node, asset, system_users):
system_users_protocol_matched = [s for s in system_users if s.protocol == asset.protocol]
icon_skin = 'file'
if asset.platform.lower() == 'windows':
icon_skin = 'windows'
elif asset.platform.lower() == 'linux':
icon_skin = 'linux'
system_users = []
for system_user in system_users_protocol_matched:
system_users.append({
'id': system_user.id,
'name': system_user.name,
'username': system_user.username,
'protocol': system_user.protocol,
'priority': system_user.priority,
'login_mode': system_user.login_mode,
'comment': system_user.comment,
})
data = {
'id': str(asset.id),
'name': asset.hostname,
'title': asset.ip,
'pId': node.key,
'isParent': False,
'open': False,
'iconSkin': icon_skin,
'meta': {
'system_users': system_users,
'type': 'asset',
'asset': {
'id': asset.id,
'hostname': asset.hostname,
'ip': asset.ip,
'port': asset.port,
'protocol': asset.protocol,
'platform': asset.platform,
'domain': None if not asset.domain else asset.domain.id,
'is_active': asset.is_active,
'comment': asset.comment
},
}
}
tree_node = TreeNode(**data)
return tree_node
This diff is collapsed.
...@@ -131,10 +131,18 @@ table.dataTable tbody td.selected td i.text-navy { ...@@ -131,10 +131,18 @@ table.dataTable tbody td.selected td i.text-navy {
margin-right: 5px !important; margin-right: 5px !important;
} }
.m-m-m{
margin-left: 50px !important;
}
.m-10 { .m-10 {
margin: 10px !important; margin: 10px !important;
} }
.f-i-l-e{
/*float: left;*/
margin-left: 1000px !important;
/*padding-left: 1000px !important;*/
}
.m-t-10 { .m-t-10 {
margin-top: 10px !important; margin-top: 10px !important;
} }
......
.login_body {
filter: "progid:DXImageTransform.Microsoft.AlphaImageLoader(sizingMethod='scale')";
-moz-background-size: 100% 100%;
background-color: #f2f2f2;
width: 100%;
height: 100%;
}
.login-dialog {
margin-top: -moz-calc((100vh - 575px) / 2);
margin-top: -webkit-calc((100vh - 575px) / 2);
margin-top: calc((100vh - 575px) / 2);
margin-left: auto;
margin-right: auto;
}
.login-container {
width: 100%;
}
.input_shadow-1{
box-shadow:-2px 5px 5px #9999
}
.button-shadow{
box-shadow:-1px 3px 3px #9999 ;
}
.contact-form .form-control-1 {
background-color: transparent;
border: 1px solid #dedede;
box-shadow: none;
height: 45px !important;
color: #0c0c0c;
height: 30px;
font-family: -apple-system, BlinkMacSystemFont, "Neue Haas Grotesk Text Pro", "Arial Nova", "Segoe UI", "Helvetica Neue", ".PingFang SC", "PingFang SC", "Microsoft YaHei", "Microsoft JhengHei", "Source Han Sans SC", "Noto Sans CJK SC", "Source Han Sans CN", "Noto Sans SC", "Source Han Sans TC", "Noto Sans CJK TC", "Hiragino Sans GB", sans-serif;
font-size: 14px;
border-radius: 0;
}
.login-logo {
text-align: center;
height: 80px;
width: 500px;
display: table-cell;
vertical-align: middle;
}
.login-logo img {
max-height: 80px;
max-width: 500px;
}
.login-title {
margin-top: 40px;
text-align: left;
font-family: "Microsoft Yahei", sans-serif;
font-size: 16px;
color: #FFFFFF;
letter-spacing: 0;
}
.login-form {
padding: 16px 0;
}
.login-input {
position: relative;
height: 40px;
}
.login-input-control {
background-color: #FFFFFF;
border: 1px solid #0D6FD1;
position: absolute;
padding: 6px 6px 6px 62px;
border-radius: 2px;
width: 500px;
height: 40px;
font-size: 14px;
color: #2C3C4E;
letter-spacing: 0;
display: block;
}
.login-input-invalid {
color: #FF521B;
border-color: #FF521B;
}
.login-input-control:focus {
color: #0D6FD1;
border-color: #0D6FD1;
}
.login-input-control:-webkit-autofill {
-webkit-box-shadow: 0 0 0 50px white inset;
}
.login-input-prefix {
position: absolute;
top: 11px;
left: 20px;
line-height: 18px;
text-align: center;
width: 22px;
height: 20px;
padding: 0 1px;
z-index: 1;
}
.login-input-prefix .fa {
font-size: 20px;
}
.login-input-prefix .fa {
color: #0D6FD1;
}
.login-input-prefix img {
max-width: 20px;
}
.login-error-msg {
position: absolute;
top: 9px;
right: 40px;
line-height: 22px;
color: #FF521B;
font-size: 12px;
letter-spacing: -0.25px;
z-index: 1;
}
.login-input-postfix {
position: absolute;
top: 9px;
right: 10px;
line-height: 20px;
z-index: 1;
}
.login-input-postfix img {
width: 22px;
height: 22px;
}
.login-input-required .fa {
font-size: 20px;
vertical-align: middle;
}
.login-msg {
color: #FF521B;
height: 30px;
padding-right: 20px;
}
.btn-login {
color: #FFFFFF;
background-color: #23A9F6;
border-color: #23A9F6;
border-radius: 30px;
width: 500px;
height: 40px;
font-size: 14px;
letter-spacing: 0;
line-height: 18px;
}
.btn-login:focus,
.btn-login.focus {
color: #fff;
background-color: #23A9F6;
border-color: #23A9F6;
}
.btn-login:hover {
color: #fff;
background-color: #53C0FF;
border-color: #53C0FF;
}
.btn-login:active, .btn-login.active, .open > .dropdown-toggle.btn-login {
color: #fff;
background-color: #0D6FD1;
border-color: #0D6FD1;
}
.btn-login:active:hover,
.btn-login.active:hover,
.open > .dropdown-toggle.btn-login:hover,
.btn-login:active:focus,
.btn-login.active:focus,
.open > .dropdown-toggle.btn-login:focus,
.btn-login:active.focus,
.btn-login.active.focus,
.open > .dropdown-toggle.btn-login.focus {
color: #fff;
background-color: #53C0FF;
border-color: #53C0FF;
}
.btn-login:active,
.btn-login.active,
.open > .dropdown-toggle.btn-login {
background-image: none;
}
.btn-login.disabled,
.btn-login[disabled],
fieldset[disabled] .btn-login,
.btn-login.disabled:hover,
.btn-login[disabled]:hover,
fieldset[disabled] .btn-login:hover,
.btn-login.disabled:focus,
.btn-login[disabled]:focus,
fieldset[disabled] .btn-login:focus,
.btn-login.disabled.focus,
.btn-login[disabled].focus,
fieldset[disabled] .btn-login.focus,
.btn-login.disabled:active,
.btn-login[disabled]:active,
fieldset[disabled] .btn-login:active,
.btn-login.disabled.active,
.btn-login[disabled].active,
fieldset[disabled] .btn-login.active {
color: #fff;
background-color: #23A9F6;
border-color: #23A9F6;
opacity: 0.6;
}
.btn-login .badge {
color: #23A9F6;
background-color: #fff;
}
.contact-form .form-control {
background-color: transparent;
border: 1px solid #dedede;
box-shadow: none;
height: 45px !important;
color: #0c0c0c;
height: 38px;
font-family: -apple-system, BlinkMacSystemFont, "Neue Haas Grotesk Text Pro", "Arial Nova", "Segoe UI", "Helvetica Neue", ".PingFang SC", "PingFang SC", "Microsoft YaHei", "Microsoft JhengHei", "Source Han Sans SC", "Noto Sans CJK SC", "Source Han Sans CN", "Noto Sans SC", "Source Han Sans TC", "Noto Sans CJK TC", "Hiragino Sans GB", sans-serif;
font-size: 14px;
border-radius: 0;
}
.contact-form input:hover,
.contact-form textarea:hover,
.contact-form #contact-submit:hover {
border-color: #28ABE3;
}
.contact-form #contact-submit {
border: none;
padding: 15px 0;
width: 100%;
margin: 0;
background: #28ABE3;
color: #fff;
border-radius: 0;
}
.contact-form textarea.form-control {
padding: 10px;
height: 120px !important;
outline: none;
}
.btn-transparent {
color: #fff;
border: 1px solid #fff;
display: inline-block;
font-size: 13px;
letter-spacing: 1px;
padding: 14px 35px;
text-transform: uppercase;
border-radius: 40px;
background: #259980;
width: 100%;
}
.btn-transparent:hover {
color: #fff;
}
.border {
height: 2px;
margin: 20px auto 20px;
position: relative;
width: 80px;
background: #28ABE3;
}
...@@ -3,7 +3,11 @@ ...@@ -3,7 +3,11 @@
<li class="nav-header"> <li class="nav-header">
<div class="profile-element" style="height: 65px"> <div class="profile-element" style="height: 65px">
<div href="http://www.jumpserver.org" target="_blank" style="width: 100%; background-image: url({% static 'img/header-profile.png' %})"> <div href="http://www.jumpserver.org" target="_blank" style="width: 100%; background-image: url({% static 'img/header-profile.png' %})">
<img alt="logo" height="55" width="185" src="/static/img/logo-text.png"/> {% if interface and interface.logo_index %}
<img alt="logo" height="55" width="185" src="{{ MEDIA_URL }}{{ interface.logo_index }}"/>
{% else %}
<img alt="logo" height="55" width="185" src="{% static 'img/logo-text.png' %}"/>
{% endif %}
</div> </div>
</div> </div>
<div class="logo-element"> <div class="logo-element">
......
...@@ -5,8 +5,18 @@ ...@@ -5,8 +5,18 @@
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="renderer" content="webkit"> <meta name="renderer" content="webkit">
<title>Jumpserver</title> <title>
<link rel="shortcut icon" href="{% static 'img/facio.ico' %}" type="image/x-icon"> {% if interface and interface.login_title %}
{{ interface.login_title }}
{% else %}
Jumpserver
{% endif %}
</title>
{% if interface and interface.favicon %}
<link rel="shortcut icon" href="{{ MEDIA_URL }}{{ interface.favicon }}" type="image/x-icon">
{% else %}
<link rel="shortcut icon" href="{% static 'img/facio.ico' %}" type="image/x-icon">
{% endif %}
{% include '_head_css_js.html' %} {% include '_head_css_js.html' %}
<link href="{% static 'css/jumpserver.css' %}" rel="stylesheet"> <link href="{% static 'css/jumpserver.css' %}" rel="stylesheet">
{% block custom_head_css_js %} {% endblock %} {% block custom_head_css_js %} {% endblock %}
......
...@@ -22,8 +22,19 @@ ...@@ -22,8 +22,19 @@
<div class="col-md-12"> <div class="col-md-12">
<div class="ibox-content"> <div class="ibox-content">
<div> <div>
{% if interface and interface.logo_logout %}
<img src="{{ MEDIA_URL }}{{ interface.logo_logout }}" style="margin: auto" width="82" height="82">
{% else %}
<img src="{% static 'img/logo.png' %}" style="margin: auto" width="82" height="82"> <img src="{% static 'img/logo.png' %}" style="margin: auto" width="82" height="82">
<h2 style="display: inline">Jumpserver</h2> {% endif %}
<h2 style="display: inline">
{% if interface and interface.login_title %}
{{ interface.login_title }}
{% else %}
{% trans 'Welcome to the Jumpserver open source fortress' %}
{% endif %}
</h2>
</div> </div>
{% if errors %} {% if errors %}
<p> <p>
......
...@@ -22,8 +22,12 @@ ...@@ -22,8 +22,12 @@
<div class="col-md-12"> <div class="col-md-12">
<div class="ibox-content"> <div class="ibox-content">
{% if interface.logout_logo %}
<img src="{{ MEDIA_URL }}{{ interface.logout_logo }}" style="margin: auto" width="82" height="82">
{% else %}
<img src="{% static 'img/logo.png' %}" style="margin: auto" width="82" height="82">
{% endif %}
<img src="{% static 'img/logo.png' %}" style="margin: auto" width="82" height="82">
<h2 class="font-bold" style="display: inline">{% trans 'Forgot password' %} ?</h2> <h2 class="font-bold" style="display: inline">{% trans 'Forgot password' %} ?</h2>
<h1></h1> <h1></h1>
......
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
<head> <head>
<meta charset="utf-8"> <meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<title> Jumpserver </title> <title>Jumpserver</title>
<link rel="shortcut icon" href="{% static "img/facio.ico" %}" type="image/x-icon"> <link rel="shortcut icon" href="{% static "img/facio.ico" %}" type="image/x-icon">
{% include '_head_css_js.html' %} {% include '_head_css_js.html' %}
<link href="{% static "css/jumpserver.css" %}" rel="stylesheet"> <link href="{% static "css/jumpserver.css" %}" rel="stylesheet">
...@@ -24,7 +24,7 @@ ...@@ -24,7 +24,7 @@
<div class="row"> <div class="row">
<div class="col-md-6"> <div class="col-md-6">
<h2 class="font-bold">{% trans 'Welcome to the Jumpserver open source fortress' %}</h2> <h2 class="font-bold" style="text-align: center">{% trans 'Welcome to the Jumpserver open source fortress' %}</h2>
<p> <p>
{% trans "The world's first fully open source fortress, using the GNU GPL v2.0 open source protocol, is a professional operation and maintenance audit system in compliance with 4A." %} {% trans "The world's first fully open source fortress, using the GNU GPL v2.0 open source protocol, is a professional operation and maintenance audit system in compliance with 4A." %}
</p> </p>
...@@ -96,8 +96,7 @@ ...@@ -96,8 +96,7 @@
{% endif %} {% endif %}
</form> </form>
<p class="m-t">
</p>
</div> </div>
</div> </div>
</div> </div>
......
{% load static %}
{% load i18n %}
<!DOCTYPE html>
<html>
<!--/*@thymesVar id="LoginConstants" type="com.fit2cloud.support.common.constants.LoginConstants"*/-->
<!--/*@thymesVar id="message" type="java.lang.String"*/-->
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
{% if interface and interface.favicon %}
<link rel="shortcut icon" href="{{ MEDIA_URL }}{{ interface.favicon }}" type="image/x-icon">
{% else %}
<link rel="shortcut icon" href="{% static 'img/facio.ico' %}" type="image/x-icon">
{% endif %}
<title>
{% if interface and interface.login_title %}
{{ interface.login_title }}
{% else %}
Jumpserver
{% endif %}
</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- Stylesheets -->
<link href="{% static 'css/bootstrap.min.css' %}" rel="stylesheet">
<link href="{% static 'css/font-awesome.min.css' %}" rel="stylesheet">
<link href="{% static 'css/bootstrap-style.css' %}" rel="stylesheet">
<link href="{% static 'css/login-style.css' %}" rel="stylesheet">
<!-- scripts -->
<script src="{% static 'js/jquery-2.1.1.js' %}"></script>
<script src="{% static 'js/plugins/sweetalert/sweetalert.min.js' %}"></script>
<script src="{% static 'js/bootstrap.min.js' %}"></script>
<script src="{% static 'js/plugins/datatables/datatables.min.js' %}"></script>
{# <script src="{% static 'js/angular.min.js' %}"></script>#}
<style>
.captcha {
float: right;
}
</style>
</head>
<body>
<div class="login-dialog">
<div class="">
<div class="row" style="height: 472px">
<div class="col-md-4 col-md-offset-2 input_shadow-1" style="text-align: center;background-color: white; padding-right: 0px;height: 100%">
<div style="background-color: white">
{% if interface.login_title %}
<div style="margin-top: 40px">
<span style="font-size: 24px;font-weight:400;color: #151515;letter-spacing: 0;">{{ interface.login_title }}</span>
</div>
{% else %}
<div style="margin-top: 40px">
<span style="font-size: 24px;font-weight:400;color: #151515;letter-spacing: 0;">{% trans 'Welcome to the Jumpserver open source fortress' %}</span>
</div>
{% endif %}
<div style="font-size: 12px;color: #999999;letter-spacing: 0;line-height: 18px;margin-top: 10px">
{% trans 'Welcome back, please enter username and password to login' %}
</div>
<div style="margin-bottom: 10px">
<div>
<div class="col-md-1"></div>
<div class="contact-form col-md-10" style="margin-top: 20px;height: 35px">
<form id="contact-form" action="" method="post" role="form" novalidate="novalidate">
{% csrf_token %}
<div style="height: 48px;color: red">
{% if block_login %}
<p class="red-fonts">{% trans 'Log in frequently and try again later' %}</p>
{% elif password_expired %}
<p class="red-fonts">{% trans 'The user password has expired' %}</p>
{% elif form.errors %}
{% if 'captcha' in form.errors %}
<p class="red-fonts">{% trans 'Captcha invalid' %}</p>
{% else %}
<p class="red-fonts">{{ form.non_field_errors.as_text }}</p>
{% endif %}
{% endif %}
</div>
<div class="form-group">
<input type="text" class="form-control" name="{{ form.username.html_name }}"
placeholder="{% trans 'Username' %}" required=""
value="{% if form.username.value %}{{ form.username.value }}{% endif %}" style="height: 35px">
</div>
<div class="form-group">
<input type="password" class="form-control" name="{{ form.password.html_name }}"
placeholder="{% trans 'Password' %}" required="">
</div>
<div class="form-group" style="height: 50px;margin-bottom: 0px">
{{ form.captcha }}
</div>
<div class="form-group" style="margin-top: 10px">
<button type="submit" class="btn btn-transparent">{% trans 'Login' %}</button>
</div>
<div style="text-align: center">
<a href="{% url 'users:forgot-password' %}">
<small>{% trans 'Forgot password' %}?</small>
</a>
</div>
</form>
</div>
<div class="col-md-1"></div>
</div>
</div>
</div>
</div>
<div class="col-md-4 " style="padding-left: 0px; height: 100%">
{% if interface.login_image %}
<img src="{{ MEDIA_URL }}{{ interface.login_image }}" style="width: 100%; height: 100%;" class="input_shadow-1" />
{% else %}
<img src="{% static 'img/login/login-image.jpg' %}" style="width: 100%; height: 100%;" class="input_shadow-1" />
{% endif %}
</div>
</div>
<div class="col-md-2"></div>
</div>
</div>
</div>
</body>
</html>
\ No newline at end of file
...@@ -72,6 +72,7 @@ function initTable() { ...@@ -72,6 +72,7 @@ function initTable() {
} else { } else {
inited = true; inited = true;
} }
url = "{% url 'api-perms:user-group-assets' pk=object.id %}";
var options = { var options = {
ele: $('#user_assets_table'), ele: $('#user_assets_table'),
columnDefs: [ columnDefs: [
...@@ -106,15 +107,16 @@ function initTable() { ...@@ -106,15 +107,16 @@ function initTable() {
return asset_table return asset_table
} }
function onSelected(event, treeNode) { function onSelected(event, treeNode) {
url = '{% url "api-perms:user-group-node-assets" pk=object.id node_id=DEFAULT_PK %}'; url = '{% url "api-perms:user-group-node-assets" pk=object.id node_id=DEFAULT_PK %}';
url = url.replace("{{ DEFAULT_PK }}", treeNode.id); var node_id = treeNode.meta.node.id;
setCookie('node_selected', treeNode.id); url = url.replace("{{ DEFAULT_PK }}", node_id);
asset_table = initTable();
asset_table.ajax.url(url); asset_table.ajax.url(url);
asset_table.ajax.reload(); asset_table.ajax.reload();
} }
function selectQueryNode() { function selectQueryNode() {
var query_node_id = $.getUrlParam("node"); var query_node_id = $.getUrlParam("node");
var cookie_node_id = getCookie('node_selected'); var cookie_node_id = getCookie('node_selected');
...@@ -149,25 +151,16 @@ function initTree() { ...@@ -149,25 +151,16 @@ function initTree() {
} }
}; };
var zNodes = []; $.get("{% url 'api-perms:user-group-nodes-assets-as-tree' pk=object.id %}?show_assets=0", function(data, status) {
$.get("{% url 'api-perms:user-group-nodes' pk=object.id %}", function(data, status){ $.fn.zTree.init($("#assetTree"), setting, data);
$.each(data, function (index, value) {
value["pId"] = value["parent"];
if (value["key"] === "0") {
value["open"] = true;
}
value["name"] = value["value"]
});
zNodes = data;
$.fn.zTree.init($("#assetTree"), setting, zNodes);
zTree = $.fn.zTree.getZTreeObj("assetTree"); zTree = $.fn.zTree.getZTreeObj("assetTree");
rMenu = $("#rMenu");
selectQueryNode();
}); });
} }
$(document).ready(function () { $(document).ready(function () {
initTree(); initTree();
initTable();
}); });
</script> </script>
{% endblock %} {% endblock %}
# ~*~ coding: utf-8 ~*~ # ~*~ coding: utf-8 ~*~
from __future__ import unicode_literals from __future__ import unicode_literals
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
......
...@@ -42,12 +42,23 @@ __all__ = [ ...@@ -42,12 +42,23 @@ __all__ = [
@method_decorator(csrf_protect, name='dispatch') @method_decorator(csrf_protect, name='dispatch')
@method_decorator(never_cache, name='dispatch') @method_decorator(never_cache, name='dispatch')
class UserLoginView(FormView): class UserLoginView(FormView):
template_name = 'users/login.html'
form_class = forms.UserLoginForm form_class = forms.UserLoginForm
form_class_captcha = forms.UserLoginCaptchaForm form_class_captcha = forms.UserLoginCaptchaForm
redirect_field_name = 'next' redirect_field_name = 'next'
key_prefix_captcha = "_LOGIN_INVALID_{}" key_prefix_captcha = "_LOGIN_INVALID_{}"
def get_template_names(self):
template_name = 'users/login.html'
if not settings.XPACK_ENABLED:
return template_name
from xpack.plugins.license.models import License
if not License.has_valid_license():
return template_name
template_name = 'users/new_login.html'
return template_name
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
if request.user.is_staff: if request.user.is_staff:
return redirect(redirect_user_first_login_or_index( return redirect(redirect_user_first_login_or_index(
......
...@@ -60,7 +60,7 @@ pytz==2018.3 ...@@ -60,7 +60,7 @@ pytz==2018.3
PyYAML==3.12 PyYAML==3.12
redis==2.10.6 redis==2.10.6
requests==2.18.4 requests==2.18.4
jms-storage==0.0.20 jms-storage==0.0.22
s3transfer==0.1.13 s3transfer==0.1.13
simplejson==3.13.2 simplejson==3.13.2
six==1.11.0 six==1.11.0
......
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