diff --git a/apps/assets/migrations/0025_auto_20190221_1902.py b/apps/assets/migrations/0025_auto_20190221_1902.py new file mode 100644 index 000000000..7124bb509 --- /dev/null +++ b/apps/assets/migrations/0025_auto_20190221_1902.py @@ -0,0 +1,21 @@ +# 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'}, + ), + ] diff --git a/apps/assets/models/cmd_filter.py b/apps/assets/models/cmd_filter.py index ea2059d51..5bd031187 100644 --- a/apps/assets/models/cmd_filter.py +++ b/apps/assets/models/cmd_filter.py @@ -27,6 +27,9 @@ class CommandFilter(OrgModelMixin): def __str__(self): return self.name + class Meta: + verbose_name = _("Command filter") + class CommandFilterRule(OrgModelMixin): TYPE_REGEX = 'regex' @@ -58,6 +61,7 @@ class CommandFilterRule(OrgModelMixin): class Meta: ordering = ('-priority', 'action') + verbose_name = _("Command filter rule") @property def _pattern(self): diff --git a/apps/audits/signals_handler.py b/apps/audits/signals_handler.py index 931c6619e..cf297c052 100644 --- a/apps/audits/signals_handler.py +++ b/apps/audits/signals_handler.py @@ -6,14 +6,17 @@ from django.dispatch import receiver from django.db import transaction 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 .models import OperateLog, PasswordChangeLog +logger = get_logger(__name__) + MODELS_NEED_RECORD = ( '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): return resource_type = sender._meta.verbose_name 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(): - OperateLog.objects.create( - user=user, action=action, resource_type=resource_type, - resource=resource, remote_addr=remote_addr - ) + try: + OperateLog.objects.create(**data) + except Exception as e: + logger.error("Create operate log error: {}".format(e)) @receiver(post_save, dispatch_uid="my_unique_identifier") diff --git a/apps/audits/views.py b/apps/audits/views.py index ed109352c..b19514e9b 100644 --- a/apps/audits/views.py +++ b/apps/audits/views.py @@ -14,13 +14,16 @@ from .models import FTPLog, OperateLog, PasswordChangeLog, UserLoginLog def get_resource_type_list(): 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 perms.models import AssetPermission models = [ User, UserGroup, Asset, Node, AdminUser, SystemUser, Domain, - Gateway, Organization, AssetPermission + Gateway, Organization, AssetPermission, CommandFilter, CommandFilterRule ] return [model._meta.verbose_name for model in models] diff --git a/apps/authentication/ldap/backends.py b/apps/authentication/ldap/backends.py index 9b9ed24c3..7fcccc046 100644 --- a/apps/authentication/ldap/backends.py +++ b/apps/authentication/ldap/backends.py @@ -16,13 +16,8 @@ class LDAPAuthorizationBackend(LDAPBackend): """ 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) - user = self.authenticate_ldap_user(ldap_user, password) - else: - logger.debug('Rejecting empty password for {}'.format(username)) - user = None - + ldap_user = LDAPUser(self, username=username.strip(), request=request) + user = self.authenticate_ldap_user(ldap_user, password) return user def get_user(self, user_id): diff --git a/apps/common/forms.py b/apps/common/forms.py index 36ab924b7..1f7e4c1fd 100644 --- a/apps/common/forms.py +++ b/apps/common/forms.py @@ -96,7 +96,7 @@ class LDAPSettingForm(BaseForm): label=_("LDAP server"), ) AUTH_LDAP_BIND_DN = forms.CharField( - label=_("Bind DN"), + required=False, label=_("Bind DN"), ) AUTH_LDAP_BIND_PASSWORD = FormEncryptCharField( label=_("Password"), diff --git a/apps/common/migrations/0005_auto_20190221_1902.py b/apps/common/migrations/0005_auto_20190221_1902.py new file mode 100644 index 000000000..997ca7a13 --- /dev/null +++ b/apps/common/migrations/0005_auto_20190221_1902.py @@ -0,0 +1,17 @@ +# 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'}, + ), + ] diff --git a/apps/common/models.py b/apps/common/models.py index 012cd7351..a6755a6d4 100644 --- a/apps/common/models.py +++ b/apps/common/models.py @@ -123,3 +123,4 @@ class Setting(models.Model): class Meta: db_table = "settings" + verbose_name = _("Setting") diff --git a/apps/common/templates/common/replay_storage_create.html b/apps/common/templates/common/replay_storage_create.html index a1781c2e3..c8f99240a 100644 --- a/apps/common/templates/common/replay_storage_create.html +++ b/apps/common/templates/common/replay_storage_create.html @@ -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]; 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_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]; @@ -239,6 +239,11 @@ $(document).ready(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 field = getFieldByType(type); var data = {'TYPE': type}; @@ -249,13 +254,16 @@ $(document).ready(function() { var url = "{% url 'api-common:replay-storage-create' %}"; var success = function(data, textStatus) { location = "{% url 'common:terminal-setting' %}"; + submitBtn.removeClass('disabled'); + submitBtn.html(origin_text); }; var error = function(data, textStatus) { 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); }) {% endblock %} diff --git a/apps/common/templatetags/common_tags.py b/apps/common/templatetags/common_tags.py index d9f2c373d..790759f0a 100644 --- a/apps/common/templatetags/common_tags.py +++ b/apps/common/templatetags/common_tags.py @@ -97,6 +97,14 @@ def is_bool_field(field): return False +@register.filter +def is_image_field(field): + if isinstance(field, forms.ImageField): + return True + else: + return False + + @register.filter def to_dict(data): return dict(data) diff --git a/apps/jumpserver/settings.py b/apps/jumpserver/settings.py index f6db4e37d..43bc3cc1a 100644 --- a/apps/jumpserver/settings.py +++ b/apps/jumpserver/settings.py @@ -108,7 +108,6 @@ MIDDLEWARE = [ ROOT_URLCONF = 'jumpserver.urls' - TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', @@ -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_SESSION_KEEP_DURATION = CONFIG.TERMINAL_SESSION_KEEP_DURATION 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 BOOTSTRAP3 = { diff --git a/apps/locale/zh/LC_MESSAGES/django.mo b/apps/locale/zh/LC_MESSAGES/django.mo index 6b3607f4b..a9d91262d 100644 Binary files a/apps/locale/zh/LC_MESSAGES/django.mo and b/apps/locale/zh/LC_MESSAGES/django.mo differ diff --git a/apps/locale/zh/LC_MESSAGES/django.po b/apps/locale/zh/LC_MESSAGES/django.po index 85bddd065..6f20978fb 100644 --- a/apps/locale/zh/LC_MESSAGES/django.po +++ b/apps/locale/zh/LC_MESSAGES/django.po @@ -8,7 +8,7 @@ msgid "" msgstr "" "Project-Id-Version: Jumpserver 0.3.3\n" "Report-Msgid-Bugs-To: \n" -"POT-Creation-Date: 2019-02-20 18:56+0800\n" +"POT-Creation-Date: 2019-02-21 19:23+0800\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: ibuler \n" "Language-Team: Jumpserver team\n" @@ -193,7 +193,7 @@ msgstr "名称" #: perms/templates/perms/asset_permission_user.html:55 users/forms.py:15 #: users/forms.py:33 users/models/authentication.py:77 users/models/user.py:53 #: users/templates/users/_select_user_modal.html:14 -#: users/templates/users/login.html:64 +#: users/templates/users/login.html:64 users/templates/users/new_login.html:85 #: users/templates/users/user_detail.html:67 #: users/templates/users/user_list.html:24 #: users/templates/users/user_profile.html:47 @@ -206,7 +206,7 @@ msgstr "密码或密钥密码" #: assets/forms/user.py:26 assets/models/base.py:24 common/forms.py:102 #: users/forms.py:17 users/forms.py:35 users/forms.py:47 -#: users/templates/users/login.html:67 +#: users/templates/users/login.html:67 users/templates/users/new_login.html:90 #: users/templates/users/reset_password.html:53 #: users/templates/users/user_create.html:10 #: users/templates/users/user_password_authentication.html:18 @@ -233,8 +233,8 @@ msgstr "密码和私钥, 必须输入一个" msgid "* Automatic login mode must fill in the username." msgstr "自动登录模式,必须填写用户名" -#: assets/forms/user.py:145 assets/models/user.py:141 -#: assets/templates/assets/_system_user.html:66 +#: assets/forms/user.py:145 assets/models/cmd_filter.py:31 +#: assets/models/user.py:141 assets/templates/assets/_system_user.html:66 #: assets/templates/assets/system_user_detail.html:165 msgid "Command filter" msgstr "命令过滤器" @@ -382,7 +382,7 @@ msgstr "标签管理" #: assets/models/asset.py:109 assets/models/base.py:30 #: assets/models/cluster.py:28 assets/models/cmd_filter.py:25 -#: assets/models/cmd_filter.py:55 assets/models/group.py:21 +#: assets/models/cmd_filter.py:58 assets/models/group.py:21 #: assets/templates/assets/admin_user_detail.html:68 #: assets/templates/assets/asset_detail.html:125 #: assets/templates/assets/cmd_filter_detail.html:77 @@ -415,7 +415,7 @@ msgstr "创建日期" #: assets/models/asset.py:111 assets/models/base.py:27 #: assets/models/cluster.py:29 assets/models/cmd_filter.py:22 -#: assets/models/cmd_filter.py:52 assets/models/domain.py:21 +#: assets/models/cmd_filter.py:55 assets/models/domain.py:21 #: assets/models/domain.py:53 assets/models/group.py:23 #: assets/models/label.py:23 assets/templates/assets/admin_user_detail.html:72 #: assets/templates/assets/admin_user_list.html:32 @@ -464,6 +464,7 @@ msgid "Reachable" msgstr "可连接" #: assets/models/asset.py:119 assets/models/base.py:36 +#: xpack/plugins/license/models.py:78 msgid "Unknown" msgstr "未知" @@ -533,11 +534,11 @@ msgstr "北京电信" msgid "BGP full netcom" msgstr "BGP全网通" -#: assets/models/cmd_filter.py:35 +#: assets/models/cmd_filter.py:38 msgid "Regex" msgstr "正则表达式" -#: assets/models/cmd_filter.py:36 ops/models/command.py:21 +#: assets/models/cmd_filter.py:39 ops/models/command.py:21 #: ops/templates/ops/command_execution_list.html:60 terminal/models.py:161 #: terminal/templates/terminal/command_list.html:55 #: terminal/templates/terminal/command_list.html:71 @@ -546,19 +547,19 @@ msgstr "正则表达式" msgid "Command" msgstr "命令" -#: assets/models/cmd_filter.py:41 +#: assets/models/cmd_filter.py:44 msgid "Deny" msgstr "拒绝" -#: assets/models/cmd_filter.py:42 +#: assets/models/cmd_filter.py:45 msgid "Allow" msgstr "允许" -#: assets/models/cmd_filter.py:46 +#: assets/models/cmd_filter.py:49 msgid "Filter" msgstr "过滤器" -#: assets/models/cmd_filter.py:47 +#: assets/models/cmd_filter.py:50 #: assets/templates/assets/cmd_filter_rule_list.html:58 #: audits/templates/audits/login_log_list.html:50 #: common/templates/common/command_storage_create.html:31 @@ -568,25 +569,26 @@ msgstr "过滤器" msgid "Type" msgstr "类型" -#: assets/models/cmd_filter.py:48 assets/models/user.py:135 +#: assets/models/cmd_filter.py:51 assets/models/user.py:135 #: assets/templates/assets/cmd_filter_rule_list.html:60 msgid "Priority" msgstr "优先级" -#: assets/models/cmd_filter.py:48 +#: assets/models/cmd_filter.py:51 msgid "1-100, the higher will be match first" msgstr "优先级可选范围为1-100,1最低优先级,100最高优先级" -#: assets/models/cmd_filter.py:50 +#: assets/models/cmd_filter.py:53 #: assets/templates/assets/cmd_filter_rule_list.html:59 +#: xpack/plugins/license/models.py:29 msgid "Content" msgstr "内容" -#: assets/models/cmd_filter.py:50 +#: assets/models/cmd_filter.py:53 msgid "One line one command" msgstr "每行一个命令" -#: assets/models/cmd_filter.py:51 +#: assets/models/cmd_filter.py:54 #: assets/templates/assets/admin_user_assets.html:52 #: assets/templates/assets/admin_user_list.html:33 #: assets/templates/assets/asset_list.html:96 @@ -614,6 +616,10 @@ msgstr "每行一个命令" msgid "Action" msgstr "动作" +#: assets/models/cmd_filter.py:64 +msgid "Command filter rule" +msgstr "命令过滤规则" + #: assets/models/domain.py:61 assets/templates/assets/domain_detail.html:21 #: assets/templates/assets/domain_detail.html:64 #: assets/templates/assets/domain_gateway_list.html:21 @@ -922,6 +928,7 @@ msgstr "其它" #: users/templates/users/user_pubkey_update.html:76 #: xpack/plugins/cloud/templates/cloud/account_create_update.html:33 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_create.html:35 +#: xpack/plugins/interface/templates/interface/interface.html:88 msgid "Reset" msgstr "重置" @@ -948,12 +955,13 @@ msgstr "重置" #: terminal/templates/terminal/session_list.html:127 #: terminal/templates/terminal/terminal_update.html:46 #: users/templates/users/_user.html:51 -#: users/templates/users/forgot_password.html:45 +#: users/templates/users/forgot_password.html:49 #: users/templates/users/user_bulk_update.html:24 #: users/templates/users/user_list.html:45 #: users/templates/users/user_password_update.html:72 #: users/templates/users/user_profile_update.html:64 #: users/templates/users/user_pubkey_update.html:77 +#: xpack/plugins/interface/templates/interface/interface.html:89 msgid "Submit" msgstr "提交" @@ -1170,6 +1178,7 @@ msgstr "创建日期" #: terminal/templates/terminal/session_detail.html:81 #: users/templates/users/user_detail.html:138 #: users/templates/users/user_profile.html:146 +#: xpack/plugins/license/templates/license/license_detail.html:93 msgid "Quick modify" msgstr "快速修改" @@ -1198,6 +1207,7 @@ msgstr "刷新" #: assets/templates/assets/asset_detail.html:308 #: users/templates/users/user_detail.html:307 #: users/templates/users/user_detail.html:334 +#: xpack/plugins/interface/views.py:31 msgid "Update successfully!" msgstr "更新成功" @@ -1216,6 +1226,7 @@ msgstr "创建资产" #: assets/templates/assets/asset_list.html:73 #: users/templates/users/user_list.html:7 +#: xpack/plugins/license/templates/license/license_detail.html:101 msgid "Import" msgstr "导入" @@ -1682,7 +1693,7 @@ msgstr "文件名" #: audits/models.py:22 audits/templates/audits/ftp_log_list.html:76 #: ops/templates/ops/command_execution_list.html:64 #: ops/templates/ops/task_list.html:39 users/models/authentication.py:73 -#: users/templates/users/user_detail.html:458 xpack/plugins/cloud/api.py:61 +#: users/templates/users/user_detail.html:458 xpack/plugins/cloud/api.py:62 msgid "Success" msgstr "成功" @@ -1784,28 +1795,28 @@ msgstr "日期" msgid "Datetime" msgstr "日期" -#: audits/views.py:68 audits/views.py:112 audits/views.py:148 -#: audits/views.py:192 audits/views.py:223 templates/_nav.html:72 +#: audits/views.py:71 audits/views.py:115 audits/views.py:151 +#: audits/views.py:195 audits/views.py:226 templates/_nav.html:72 msgid "Audits" msgstr "日志审计" -#: audits/views.py:69 templates/_nav.html:76 +#: audits/views.py:72 templates/_nav.html:76 msgid "FTP log" msgstr "FTP日志" -#: audits/views.py:113 templates/_nav.html:77 +#: audits/views.py:116 templates/_nav.html:77 msgid "Operate log" msgstr "操作日志" -#: audits/views.py:149 templates/_nav.html:78 +#: audits/views.py:152 templates/_nav.html:78 msgid "Password change log" msgstr "改密日志" -#: audits/views.py:193 templates/_nav.html:75 +#: audits/views.py:196 templates/_nav.html:75 msgid "Login log" msgstr "登录日志" -#: audits/views.py:224 ops/views/command.py:44 +#: audits/views.py:227 ops/views/command.py:44 msgid "Command execution list" msgstr "命令执行列表" @@ -2117,6 +2128,11 @@ msgstr "" msgid "Enabled" msgstr "启用" +#: common/models.py:126 users/templates/users/reset_password.html:68 +#: users/templates/users/user_profile.html:20 +msgid "Setting" +msgstr "设置" + #: common/templates/common/basic_setting.html:15 #: common/templates/common/email_setting.html:15 #: common/templates/common/ldap_setting.html:15 @@ -2257,6 +2273,10 @@ msgstr "宁夏: cn-northwest-1" msgid "More" msgstr "更多" +#: common/templates/common/replay_storage_create.html:246 +msgid "Submitting" +msgstr "提交中" + #: common/templates/common/security_setting.html:46 msgid "Password check rule" msgstr "密码校验规则" @@ -2711,6 +2731,7 @@ msgid "Validity period" msgstr "有效期" #: perms/templates/perms/asset_permission_detail.html:66 +#: xpack/plugins/license/templates/license/license_detail.html:67 msgid "User count" msgstr "用户数量" @@ -2719,6 +2740,7 @@ msgid "User group count" msgstr "用户组列表" #: perms/templates/perms/asset_permission_detail.html:74 +#: xpack/plugins/license/templates/license/license_detail.html:63 msgid "Asset count" msgstr "资产数量" @@ -2828,7 +2850,7 @@ msgid "Logout" msgstr "注销登录" #: templates/_header_bar.html:101 users/templates/users/login.html:46 -#: users/templates/users/login.html:72 +#: users/templates/users/login.html:72 users/templates/users/new_login.html:96 msgid "Login" msgstr "登录" @@ -2896,9 +2918,9 @@ msgstr "" "\"%(user_pubkey_update)s\"> 链接 更新\n" " " -#: templates/_nav.html:10 users/views/group.py:28 users/views/group.py:44 -#: users/views/group.py:60 users/views/group.py:76 users/views/group.py:92 -#: users/views/login.py:349 users/views/user.py:68 users/views/user.py:83 +#: templates/_nav.html:10 users/views/group.py:27 users/views/group.py:43 +#: users/views/group.py:59 users/views/group.py:75 users/views/group.py:91 +#: users/views/login.py:360 users/views/user.py:68 users/views/user.py:83 #: users/views/user.py:113 users/views/user.py:194 users/views/user.py:355 #: users/views/user.py:405 users/views/user.py:445 msgid "Users" @@ -2972,7 +2994,15 @@ msgstr "语言播放验证码" msgid "Captcha" msgstr "验证码" -#: templates/flash_message_standalone.html:45 +#: templates/flash_message_standalone.html:35 +#: users/templates/users/login.html:27 users/templates/users/login_otp.html:27 +#: users/templates/users/new_login.html:56 +#: users/templates/users/reset_password.html:25 +#: xpack/plugins/interface/models.py:36 +msgid "Welcome to the Jumpserver open source fortress" +msgstr "欢迎使用Jumpserver开源堡垒机" + +#: templates/flash_message_standalone.html:56 msgid "Return" msgstr "返回" @@ -3339,6 +3369,7 @@ msgid "" msgstr "你可以使用ssh客户端工具连接终端" #: users/api/auth.py:40 users/templates/users/login.html:52 +#: users/templates/users/new_login.html:71 msgid "Log in frequently and try again later" msgstr "登录频繁, 稍后重试" @@ -3619,7 +3650,7 @@ msgid "Date password last updated" msgstr "最后更新密码日期" #: users/models/user.py:128 users/templates/users/user_update.html:22 -#: users/views/login.py:243 users/views/login.py:302 users/views/user.py:418 +#: users/views/login.py:254 users/views/login.py:313 users/views/user.py:418 msgid "User auth from {}, go there change password" msgstr "用户认证源来自 {}, 请去相应系统修改密码" @@ -3641,7 +3672,7 @@ msgstr "安全令牌验证" #: users/templates/users/_base_otp.html:44 users/templates/users/_user.html:13 #: users/templates/users/user_profile_update.html:51 -#: xpack/plugins/cloud/models.py:60 xpack/plugins/cloud/models.py:120 +#: xpack/plugins/cloud/models.py:120 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_detail.html:59 #: xpack/plugins/cloud/templates/cloud/sync_instance_task_list.html:13 msgid "Account" @@ -3722,20 +3753,15 @@ msgid " for more information" msgstr "获取更多信息" #: users/templates/users/forgot_password.html:11 -#: users/templates/users/forgot_password.html:27 -#: users/templates/users/login.html:83 +#: users/templates/users/forgot_password.html:31 +#: users/templates/users/login.html:83 users/templates/users/new_login.html:100 msgid "Forgot password" msgstr "忘记密码" -#: users/templates/users/forgot_password.html:34 +#: users/templates/users/forgot_password.html:38 msgid "Input your email, that will send a mail to your" msgstr "输入您的邮箱, 将会发一封重置邮件到您的邮箱中" -#: users/templates/users/login.html:27 users/templates/users/login_otp.html:27 -#: users/templates/users/reset_password.html:25 -msgid "Welcome to the Jumpserver open source fortress" -msgstr "欢迎使用Jumpserver开源堡垒机" - #: users/templates/users/login.html:29 users/templates/users/login_otp.html:29 msgid "" "The world's first fully open source fortress, using the GNU GPL v2.0 open " @@ -3768,11 +3794,11 @@ msgstr "" msgid "Changes the world, starting with a little bit." msgstr "改变世界,从一点点开始。" -#: users/templates/users/login.html:54 +#: users/templates/users/login.html:54 users/templates/users/new_login.html:73 msgid "The user password has expired" msgstr "用户密码已过期" -#: users/templates/users/login.html:57 +#: users/templates/users/login.html:57 users/templates/users/new_login.html:76 msgid "Captcha invalid" msgstr "验证码错误" @@ -3812,6 +3838,10 @@ msgstr "6位数字" msgid "Can't provide security? Please contact the administrator!" msgstr "如果不能提供MFA验证码,请联系管理员!" +#: users/templates/users/new_login.html:61 +msgid "Welcome back, please enter username and password to login" +msgstr "欢迎回来,请输入用户名和密码登录" + #: users/templates/users/reset_password.html:28 msgid "" "Jumpserver is an open source desktop system developed using Python and " @@ -3864,11 +3894,6 @@ msgstr "密码强度:" msgid "Password again" msgstr "再次输入密码" -#: users/templates/users/reset_password.html:68 -#: users/templates/users/user_profile.html:20 -msgid "Setting" -msgstr "设置" - #: users/templates/users/reset_password.html:105 #: users/templates/users/user_password_update.html:99 #: users/templates/users/user_update.html:46 @@ -4018,7 +4043,7 @@ msgstr "重置用户MFA成功" #: users/templates/users/user_group_detail.html:22 #: users/templates/users/user_group_granted_asset.html:18 -#: users/views/group.py:77 +#: users/views/group.py:76 msgid "User group detail" msgstr "用户组详情" @@ -4027,7 +4052,7 @@ msgstr "用户组详情" msgid "Add user" msgstr "添加用户" -#: users/templates/users/user_group_list.html:5 users/views/group.py:45 +#: users/templates/users/user_group_list.html:5 users/views/group.py:44 msgid "Create user group" msgstr "创建用户组" @@ -4360,68 +4385,68 @@ msgstr "禁用或失效" msgid "Password or SSH public key invalid" msgstr "密码或密钥不合法" -#: users/views/group.py:29 +#: users/views/group.py:28 msgid "User group list" msgstr "用户组列表" -#: users/views/group.py:61 +#: users/views/group.py:60 msgid "Update user group" msgstr "更新用户组" -#: users/views/group.py:93 +#: users/views/group.py:92 msgid "User group granted asset" msgstr "用户组授权资产" -#: users/views/login.py:69 +#: users/views/login.py:80 msgid "Please enable cookies and try again." msgstr "设置你的浏览器支持cookie" -#: users/views/login.py:191 users/views/user.py:532 users/views/user.py:557 +#: users/views/login.py:202 users/views/user.py:532 users/views/user.py:557 msgid "MFA code invalid, or ntp sync server time" msgstr "MFA验证码不正确,或者服务器端时间不对" -#: users/views/login.py:223 +#: users/views/login.py:234 msgid "Logout success" msgstr "退出登录成功" -#: users/views/login.py:224 +#: users/views/login.py:235 msgid "Logout success, return login page" msgstr "退出登录成功,返回到登录页面" -#: users/views/login.py:240 +#: users/views/login.py:251 msgid "Email address invalid, please input again" msgstr "邮箱地址错误,重新输入" -#: users/views/login.py:256 +#: users/views/login.py:267 msgid "Send reset password message" msgstr "发送重置密码邮件" -#: users/views/login.py:257 +#: users/views/login.py:268 msgid "Send reset password mail success, login your mail box and follow it " msgstr "" "发送重置邮件成功, 请登录邮箱查看, 按照提示操作 (如果没收到,请等待3-5分钟)" -#: users/views/login.py:270 +#: users/views/login.py:281 msgid "Reset password success" msgstr "重置密码成功" -#: users/views/login.py:271 +#: users/views/login.py:282 msgid "Reset password success, return to login page" msgstr "重置密码成功,返回到登录页面" -#: users/views/login.py:286 users/views/login.py:305 +#: users/views/login.py:297 users/views/login.py:316 msgid "Token invalid or expired" msgstr "Token错误或失效" -#: users/views/login.py:298 +#: users/views/login.py:309 msgid "Password not same" msgstr "密码不一致" -#: users/views/login.py:311 users/views/user.py:128 users/views/user.py:428 +#: users/views/login.py:322 users/views/user.py:128 users/views/user.py:428 msgid "* Your password does not meet the requirements" msgstr "* 您的密码不符合要求" -#: users/views/login.py:349 +#: users/views/login.py:360 msgid "First login" msgstr "首次登录" @@ -4473,7 +4498,7 @@ msgstr "MFA 解绑成功" msgid "MFA disable success, return login page" msgstr "MFA 解绑成功,返回登录页面" -#: xpack/plugins/cloud/api.py:60 xpack/plugins/cloud/providers/base.py:84 +#: xpack/plugins/cloud/api.py:61 xpack/plugins/cloud/providers/base.py:84 msgid "Account unavailable" msgstr "账户无效" @@ -4535,6 +4560,10 @@ msgstr "" msgid "Access key secret" msgstr "" +#: xpack/plugins/cloud/models.py:60 +msgid "Cloud account" +msgstr "云账号" + #: xpack/plugins/cloud/models.py:121 msgid "Regions" msgstr "地域" @@ -4684,6 +4713,160 @@ msgstr "同步实例任务列表" msgid "Create sync Instance task" msgstr "创建同步实例任务" +#: xpack/plugins/interface/forms.py:16 xpack/plugins/interface/models.py:15 +msgid "Title of login page" +msgstr "登录页面标题" + +#: xpack/plugins/interface/forms.py:18 +msgid "" +"Tips: This will be displayed on the enterprise user login page. (eg: Welcome " +"to the Jumpserver open source fortress)" +msgstr "提示:将会显示在企业版用户登录页面(eg: 欢迎使用Jumpserver开源堡垒机)" + +#: xpack/plugins/interface/forms.py:24 xpack/plugins/interface/models.py:19 +msgid "Image of login page" +msgstr "登录页面图片" + +#: xpack/plugins/interface/forms.py:26 +msgid "" +"Tips: This will be displayed on the enterprise user login page. (suggest " +"image size: 635px*472px)" +msgstr "提示:将会显示在企业版用户登录页面(建议图片大小为: 635px*472px)" + +#: xpack/plugins/interface/forms.py:32 xpack/plugins/interface/models.py:23 +msgid "Website icon" +msgstr "网站图标" + +#: xpack/plugins/interface/forms.py:34 +msgid "Tips: website icon. (suggest image size: 16px*16px)" +msgstr "提示:网站图标(建议图片大小为: 16px*16px)" + +#: xpack/plugins/interface/forms.py:39 xpack/plugins/interface/models.py:27 +msgid "Logo of management page" +msgstr "管理页面logo" + +#: xpack/plugins/interface/forms.py:41 +msgid "" +"Tips: This will appear at the top left of the administration page. (suggest " +"image size: 185px*55px)" +msgstr "提示:将会显示在管理页面左上方(建议图片大小为: 185px*55px)" + +#: xpack/plugins/interface/forms.py:47 xpack/plugins/interface/models.py:31 +msgid "Logo of logout page" +msgstr "退出页面logo" + +#: xpack/plugins/interface/forms.py:49 +msgid "" +"Tips: This will be displayed on the enterprise user logout page. (suggest " +"image size: 82px*82px)" +msgstr "提示:将会显示在企业版用户退出页面(建议图片大小为:82px*82px)" + +#: xpack/plugins/interface/meta.py:10 +msgid "Interface settings" +msgstr "界面设置" + +#: xpack/plugins/interface/templates/interface/interface.html:15 +#: xpack/plugins/interface/views.py:21 +msgid "Interface setting" +msgstr "界面设置" + +#: xpack/plugins/interface/views.py:20 +msgid "Interface" +msgstr "界面" + +#: xpack/plugins/license/meta.py:11 xpack/plugins/license/models.py:95 +#: xpack/plugins/license/templates/license/license_detail.html:50 +#: xpack/plugins/license/views.py:31 +msgid "License" +msgstr "许可证" + +#: xpack/plugins/license/models.py:74 +msgid "Standard edition" +msgstr "" + +#: xpack/plugins/license/models.py:76 +msgid "Enterprise edition" +msgstr "企业版" + +#: xpack/plugins/license/templates/license/_license_import_modal.html:4 +#: xpack/plugins/license/templates/license/license_detail.html:99 +msgid "Import license" +msgstr "导入许可证" + +#: xpack/plugins/license/templates/license/_license_import_modal.html:9 +msgid "License file" +msgstr "许可证文件" + +#: xpack/plugins/license/templates/license/license_detail.html:12 +msgid "Please Import License" +msgstr "请导入许可证" + +#: xpack/plugins/license/templates/license/license_detail.html:17 +msgid "License has expired" +msgstr "许可证已经过期" + +#: xpack/plugins/license/templates/license/license_detail.html:22 +msgid "The license will at " +msgstr "许可证将在 " + +#: xpack/plugins/license/templates/license/license_detail.html:22 +msgid " expired." +msgstr " 过期。" + +#: xpack/plugins/license/templates/license/license_detail.html:37 +#: xpack/plugins/license/views.py:32 +msgid "License detail" +msgstr "许可证详情" + +#: xpack/plugins/license/templates/license/license_detail.html:51 +msgid "No license" +msgstr "暂无许可证" + +#: xpack/plugins/license/templates/license/license_detail.html:55 +msgid "Corporation" +msgstr "公司" + +#: xpack/plugins/license/templates/license/license_detail.html:59 +msgid "Expired" +msgstr "过期时间" + +#: xpack/plugins/license/templates/license/license_detail.html:64 +#: xpack/plugins/license/templates/license/license_detail.html:68 +#: xpack/plugins/license/templates/license/license_detail.html:72 +#: xpack/plugins/license/templates/license/license_detail.html:76 +msgid "Unlimited" +msgstr "无限制" + +#: xpack/plugins/license/templates/license/license_detail.html:71 +#, fuzzy +#| msgid "CPU count" +msgid "Cpu count" +msgstr "CPU数量" + +#: xpack/plugins/license/templates/license/license_detail.html:75 +msgid "Concurrent connections" +msgstr "并发连接" + +#: xpack/plugins/license/templates/license/license_detail.html:80 +msgid "Edition" +msgstr "版本" + +#: xpack/plugins/license/templates/license/license_detail.html:106 +msgid "Technology consulting" +msgstr "技术咨询" + +#: xpack/plugins/license/templates/license/license_detail.html:109 +msgid "Consult" +msgstr "咨询" + +#: xpack/plugins/license/views.py:45 +msgid "License import successfully" +msgstr "许可证导入成功" + +#: xpack/plugins/license/views.py:47 +msgid "License is invalid" +msgstr "无效的许可证" + #: xpack/plugins/orgs/forms.py:14 #: xpack/plugins/orgs/templates/orgs/org_detail.html:76 #: xpack/plugins/orgs/templates/orgs/org_list.html:13 @@ -4728,6 +4911,19 @@ msgstr "创建组织" msgid "Update org" msgstr "更新组织" +#~ msgid "Login Jumpserver" +#~ msgstr "登录 Jumpserver" + +#, fuzzy +#~| msgid "Delete succeed" +#~ msgid "Delete success!" +#~ msgstr "删除成功" + +#, fuzzy +#~| msgid "Username does not exist" +#~ msgid "This license does not exist!" +#~ msgstr "用户名不存在" + #~ msgid "Valid" #~ msgstr "账户状态" @@ -4774,11 +4970,6 @@ msgstr "更新组织" #~ msgid "Bit" #~ msgstr " 位" -#, fuzzy -#~| msgid "Delete succeed" -#~ msgid "Delete success" -#~ msgstr "删除成功" - #~ msgid "" #~ "Set terminal storage setting, `default` is the using as default,You can " #~ "set other storage and some terminal using" diff --git a/apps/perms/api.py b/apps/perms/api.py index 2ecdfd52e..d19f82f3f 100644 --- a/apps/perms/api.py +++ b/apps/perms/api.py @@ -16,7 +16,9 @@ from common.tree import TreeNode, TreeNodeSerializer from common.utils import get_object_or_none from orgs.mixins import RootOrgViewMixin 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 .hands import ( AssetGrantedSerializer, User, UserGroup, Asset, Node, @@ -298,71 +300,6 @@ class UserGrantedNodesWithAssetsAsTreeApi(ListAPIView): self.system_user_id = request.query_params.get('system_user') 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): if self.kwargs.get('pk') is None: self.permission_classes = (IsValidUser,) @@ -381,12 +318,12 @@ class UserGrantedNodesWithAssetsAsTreeApi(ListAPIView): 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 = self.parse_node_to_tree_node(node) + data = parse_node_to_tree_node(node) queryset.append(data) if not self.show_assets: continue 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 = sorted(queryset) return queryset @@ -490,6 +427,44 @@ class UserGroupGrantedNodesWithAssetsApi(ListAPIView): 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): permission_classes = (IsOrgAdminOrAppUser,) serializer_class = AssetGrantedSerializer diff --git a/apps/perms/urls/api_urls.py b/apps/perms/urls/api_urls.py index 8e4716fac..00e842f52 100644 --- a/apps/perms/urls/api_urls.py +++ b/apps/perms/urls/api_urls.py @@ -43,6 +43,9 @@ urlpatterns = [ path('user-group//nodes-assets/', api.UserGroupGrantedNodesWithAssetsApi.as_view(), name='user-group-nodes-assets'), + path('user-group//nodes-assets/tree/', + api.UserGroupGrantedNodesWithAssetsAsTreeApi.as_view(), + name='user-group-nodes-assets-as-tree'), path('user-group//nodes//assets/', api.UserGroupGrantedNodeAssetsApi.as_view(), name='user-group-node-assets'), diff --git a/apps/perms/utils.py b/apps/perms/utils.py index 45ec6e039..ba5ebdee1 100644 --- a/apps/perms/utils.py +++ b/apps/perms/utils.py @@ -5,6 +5,7 @@ from collections import defaultdict from django.db.models import Q from common.utils import get_logger +from common.tree import TreeNode from .models import AssetPermission from .hands import Node @@ -193,3 +194,69 @@ def sort_assets(assets, order_by='hostname', reverse=False): else: assets = sorted(assets, key=lambda asset: getattr(asset, order_by), reverse=reverse) 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 diff --git a/apps/static/css/bootstrap-style.css b/apps/static/css/bootstrap-style.css new file mode 100644 index 000000000..f04bdf454 --- /dev/null +++ b/apps/static/css/bootstrap-style.css @@ -0,0 +1,756 @@ +/*! + * 覆盖bootstrap样式,项目CSS禁止修改或添加该CSS内容,否则被覆盖后果自负 +**/ + +body { + font-family: "Microsoft Yahei", Helvetica, "PingFang SC", "Hiragino Sans GB", "STXihei", "SimHei", "SimSun", Helvetica Neue, Arial, sans-serif; + font-size: 14px; + line-height: 26px; + color: #2B415C; + background: #f2f2f2; + -webkit-font-smoothing: antialiased; +} + +h1 { + font-size: 24px; +} + +h2 { + font-size: 22px; +} + +h3 { + font-size: 20px; +} + +h4 { + font-size: 18px; +} + +h5 { + font-size: 16px; +} + +h6 { + font-size: 14px; +} + +p { + padding: 0; + margin: 0; +} + +a:hover { + text-decoration: none; +} + +a:hover, a:active, a:focus { + outline: 0; +} + +:focus { + outline: none; +} + +::-moz-focus-inner { + border: 0; +} + +/* Modal */ +.modal-content { + padding-left: 20px; + padding-right: 20px; + position: relative; + background-color: #ffffff; + -webkit-background-clip: padding-box; + background-clip: padding-box; + border: 1px solid #DFDFDF; + border-radius: 4px; + outline: 0; +} + +.modal-header { + height: 56px; + max-height: 56px; + vertical-align: middle; + border-bottom: 1px solid #DFDFDF; + padding: 0; +} + +.modal-header .close { + color: #2B415C; + background-color: transparent; + opacity: 0.5; + padding-top: 16px; + margin-top: 0; + font-size: 24px; +} + +.modal-header .modal-title { + float: left; + color: #2B415C; + font-size: 18px; + line-height: 24px; + padding-top: 16px; + padding-bottom: 16px; +} + +.modal-body { + padding: 15px 0; +} + +.modal-footer { + padding-top: 0; + padding-bottom: 26px; + border-top: none; + text-align: center; +} + +/* Form */ +form label { + opacity: 0.75; + color: #405773; + line-height: 19px; + white-space: nowrap; +} + +.form-control { + color: #2B415C; + background-color: #FFF; + border: 1px solid #8B9EB6; + border-radius: 2px; + -webkit-box-shadow: none; + box-shadow: none; +} + +.form-control:invalid { + color: #FF521B; + border-color: #FF521B; +} + +.form-control:hover { + border-color: #CED0DA; + background-color: #FFF; +} + +.form-control:focus { + color: #2B415C; + background-color: #F6F9Fd; + border-color: #2EA2F8; + outline: 0; + -webkit-box-shadow: none; + box-shadow: none; +} + +.form-control::-moz-placeholder { + color: #354052; + opacity: 0.5; +} + +.form-control:-ms-input-placeholder { + color: #354052; + opacity: 0.5; +} + +.form-control::-webkit-input-placeholder { + color: #354052; + opacity: 0.5; +} + +.form-control[readonly] { + background-color: #FBFCFD; + cursor: default; +} + +.form-control[disabled], fieldset[disabled] .form-control { + background-color: #E7EAED; + cursor: default; + opacity: 0.75; +} + +.form-control[disabled]:hover, .form-control[readonly]:hover, fieldset[disabled] .form-control:hover { + border-color: #8B9EB6; +} + +.input-group-addon { + color: #2B415C; + background-color: #FBFCFD; + border: 1px solid #8B9EB6; +} + +/*popover*/ +.popover { + border-radius: 4px; + -webkit-box-shadow: none; + box-shadow: none; +} + +.popover-content { + padding: 0; + min-width: 160px; +} + +/*页签*/ +.nav-tabs { + border-bottom: none; +} + +.nav-tabs.nav-tabs-white { + border-bottom: 1px solid #FFFFFF; +} + +.nav-tabs.nav-tabs-grey { + border-bottom: 1px solid #DFDFDF; +} + +.nav-tabs > li.active > a { + font-weight: normal; + border-top: 2px solid #095DB1; + border-bottom: none; + border-radius: 0; +} + +.nav-white > li.active > a { + z-index: 99; +} + +.nav-tabs > li > a { + border: 1px solid #DFDFDF; + border-radius: 0; + background-color: #F8F8F8; + color: #2B415C; +} + +.nav-tabs > li > a:hover { + border-bottom-color: #fff; +} + +.nav-tabs > li > a:focus, .nav-tabs > li > a:hover { + background-color: #fff; +} + +.nav-tabs > li.active > a, .nav-tabs > li.active > a:focus, .nav-tabs > li.active > a:hover { + color: #095DB1; +} + +.nav-pills > li + li { + margin-bottom: 5px; +} + +.nav-pills > li { + margin-left: 2px; +} + +/*表格样式*/ +.table { + border-radius: 0; + border: 0; + font-size: 12px; +} + +.table > thead > tr > th { + border: none; + background-color: #DDE7F4; + padding: 5px 10px; + vertical-align: middle; + height: 35px; +} + +.table.table-bordered > thead > tr > th { + border-left: 1px solid #BFCDE0; +} + +.table.table-bordered > thead > tr > th:first-child { + border-left: none; +} + +.table tbody > tr > td { + border: none; + border-left: 1px solid transparent; + padding: 5px 10px; + vertical-align: middle; + height: 35px; +} + +.table.table-bordered tbody > tr > td { + border-left: 1px solid #DFDFDF; +} + +.table.table-striped > tbody > tr:nth-of-type(odd) { + background-color: #F6F9FC; +} + +.table > tbody > tr { + border-top: 1px solid transparent; +} + +.table.table-bordered > tbody > tr { + border-top: 1px solid #DFDFDF; +} + +.table.table-bordered > tbody > tr:last-child { + border-bottom: 1px solid #DFDFDF; +} + +.table.table-hover > tbody > tr:hover { + background-color: #EBFAFF; + border-top: 1px solid #23A9F6; + border-bottom: 1px solid #23A9F6; +} + +.table.table-hover > tbody > tr:hover td:last-child { + border-right: none; +} + +.table > tbody > tr.tr-selected, +.table.table-bordered > tbody > tr.tr-selected, +.table.table-condensed > tbody > tr.tr-selected, +.table.table-hover > tbody > tr.tr-selected, +.table.table-striped > tbody > tr.tr-selected, +.table.table-responsive > tbody > tr.tr-selected { + background-color: #E7EAED; +} + +.table.table-hover > tbody > tr:hover td:first-child { + border-left: none; +} + +.table.table-hover > tbody > tr:hover td:last-child { + border-right: none; +} + +.table td:first-child, .table th:first-child, +.table.table-bordered td:first-child { + border-left: none; +} + +.table td:last-child, .table th:last-child, +.table.table-bordered td:last-child { + border-right: none; +} + +.table thead:first-child tr:first-child > th:first-child, +.table tbody:first-child tr:first-child > td:first-child { + border-radius: 0; +} + +/*分页*/ +.ng-pagination { + font-size: 12px; + color: #858585; +} + +.ng-pagination > ul > li { + min-width: 38px; + min-height: 38px; + border: 1px solid #F2F2F2; + border-radius: 2px; +} + +.ng-pagination > ul > li.active, .ng-pagination > ul > li.active:hover { + background-color: #215db1; + border: 1px solid #215db1; + color: #FFF; +} + +.ng-pagination > ul > li:hover { + background-color: transparent; + border: 1px solid #23A9F6; + color: #23A9F6; +} + +/* 按钮 */ +.btn { + border-radius: 2px; + min-width: 80px; + min-height: 28px; + padding: 0; +} + +.btn.active, .btn:active { + -webkit-box-shadow: none; + box-shadow: none; +} + +.btn.active.focus, .btn.active:focus, .btn.focus, .btn:active.focus, .btn:active:focus, .btn:focus { + outline: none; +} + +/* btn-primary */ +.btn-primary { + color: #FFF; + background-color: #095DB1; + border-color: #095DB1; +} + +.btn-primary:focus, +.btn-primary.focus { + color: #fff; + background-color: #095DB1; + border-color: #095DB1; +} + +.btn-primary:hover { + color: #fff; + background-color: #0D6FD1; + border-color: #0D6FD1; +} + +.btn-primary:active, .btn-primary.active, .open > .dropdown-toggle.btn-primary { + color: #fff; + background-color: #095DB1; + border-color: #095DB1; +} + +.btn-primary:active:hover, +.btn-primary.active:hover, +.open > .dropdown-toggle.btn-primary:hover, +.btn-primary:active:focus, +.btn-primary.active:focus, +.open > .dropdown-toggle.btn-primary:focus, +.btn-primary:active.focus, +.btn-primary.active.focus, +.open > .dropdown-toggle.btn-primary.focus { + color: #fff; + background-color: #095DB1; + border-color: #095DB1; +} + +.btn-primary:active, +.btn-primary.active, +.open > .dropdown-toggle.btn-primary { + background-image: none; +} + +.btn-primary.disabled, +.btn-primary[disabled], +fieldset[disabled] .btn-primary, +.btn-primary.disabled:hover, +.btn-primary[disabled]:hover, +fieldset[disabled] .btn-primary:hover, +.btn-primary.disabled:focus, +.btn-primary[disabled]:focus, +fieldset[disabled] .btn-primary:focus, +.btn-primary.disabled.focus, +.btn-primary[disabled].focus, +fieldset[disabled] .btn-primary.focus, +.btn-primary.disabled:active, +.btn-primary[disabled]:active, +fieldset[disabled] .btn-primary:active, +.btn-primary.disabled.active, +.btn-primary[disabled].active, +fieldset[disabled] .btn-primary.active { + color: #fff; + background-color: #095DB1; + border-color: #095DB1; + opacity: 0.5; +} + +.btn-primary .badge { + color: #095DB1; + background-color: #fff; +} + +/* btn-info */ +.btn-info { + color: #FFF; + background-color: #23A9F6; + border-color: #23A9F6; +} + +.btn-info:focus, +.btn-info.focus { + color: #fff; + background-color: #23A9F6; + border-color: #23A9F6; +} + +.btn-info:hover { + color: #fff; + background-color: #53C0FF; + border-color: #53C0FF; +} + +.btn-info:active, .btn-info.active, .open > .dropdown-toggle.btn-info { + color: #fff; + background-color: #23A9F6; + border-color: #23A9F6; +} + +.btn-info:active:hover, +.btn-info.active:hover, +.open > .dropdown-toggle.btn-info:hover, +.btn-info:active:focus, +.btn-info.active:focus, +.open > .dropdown-toggle.btn-info:focus, +.btn-info:active.focus, +.btn-info.active.focus, +.open > .dropdown-toggle.btn-info.focus { + color: #fff; + background-color: #23A9F6; + border-color: #23A9F6; +} + +.btn-info:active, +.btn-info.active, +.open > .dropdown-toggle.btn-info { + background-image: none; +} + +.btn-info.disabled, +.btn-info[disabled], +fieldset[disabled] .btn-info, +.btn-info.disabled:hover, +.btn-info[disabled]:hover, +fieldset[disabled] .btn-info:hover, +.btn-info.disabled:focus, +.btn-info[disabled]:focus, +fieldset[disabled] .btn-info:focus, +.btn-info.disabled.focus, +.btn-info[disabled].focus, +fieldset[disabled] .btn-info.focus, +.btn-info.disabled:active, +.btn-info[disabled]:active, +fieldset[disabled] .btn-info:active, +.btn-info.disabled.active, +.btn-info[disabled].active, +fieldset[disabled] .btn-info.active { + color: #fff; + background-color: #23A9F6; + border-color: #23A9F6; + opacity: 0.5; +} + +.btn-info .badge { + color: #23A9F6; + background-color: #fff; +} + +/* btn-default */ +.btn-default { + color: #095DB1; + background-color: #FFF; + border-color: rgba(9, 93, 177, 0.70); +} + +.btn-default:focus, +.btn-default.focus { + color: #095DB1; + background-color: #FFF; + border-color: rgba(9, 93, 177, 0.70); +} + +.btn-default:hover { + color: #23A9F6; + background-color: #fff; + border-color: #23A9F6; +} + +.btn-default:active, .btn-default.active, .open > .dropdown-toggle.btn-default { + color: #095DB1; + background-color: #FFF; + border-color: rgba(9, 93, 177, 0.70); +} + +.btn-default:active:hover, +.btn-default.active:hover, +.open > .dropdown-toggle.btn-default:hover, +.btn-default:active:focus, +.btn-default.active:focus, +.open > .dropdown-toggle.btn-default:focus, +.btn-default:active.focus, +.btn-default.active.focus, +.open > .dropdown-toggle.btn-default.focus { + color: #095DB1; + background-color: #FFF; + border-color: rgba(9, 93, 177, 0.70); +} + +.btn-default:active, +.btn-default.active, +.open > .dropdown-toggle.btn-default { + background-image: none; +} + +.btn-default.disabled, +.btn-default[disabled], +fieldset[disabled] .btn-default, +.btn-default.disabled:hover, +.btn-default[disabled]:hover, +fieldset[disabled] .btn-default:hover, +.btn-default.disabled:focus, +.btn-default[disabled]:focus, +fieldset[disabled] .btn-default:focus, +.btn-default.disabled.focus, +.btn-default[disabled].focus, +fieldset[disabled] .btn-default.focus, +.btn-default.disabled:active, +.btn-default[disabled]:active, +fieldset[disabled] .btn-default:active, +.btn-default.disabled.active, +.btn-default[disabled].active, +fieldset[disabled] .btn-default.active { + color: #095DB1; + background-color: #FFF; + border-color: rgba(9, 93, 177, 0.70); + opacity: 0.5; +} + +.btn-default .badge { + color: #095DB1; + background-color: #FFF; +} + +/* btn-success */ +.btn-success { + color: #FFF; + background-color: #8FB143; + border-color: #8FB143; +} + +.btn-success:focus, +.btn-success.focus { + color: #fff; + background-color: #8FB143; + border-color: #8FB143; +} + +.btn-success:hover { + color: #fff; + background-color: #A9D051; + border-color: #A9D051; +} + +.btn-success:active, .btn-success.active, .open > .dropdown-toggle.btn-success { + color: #fff; + background-color: #8FB143; + border-color: #8FB143; +} + +.btn-success:active:hover, +.btn-success.active:hover, +.open > .dropdown-toggle.btn-success:hover, +.btn-success:active:focus, +.btn-success.active:focus, +.open > .dropdown-toggle.btn-success:focus, +.btn-success:active.focus, +.btn-success.active.focus, +.open > .dropdown-toggle.btn-success.focus { + color: #fff; + background-color: #8FB143; + border-color: #8FB143; +} + +.btn-success:active, +.btn-success.active, +.open > .dropdown-toggle.btn-success { + background-image: none; +} + +.btn-success.disabled, +.btn-success[disabled], +fieldset[disabled] .btn-success, +.btn-success.disabled:hover, +.btn-success[disabled]:hover, +fieldset[disabled] .btn-success:hover, +.btn-success.disabled:focus, +.btn-success[disabled]:focus, +fieldset[disabled] .btn-success:focus, +.btn-success.disabled.focus, +.btn-success[disabled].focus, +fieldset[disabled] .btn-success.focus, +.btn-success.disabled:active, +.btn-success[disabled]:active, +fieldset[disabled] .btn-success:active, +.btn-success.disabled.active, +.btn-success[disabled].active, +fieldset[disabled] .btn-success.active { + color: #fff; + background-color: #8FB143; + border-color: #8FB143; + opacity: 0.5; +} + +.btn-success .badge { + color: #8FB143; + background-color: #fff; +} + +/* btn-danger */ +.btn-danger { + color: #FFF; + background-color: #FF521B; + border-color: #FF521B; +} + +.btn-danger:focus, +.btn-danger.focus { + color: #fff; + background-color: #FF521B; + border-color: #FF521B; +} + +.btn-danger:hover { + color: #fff; + background-color: #FF6C3E; + border-color: #FF6C3E; +} + +.btn-danger:active, .btn-danger.active, .open > .dropdown-toggle.btn-danger { + color: #fff; + background-color: #FF521B; + border-color: #FF521B; +} + +.btn-danger:active:hover, +.btn-danger.active:hover, +.open > .dropdown-toggle.btn-danger:hover, +.btn-danger:active:focus, +.btn-danger.active:focus, +.open > .dropdown-toggle.btn-danger:focus, +.btn-danger:active.focus, +.btn-danger.active.focus, +.open > .dropdown-toggle.btn-danger.focus { + color: #fff; + background-color: #FF521B; + border-color: #FF521B; +} + +.btn-danger:active, +.btn-danger.active, +.open > .dropdown-toggle.btn-danger { + background-image: none; +} + +.btn-danger.disabled, +.btn-danger[disabled], +fieldset[disabled] .btn-danger, +.btn-danger.disabled:hover, +.btn-danger[disabled]:hover, +fieldset[disabled] .btn-danger:hover, +.btn-danger.disabled:focus, +.btn-danger[disabled]:focus, +fieldset[disabled] .btn-danger:focus, +.btn-danger.disabled.focus, +.btn-danger[disabled].focus, +fieldset[disabled] .btn-danger.focus, +.btn-danger.disabled:active, +.btn-danger[disabled]:active, +fieldset[disabled] .btn-danger:active, +.btn-danger.disabled.active, +.btn-danger[disabled].active, +fieldset[disabled] .btn-danger.active { + color: #fff; + background-color: #FF521B; + border-color: #FF521B; + opacity: 0.5; +} + +.btn-danger .badge { + color: #FF521B; + background-color: #fff; +} + +/*! + * 覆盖bootstrap样式,项目CSS禁止修改或添加该CSS内容,否则被覆盖后果自负 +**/ \ No newline at end of file diff --git a/apps/static/css/jumpserver.css b/apps/static/css/jumpserver.css index c979678e0..3945258b0 100644 --- a/apps/static/css/jumpserver.css +++ b/apps/static/css/jumpserver.css @@ -131,10 +131,18 @@ table.dataTable tbody td.selected td i.text-navy { margin-right: 5px !important; } +.m-m-m{ + margin-left: 50px !important; +} + .m-10 { margin: 10px !important; } - +.f-i-l-e{ + /*float: left;*/ + margin-left: 1000px !important; + /*padding-left: 1000px !important;*/ +} .m-t-10 { margin-top: 10px !important; } diff --git a/apps/static/css/login-style.css b/apps/static/css/login-style.css new file mode 100644 index 000000000..e7a16723d --- /dev/null +++ b/apps/static/css/login-style.css @@ -0,0 +1,298 @@ +.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; +} diff --git a/apps/static/img/login/login-image.jpg b/apps/static/img/login/login-image.jpg new file mode 100644 index 000000000..794f98b7f Binary files /dev/null and b/apps/static/img/login/login-image.jpg differ diff --git a/apps/templates/_user_profile.html b/apps/templates/_user_profile.html index 365a4048c..2b60e78c9 100644 --- a/apps/templates/_user_profile.html +++ b/apps/templates/_user_profile.html @@ -3,7 +3,11 @@