Merge branch 'v3' of github.com:jumpserver/jumpserver into v3

pull/9244/head
Bai 2022-12-26 19:16:18 +08:00
commit 7f6d13a5a6
19 changed files with 174 additions and 119 deletions

View File

@ -1,9 +1,6 @@
from assets import serializers from assets import serializers
from assets.models import AccountTemplate from assets.models import AccountTemplate
from rbac.permissions import RBACPermission
from authentication.const import ConfirmType
from common.mixins import RecordViewLogMixin from common.mixins import RecordViewLogMixin
from common.permissions import UserConfirmation
from orgs.mixins.api import OrgBulkModelViewSet from orgs.mixins.api import OrgBulkModelViewSet

View File

@ -82,6 +82,10 @@ class Account(AbsConnectivity, BaseAccount):
def __str__(self): def __str__(self):
return '{}'.format(self.username) return '{}'.format(self.username)
@lazyproperty
def has_secret(self):
return bool(self.secret)
@classmethod @classmethod
def get_manual_account(cls): def get_manual_account(cls):
""" @INPUT 手动登录的账号(any) """ """ @INPUT 手动登录的账号(any) """

View File

@ -1,5 +1,6 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
from django.utils.translation import gettext_lazy as _ from django.utils.translation import gettext_lazy as _
from rest_framework import serializers
from assets.models import BaseAccount from assets.models import BaseAccount
from assets.serializers.base import AuthValidateMixin from assets.serializers.base import AuthValidateMixin
@ -9,6 +10,8 @@ __all__ = ['BaseAccountSerializer']
class BaseAccountSerializer(AuthValidateMixin, BulkOrgResourceModelSerializer): class BaseAccountSerializer(AuthValidateMixin, BulkOrgResourceModelSerializer):
has_secret = serializers.BooleanField(label=_("Has secret"), read_only=True)
class Meta: class Meta:
model = BaseAccount model = BaseAccount
fields_mini = ['id', 'name', 'username'] fields_mini = ['id', 'name', 'username']

View File

@ -1,5 +1,5 @@
from common.drf.serializers import SecretReadableMixin
from assets.models import AccountTemplate from assets.models import AccountTemplate
from common.drf.serializers import SecretReadableMixin
from .base import BaseAccountSerializer from .base import BaseAccountSerializer

View File

@ -18,6 +18,23 @@
<style> <style>
.login-content { .login-content {
{#box-shadow: 0 5px 5px -3px rgb(0 0 0 / 15%), 0 8px 10px 1px rgb(0 0 0 / 14%), 0 3px 14px 2px rgb(0 0 0 / 12%);#}
}
.login-footer {
height: 50px;
width: 1000px;
margin: 40px auto;
text-align: center;
}
.footer-item {
padding: 5px 20px;
color: gray;
}
.footer-item a {
color: gray;
} }
.help-block { .help-block {
@ -52,15 +69,15 @@
} }
.login-content { .login-content {
height: 490px; height: 500px;
width: 1066px; width: 1000px;
margin-right: auto; margin-right: auto;
margin-left: auto; margin-left: auto;
margin-top: calc((100vh - 470px) / 3); margin-top: calc((100vh - 470px) / 3);
} }
body { body {
background-color: #ffffff; background-color: #f3f3f3;
height: calc(100vh - (100vh - 470px) / 3); height: calc(100vh - (100vh - 470px) / 3);
} }
@ -141,10 +158,12 @@
color: #151515; color: #151515;
letter-spacing: 0; letter-spacing: 0;
} }
.more-methods-title { .more-methods-title {
position: relative; position: relative;
margin-top: 20px; margin-top: 20px;
} }
.more-methods-title:before, .more-methods-title:after { .more-methods-title:before, .more-methods-title:after {
position: absolute; position: absolute;
top: 50%; top: 50%;
@ -153,18 +172,23 @@
border: 1px dashed #e7eaec; border: 1px dashed #e7eaec;
width: 35%; width: 35%;
} }
.more-methods-title:before { .more-methods-title:before {
left: 0; left: 0;
} }
.more-methods-title:after { .more-methods-title:after {
right: 0; right: 0;
} }
.more-methods-title.ja:before, .more-methods-title.ja:after { .more-methods-title.ja:before, .more-methods-title.ja:after {
width: 26%; width: 26%;
} }
.captcha-field .form-group { .captcha-field .form-group {
margin-bottom: 5px; margin-bottom: 5px;
} }
.auto-login.form-group .checkbox { .auto-login.form-group .checkbox {
margin: 5px 0; margin: 5px 0;
} }
@ -176,16 +200,20 @@
.has-error .more-login { .has-error .more-login {
margin-top: 0; margin-top: 0;
} }
.welcome-message { .welcome-message {
color: #646A73; color: #646A73;
} }
.navbar-right .dropdown-menu { .navbar-right .dropdown-menu {
right: -24px !important; right: -24px !important;
left: auto; left: auto;
} }
.auto_login_box { .auto_login_box {
display: inline-block; display: inline-block;
} }
.auto-login input[type=checkbox] { .auto-login input[type=checkbox] {
-webkit-appearance: none; -webkit-appearance: none;
-moz-appearance: none; -moz-appearance: none;
@ -201,9 +229,11 @@
outline: none; outline: none;
cursor: pointer; cursor: pointer;
} }
.auto-login input[type=checkbox]:checked { .auto-login input[type=checkbox]:checked {
border: 4px solid var(--primary-color); border: 4px solid var(--primary-color);
} }
.auto-login > .row::after { .auto-login > .row::after {
clear: none; clear: none;
} }
@ -218,28 +248,15 @@
</a> </a>
</div> </div>
<div class="left-form-box {% if not form.challenge and not form.captcha %} no-captcha-challenge {% endif %}"> <div class="left-form-box {% if not form.challenge and not form.captcha %} no-captcha-challenge {% endif %}">
<div style="background-color: white"> <div style="position: relative;top: 50%;transform: translateY(-50%);">
<div class="jms-title"> <div style='padding: 15px 60px; text-align: left'>
<img src="{{ INTERFACE.logo_text_green }}" class="jms-title-img" /> <h2 style='font-weight: 400;display: inline'>
</div> {% trans 'Login' %}
<div class="contact-form col-md-10 col-md-offset-1"> </h2>
<form id="login-form" action="" method="post" role="form" novalidate="novalidate">
{% csrf_token %}
<div style="line-height: 17px;margin-bottom: 20px;color: #999999;">
{% if form.non_field_errors %}
<p class="help-block red-fonts">
{{ form.non_field_errors.as_text }}
</p>
{% else %}
<p class="welcome-message">
{% trans 'Welcome back, please enter username and password to login' %}
</p>
{% endif %}
</div>
<ul class=" nav navbar-top-links navbar-right"> <ul class=" nav navbar-top-links navbar-right">
<li class="dropdown"> <li class="dropdown">
<a class="dropdown-toggle login-page-language" data-toggle="dropdown" href="#" target="_blank"> <a class="dropdown-toggle login-page-language" data-toggle="dropdown" href="#"
target="_blank">
<i class="fa fa-globe fa-lg" style="margin-right: 2px"></i> <i class="fa fa-globe fa-lg" style="margin-right: 2px"></i>
<span>{{ current_lang.title }}<b class="caret"></b></span> <span>{{ current_lang.title }}<b class="caret"></b></span>
</a> </a>
@ -254,11 +271,24 @@
</ul> </ul>
</li> </li>
</ul> </ul>
</div>
<div class="contact-form col-md-10 col-md-offset-1" style='float: none; overflow: hidden'>
<form id="login-form" action="" method="post" role="form" novalidate="novalidate">
{% csrf_token %}
<div style="line-height: 17px;margin-bottom: 20px;color: #999999;">
{% if form.non_field_errors %}
<p class="help-block red-fonts">
{{ form.non_field_errors.as_text }}
</p>
{% endif %}
</div>
{% bootstrap_field form.username show_label=False %} {% bootstrap_field form.username show_label=False %}
<div class="form-group {% if form.password.errors %} has-error {% endif %}"> <div class="form-group {% if form.password.errors %} has-error {% endif %}">
<input type="password" class="form-control" id="password" placeholder="{% trans 'Password' %}" required> <input type="password" class="form-control" id="password" placeholder="{% trans 'Password' %}"
<input id="password-hidden" type="text" style="display:none" name="{{ form.password.html_name }}"> required>
<input id="password-hidden" type="text" style="display:none"
name="{{ form.password.html_name }}">
{% if form.password.errors %} {% if form.password.errors %}
<p class="help-block" style="text-align: left"> <p class="help-block" style="text-align: left">
{{ form.password.errors.as_text }} {{ form.password.errors.as_text }}
@ -306,7 +336,8 @@
<div class="more-login-items"> <div class="more-login-items">
{% for method in auth_methods %} {% for method in auth_methods %}
<a href="{{ method.url }}" class="more-login-item"> <a href="{{ method.url }}" class="more-login-item">
<i class="fa"><img src="{{ method.logo }}" height="15" width="15"></i> {{ method.name }} <i class="fa">
<img src="{{ method.logo }}" height="15" width="15"></i> {{ method.name }}
</a> </a>
{% endfor %} {% endfor %}
</div> </div>
@ -320,6 +351,7 @@
</div> </div>
</div> </div>
</div> </div>
</body> </body>
{% include '_foot_js.html' %} {% include '_foot_js.html' %}
<script type="text/javascript" src="/static/js/plugins/jsencrypt/jsencrypt.min.js"></script> <script type="text/javascript" src="/static/js/plugins/jsencrypt/jsencrypt.min.js"></script>

View File

@ -1,14 +1,13 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# #
from django.templatetags.static import static
from django.conf import settings from django.conf import settings
from django.templatetags.static import static
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
default_interface = dict(( default_interface = dict((
('logo_logout', static('img/logo.png')), ('logo_logout', static('img/logo.png')),
('logo_index', static('img/logo_text_white.png')), ('logo_index', static('img/logo_text_white.png')),
('logo_text_green', static('img/logo_text_green.png')), ('login_image', static('img/login_image.png')),
('login_image', static('img/login_image.jpg')),
('favicon', static('img/facio.ico')), ('favicon', static('img/facio.ico')),
('login_title', _('JumpServer Open Source Bastion Host')), ('login_title', _('JumpServer Open Source Bastion Host')),
('theme', 'classic_green'), ('theme', 'classic_green'),
@ -37,6 +36,3 @@ def jumpserver_processor(request):
'SECURITY_VIEW_AUTH_NEED_MFA': settings.SECURITY_VIEW_AUTH_NEED_MFA, 'SECURITY_VIEW_AUTH_NEED_MFA': settings.SECURITY_VIEW_AUTH_NEED_MFA,
}) })
return context return context

View File

@ -1,3 +1,3 @@
version https://git-lfs.github.com/spec/v1 version https://git-lfs.github.com/spec/v1
oid sha256:b02c5d36ea6ea96590be9a25dc6d3f1340a5af2aa940764243f85da1c756c732 oid sha256:aec4662e56ce44daac5eea9fe6d39c21ce9b2c55cfb60006ad6f0e639329c552
size 104221 size 105895

View File

@ -340,9 +340,8 @@ msgid "App assets"
msgstr "资产管理" msgstr "资产管理"
#: assets/automations/base/manager.py:123 #: assets/automations/base/manager.py:123
#, fuzzy
msgid "{} disabled" msgid "{} disabled"
msgstr "禁用" msgstr "{} 已禁用"
#: assets/const/account.py:6 audits/const.py:6 audits/const.py:64 #: assets/const/account.py:6 audits/const.py:6 audits/const.py:64
#: common/utils/ip/geoip/utils.py:31 common/utils/ip/geoip/utils.py:37 #: common/utils/ip/geoip/utils.py:31 common/utils/ip/geoip/utils.py:37
@ -374,52 +373,45 @@ msgid "Password"
msgstr "密码" msgstr "密码"
#: assets/const/account.py:13 #: assets/const/account.py:13
#, fuzzy
#| msgid "SSH Key"
msgid "SSH key" msgid "SSH key"
msgstr "SSH 密钥" msgstr "SSH 密钥"
#: assets/const/account.py:14 authentication/models/access_key.py:33 #: assets/const/account.py:14 authentication/models/access_key.py:33
msgid "Access key" msgid "Access key"
msgstr "Access key" msgstr "访问密钥"
#: assets/const/account.py:15 assets/models/_user.py:38 #: assets/const/account.py:15 assets/models/_user.py:38
#: authentication/models/sso_token.py:14 #: authentication/models/sso_token.py:14
msgid "Token" msgid "Token"
msgstr "Token" msgstr "令牌"
#: assets/const/automation.py:13 #: assets/const/automation.py:13
msgid "Ping" msgid "Ping"
msgstr "" msgstr ""
#: assets/const/automation.py:14 #: assets/const/automation.py:14
#, fuzzy
msgid "Gather facts" msgid "Gather facts"
msgstr "收集账号" msgstr "收集资产信息"
#: assets/const/automation.py:15 #: assets/const/automation.py:15
#, fuzzy
msgid "Create account" msgid "Create account"
msgstr "收集账号" msgstr "创建账号"
#: assets/const/automation.py:16 #: assets/const/automation.py:16
#, fuzzy
msgid "Change secret" msgid "Change secret"
msgstr "执行改密" msgstr "更改密码"
#: assets/const/automation.py:17 #: assets/const/automation.py:17
#, fuzzy
msgid "Verify account" msgid "Verify account"
msgstr "验证密码/密钥" msgstr "验证账号"
#: assets/const/automation.py:18 #: assets/const/automation.py:18
#, fuzzy
msgid "Gather accounts" msgid "Gather accounts"
msgstr "收集账号" msgstr "收集账号"
#: assets/const/automation.py:38 assets/serializers/account/base.py:26 #: assets/const/automation.py:38 assets/serializers/account/base.py:26
msgid "Specific" msgid "Specific"
msgstr "" msgstr "特有的"
#: assets/const/automation.py:39 ops/const.py:20 #: assets/const/automation.py:39 ops/const.py:20
msgid "All assets use the same random password" msgid "All assets use the same random password"
@ -450,7 +442,7 @@ msgstr "主机"
#: assets/const/category.py:12 #: assets/const/category.py:12
msgid "Device" msgid "Device"
msgstr "" msgstr "网络设备"
#: assets/const/category.py:13 assets/models/asset/database.py:8 #: assets/const/category.py:13 assets/models/asset/database.py:8
#: assets/models/asset/database.py:34 #: assets/models/asset/database.py:34
@ -458,14 +450,13 @@ msgid "Database"
msgstr "数据库" msgstr "数据库"
#: assets/const/category.py:14 #: assets/const/category.py:14
#, fuzzy
msgid "Cloud service" msgid "Cloud service"
msgstr "云管中心" msgstr "云服务"
#: assets/const/category.py:15 audits/const.py:62 #: assets/const/category.py:15 audits/const.py:62
#: terminal/models/applet/applet.py:20 #: terminal/models/applet/applet.py:20
msgid "Web" msgid "Web"
msgstr "" msgstr "Web"
#: assets/const/device.py:7 terminal/models/applet/applet.py:19 #: assets/const/device.py:7 terminal/models/applet/applet.py:19
#: tickets/const.py:8 #: tickets/const.py:8
@ -473,28 +464,24 @@ msgid "General"
msgstr "一般" msgstr "一般"
#: assets/const/device.py:8 #: assets/const/device.py:8
#, fuzzy
msgid "Switch" msgid "Switch"
msgstr "切换自" msgstr "交换机"
#: assets/const/device.py:9 #: assets/const/device.py:9
msgid "Router" msgid "Router"
msgstr "" msgstr "路由器"
#: assets/const/device.py:10 #: assets/const/device.py:10
msgid "Firewall" msgid "Firewall"
msgstr "" msgstr "防火墙"
#: assets/const/types.py:181 #: assets/const/types.py:181
#, fuzzy
#| msgid "MFA type"
msgid "All types" msgid "All types"
msgstr "MFA 类型" msgstr "所有类型"
#: assets/const/web.py:7 #: assets/const/web.py:7
#, fuzzy
msgid "Website" msgid "Website"
msgstr "网站图标" msgstr "网站"
#: assets/models/_user.py:24 #: assets/models/_user.py:24
msgid "Automatic managed" msgid "Automatic managed"
@ -656,9 +643,8 @@ msgid "Can view asset account template secret"
msgstr "可以查看资产账号密码" msgstr "可以查看资产账号密码"
#: assets/models/account.py:108 #: assets/models/account.py:108
#, fuzzy
msgid "Can change asset account template secret" msgid "Can change asset account template secret"
msgstr "可以更改资产账号密码" msgstr "可以更改账号模版密码"
#: assets/models/asset/common.py:103 assets/models/platform.py:109 #: assets/models/asset/common.py:103 assets/models/platform.py:109
#: assets/serializers/asset/common.py:65 #: assets/serializers/asset/common.py:65
@ -702,9 +688,8 @@ msgid "Can test asset connectivity"
msgstr "可以测试资产连接性" msgstr "可以测试资产连接性"
#: assets/models/asset/common.py:226 #: assets/models/asset/common.py:226
#, fuzzy
msgid "Can push account to asset" msgid "Can push account to asset"
msgstr "可以推送系统用户到资产" msgstr "可以推送账号到资产"
#: assets/models/asset/common.py:227 #: assets/models/asset/common.py:227
msgid "Can match asset" msgid "Can match asset"
@ -956,7 +941,7 @@ msgstr "校验日期"
#: assets/models/base.py:70 #: assets/models/base.py:70
msgid "Privileged" msgid "Privileged"
msgstr "" msgstr "特权账号"
#: assets/models/cmd_filter.py:28 perms/models/asset_permission.py:60 #: assets/models/cmd_filter.py:28 perms/models/asset_permission.py:60
#: users/models/group.py:25 users/models/user.py:681 #: users/models/group.py:25 users/models/user.py:681
@ -996,9 +981,9 @@ msgid "No account"
msgstr "没有账号" msgstr "没有账号"
#: assets/models/gateway.py:84 #: assets/models/gateway.py:84
#, fuzzy, python-brace-format #, python-brace-format
msgid "Unable to connect to port {port} on {address}" msgid "Unable to connect to port {port} on {address}"
msgstr "无法连接到 {ip} 上的端口 {port}" msgstr "无法连接到 {port} 上的端口 {address}"
#: assets/models/gateway.py:87 authentication/middleware.py:76 #: assets/models/gateway.py:87 authentication/middleware.py:76
#: xpack/plugins/cloud/providers/fc.py:48 #: xpack/plugins/cloud/providers/fc.py:48
@ -1231,10 +1216,8 @@ msgid "Account template not found"
msgstr "账号模版未找到" msgstr "账号模版未找到"
#: assets/serializers/account/account.py:72 #: assets/serializers/account/account.py:72
#, fuzzy
#| msgid "Asset Info"
msgid "Asset not found" msgid "Asset not found"
msgstr "资产信息" msgstr "资产不存在"
#: assets/serializers/account/backup.py:29 #: assets/serializers/account/backup.py:29
#: assets/serializers/automations/base.py:34 ops/mixin.py:22 ops/mixin.py:102 #: assets/serializers/automations/base.py:34 ops/mixin.py:22 ops/mixin.py:102
@ -2119,7 +2102,7 @@ msgstr "超级连接令牌"
#: authentication/models/private_token.py:9 #: authentication/models/private_token.py:9
msgid "Private Token" msgid "Private Token"
msgstr "SSH密钥" msgstr "私有令牌"
#: authentication/models/sso_token.py:15 #: authentication/models/sso_token.py:15
msgid "Expired" msgid "Expired"
@ -2142,8 +2125,6 @@ msgid "binding reminder"
msgstr "绑定提醒" msgstr "绑定提醒"
#: authentication/serializers/connect_token_secret.py:105 #: authentication/serializers/connect_token_secret.py:105
#, fuzzy
#| msgid "Builtin"
msgid "Is builtin" msgid "Is builtin"
msgstr "内置的" msgstr "内置的"
@ -2626,18 +2607,18 @@ msgid "This field is required."
msgstr "该字段是必填项。" msgstr "该字段是必填项。"
#: common/drf/fields.py:78 #: common/drf/fields.py:78
#, fuzzy, python-brace-format #, python-brace-format
msgid "Invalid pk \"{pk_value}\" - object does not exist." msgid "Invalid pk \"{pk_value}\" - object does not exist."
msgstr "%s对象不存在" msgstr "错误的 id \"{pk_value}\" - 对象不存在"
#: common/drf/fields.py:79 #: common/drf/fields.py:79
#, python-brace-format #, python-brace-format
msgid "Incorrect type. Expected pk value, received {data_type}." msgid "Incorrect type. Expected pk value, received {data_type}."
msgstr "" msgstr "错误类型。期望 pk 值,收到 {data_type}。"
#: common/drf/fields.py:141 #: common/drf/fields.py:141
msgid "Invalid data type, should be list" msgid "Invalid data type, should be list"
msgstr "" msgstr "错误的数据类型,应该是列表"
#: common/drf/fields.py:156 #: common/drf/fields.py:156
msgid "Invalid choice: {}" msgid "Invalid choice: {}"
@ -2653,7 +2634,7 @@ msgstr "解析文件错误: {}"
#: common/drf/serializers/common.py:86 #: common/drf/serializers/common.py:86
msgid "Children" msgid "Children"
msgstr "" msgstr "节点"
#: common/drf/serializers/common.py:94 #: common/drf/serializers/common.py:94
msgid "File" msgid "File"
@ -3686,7 +3667,7 @@ msgstr "企业微信 认证"
#: settings/serializers/auth/base.py:18 #: settings/serializers/auth/base.py:18
msgid "SSO Auth" msgid "SSO Auth"
msgstr "SSO Token 认证" msgstr "SSO 令牌认证"
#: settings/serializers/auth/base.py:19 #: settings/serializers/auth/base.py:19
msgid "SAML2 Auth" msgid "SAML2 Auth"
@ -3882,8 +3863,6 @@ msgid "Enable PKCE"
msgstr "启用 PKCE" msgstr "启用 PKCE"
#: settings/serializers/auth/oidc.py:43 #: settings/serializers/auth/oidc.py:43
#, fuzzy
#| msgid "Connect method"
msgid "Code challenge method" msgid "Code challenge method"
msgstr "连接方式" msgstr "连接方式"
@ -4047,7 +4026,7 @@ msgstr "模板+签名不能超过65个字"
#: settings/serializers/auth/sso.py:13 #: settings/serializers/auth/sso.py:13
msgid "Enable SSO auth" msgid "Enable SSO auth"
msgstr "启用 SSO Token 认证" msgstr "启用 SSO 令牌认证"
#: settings/serializers/auth/sso.py:14 #: settings/serializers/auth/sso.py:14
msgid "Other service can using SSO token login to JumpServer without password" msgid "Other service can using SSO token login to JumpServer without password"
@ -4055,7 +4034,7 @@ msgstr "其它系统可以使用 SSO Token 对接 JumpServer, 免去登录的过
#: settings/serializers/auth/sso.py:17 #: settings/serializers/auth/sso.py:17
msgid "SSO auth key TTL" msgid "SSO auth key TTL"
msgstr "Token 有效期" msgstr "令牌有效期"
#: settings/serializers/auth/sso.py:17 #: settings/serializers/auth/sso.py:17
#: xpack/plugins/cloud/serializers/account_attrs.py:176 #: xpack/plugins/cloud/serializers/account_attrs.py:176
@ -5662,8 +5641,6 @@ msgid "Run command"
msgstr "运行的命令" msgstr "运行的命令"
#: tickets/models/ticket/command_confirm.py:19 #: tickets/models/ticket/command_confirm.py:19
#, fuzzy
#| msgid "Command filter"
msgid "Command filter acl" msgid "Command filter acl"
msgstr "命令过滤器" msgstr "命令过滤器"
@ -5712,9 +5689,8 @@ msgid "Login asset"
msgstr "登录资产" msgstr "登录资产"
#: tickets/models/ticket/login_asset_confirm.py:17 #: tickets/models/ticket/login_asset_confirm.py:17
#, fuzzy
msgid "Login account" msgid "Login account"
msgstr "登录访问控制" msgstr "登录账号"
#: tickets/models/ticket/login_confirm.py:12 #: tickets/models/ticket/login_confirm.py:12
msgid "Login datetime" msgid "Login datetime"
@ -6342,7 +6318,7 @@ msgstr "非本地用户仅允许从第三方平台登录,不支持修改密码
#: users/views/profile/reset.py:149 users/views/profile/reset.py:160 #: users/views/profile/reset.py:149 users/views/profile/reset.py:160
msgid "Token invalid or expired" msgid "Token invalid or expired"
msgstr "Token错误或失效" msgstr "令牌错误或失效"
#: users/views/profile/reset.py:165 #: users/views/profile/reset.py:165
msgid "User auth from {}, go there change password" msgid "User auth from {}, go there change password"

View File

@ -13,7 +13,8 @@ class AdHocRunner:
"reboot", 'shutdown', 'poweroff', 'halt', 'dd', 'half', 'top' "reboot", 'shutdown', 'poweroff', 'halt', 'dd', 'half', 'top'
] ]
def __init__(self, inventory, module, module_args='', pattern='*', project_dir='/tmp/', extra_vars={}): def __init__(self, inventory, module, module_args='', pattern='*', project_dir='/tmp/', extra_vars={},
dry_run=False):
self.id = uuid.uuid4() self.id = uuid.uuid4()
self.inventory = inventory self.inventory = inventory
self.pattern = pattern self.pattern = pattern
@ -23,6 +24,7 @@ class AdHocRunner:
self.cb = DefaultCallback() self.cb = DefaultCallback()
self.runner = None self.runner = None
self.extra_vars = extra_vars self.extra_vars = extra_vars
self.dry_run = dry_run
def check_module(self): def check_module(self):
if self.module not in self.cmd_modules_choices: if self.module not in self.cmd_modules_choices:

View File

@ -1,3 +1,4 @@
from django.db.models import Count
from rest_framework.views import APIView from rest_framework.views import APIView
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
from rest_framework.response import Response from rest_framework.response import Response
@ -5,12 +6,14 @@ from rest_framework.response import Response
from ops.models import Job, JobExecution from ops.models import Job, JobExecution
from ops.serializers.job import JobSerializer, JobExecutionSerializer from ops.serializers.job import JobSerializer, JobExecutionSerializer
__all__ = ['JobViewSet', 'JobExecutionViewSet', 'JobRunVariableHelpAPIView', 'JobAssetDetail', 'JobExecutionTaskDetail'] __all__ = ['JobViewSet', 'JobExecutionViewSet', 'JobRunVariableHelpAPIView',
'JobAssetDetail', 'JobExecutionTaskDetail','FrequentUsernames']
from ops.tasks import run_ops_job_execution from ops.tasks import run_ops_job_execution
from ops.variables import JMS_JOB_VARIABLE_HELP from ops.variables import JMS_JOB_VARIABLE_HELP
from orgs.mixins.api import OrgBulkModelViewSet from orgs.mixins.api import OrgBulkModelViewSet
from orgs.utils import tmp_to_org, get_current_org_id, get_current_org from orgs.utils import tmp_to_org, get_current_org_id, get_current_org
from assets.models import Account
def set_task_to_serializer_data(serializer, task): def set_task_to_serializer_data(serializer, task):
@ -111,3 +114,12 @@ class JobExecutionTaskDetail(APIView):
'is_success': execution.is_success, 'is_success': execution.is_success,
'time_cost': execution.time_cost, 'time_cost': execution.time_cost,
}) })
class FrequentUsernames(APIView):
rbac_perms = ()
permission_classes = ()
def get(self, request, **kwargs):
top_accounts = Account.objects.all().values('username').annotate(total=Count('username')).order_by('total')
return Response(data=top_accounts)

View File

@ -166,6 +166,10 @@ class JobExecution(JMSOrgBaseModel):
return return
result = self.current_job.args result = self.current_job.args
result += " chdir={}".format(self.current_job.chdir) result += " chdir={}".format(self.current_job.chdir)
if self.current_job.module in ['python']:
result += " executable={}".format(self.current_job.module)
print(result)
return self.job.args return self.job.args
def get_runner(self): def get_runner(self):
@ -187,9 +191,17 @@ class JobExecution(JMSOrgBaseModel):
if self.current_job.type == 'adhoc': if self.current_job.type == 'adhoc':
args = self.compile_shell() args = self.compile_shell()
module = "shell"
if self.current_job.module not in ['python']:
module = self.current_job.module
runner = AdHocRunner( runner = AdHocRunner(
self.inventory_path, self.current_job.module, module_args=args, self.inventory_path,
pattern="all", project_dir=self.private_dir, extra_vars=extra_vars, module,
module_args=args,
pattern="all",
project_dir=self.private_dir,
extra_vars=extra_vars,
) )
elif self.current_job.type == 'playbook': elif self.current_job.type == 'playbook':
runner = PlaybookRunner( runner = PlaybookRunner(

View File

@ -1,5 +1,7 @@
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from rest_framework import serializers from rest_framework import serializers
from assets.models import Node
from common.drf.fields import ReadableHiddenField from common.drf.fields import ReadableHiddenField
from ops.mixin import PeriodTaskSerializerMixin from ops.mixin import PeriodTaskSerializerMixin
from ops.models import Job, JobExecution from ops.models import Job, JobExecution
@ -10,6 +12,16 @@ from orgs.mixins.serializers import BulkOrgResourceModelSerializer
class JobSerializer(BulkOrgResourceModelSerializer, PeriodTaskSerializerMixin): class JobSerializer(BulkOrgResourceModelSerializer, PeriodTaskSerializerMixin):
creator = ReadableHiddenField(default=serializers.CurrentUserDefault()) creator = ReadableHiddenField(default=serializers.CurrentUserDefault())
run_after_save = serializers.BooleanField(label=_("Run after save"), default=False, required=False) run_after_save = serializers.BooleanField(label=_("Run after save"), default=False, required=False)
nodes = serializers.ListField(required=False, child=serializers.CharField())
def create(self, validated_data):
assets = validated_data.__getitem__('assets')
node_ids = validated_data.pop('nodes')
if node_ids:
nodes = Node.objects.filter(id__in=node_ids)
assets.extend(
Node.get_nodes_all_assets(*nodes).exclude(id__in=[asset.id for asset in assets]))
return super().create(validated_data)
class Meta: class Meta:
model = Job model = Job
@ -22,7 +34,7 @@ class JobSerializer(BulkOrgResourceModelSerializer, PeriodTaskSerializerMixin):
"chdir", "chdir",
"comment", "comment",
"summary", "summary",
"is_periodic", "interval", "crontab", "run_after_save" "is_periodic", "interval", "crontab", "run_after_save", "nodes"
] ]

View File

@ -26,6 +26,7 @@ urlpatterns = [
path('variables/help/', api.JobRunVariableHelpAPIView.as_view(), name='variable-help'), path('variables/help/', api.JobRunVariableHelpAPIView.as_view(), name='variable-help'),
path('job-execution/asset-detail/', api.JobAssetDetail.as_view(), name='asset-detail'), path('job-execution/asset-detail/', api.JobAssetDetail.as_view(), name='asset-detail'),
path('job-execution/task-detail/', api.JobExecutionTaskDetail.as_view(), name='task-detail'), path('job-execution/task-detail/', api.JobExecutionTaskDetail.as_view(), name='task-detail'),
path('frequent-username/', api.FrequentUsernames.as_view(), name='frequent-usernames'),
path('ansible/job-execution/<uuid:pk>/log/', api.AnsibleTaskLogApi.as_view(), name='job-execution-log'), path('ansible/job-execution/<uuid:pk>/log/', api.AnsibleTaskLogApi.as_view(), name='job-execution-log'),
path('celery/task/<uuid:name>/task-execution/<uuid:pk>/log/', api.CeleryTaskExecutionLogApi.as_view(), path('celery/task/<uuid:name>/task-execution/<uuid:pk>/log/', api.CeleryTaskExecutionLogApi.as_view(),

View File

@ -25,23 +25,25 @@ user_permission_urlpatterns = [
name='user-direct-assets-as-tree'), name='user-direct-assets-as-tree'),
path('<str:user>/ungroup/assets/tree/', api.UserUngroupAssetsAsTreeApi.as_view(), path('<str:user>/ungroup/assets/tree/', api.UserUngroupAssetsAsTreeApi.as_view(),
name='user-ungroup-assets-as-tree'), name='user-ungroup-assets-as-tree'),
# tree-node
# tree-node不包含资产
path('<str:user>/nodes/tree/', api.UserAllPermedNodesAsTreeApi.as_view(), path('<str:user>/nodes/tree/', api.UserAllPermedNodesAsTreeApi.as_view(),
name='user-all-nodes-as-tree'), name='user-all-nodes-as-tree'),
path('<str:user>/nodes/children/tree/', api.UserPermedNodeChildrenAsTreeApi.as_view(), path('<str:user>/nodes/children/tree/', api.UserPermedNodeChildrenAsTreeApi.as_view(),
name='user-node-children-as-tree'), name='user-node-children-as-tree'),
# tree-node-with-asset # tree-node-with-asset
# 异步树 # 异步树
path('<str:user>/nodes/children-with-assets/tree/', path('<str:user>/nodes/children-with-assets/tree/',
api.UserPermedNodeChildrenWithAssetsAsTreeApi.as_view(), api.UserPermedNodeChildrenWithAssetsAsTreeApi.as_view(),
name='user-node-children-with-assets-as-tree'), name='user-node-children-with-assets-as-tree'),
# 同步树
path('<str:user>/nodes/all-with-assets/tree/',
api.UserPermedNodesWithAssetsAsTreeApi.as_view(),
name='user-nodes-with-assets-as-tree'),
path('<str:user>/nodes/children-with-k8s/tree/', path('<str:user>/nodes/children-with-k8s/tree/',
api.UserGrantedK8sAsTreeApi.as_view(), api.UserGrantedK8sAsTreeApi.as_view(),
name='user-nodes-children-with-k8s-as-tree'), name='user-nodes-children-with-k8s-as-tree'),
# 同步树
path('<str:user>/nodes-with-assets/tree/', api.UserPermedNodesWithAssetsAsTreeApi.as_view(),
name='user-nodes-with-assets-as-tree'),
# accounts # accounts
path('<str:user>/assets/<uuid:asset_id>/accounts/', api.UserPermedAssetAccountsApi.as_view(), path('<str:user>/assets/<uuid:asset_id>/accounts/', api.UserPermedAssetAccountsApi.as_view(),
name='user-permed-asset-accounts'), name='user-permed-asset-accounts'),

Binary file not shown.

Before

Width:  |  Height:  |  Size: 225 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 184 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.9 KiB

After

Width:  |  Height:  |  Size: 5.6 KiB

View File

@ -26,8 +26,14 @@ logger = get_logger(__file__)
class RolesSerializerMixin(serializers.Serializer): class RolesSerializerMixin(serializers.Serializer):
system_roles = ObjectRelatedField(queryset=Role.system_roles, label=_("System roles"), many=True) system_roles = ObjectRelatedField(
org_roles = ObjectRelatedField(queryset=Role.org_roles, label=_("Org roles"), many=True) queryset=Role.system_roles, attrs=('id', 'display_name'),
label=_("System roles"), many=True
)
org_roles = ObjectRelatedField(
queryset=Role.org_roles, attrs=('id', 'display_name'),
label=_("Org roles"), many=True
)
@staticmethod @staticmethod
def get_system_roles_display(user): def get_system_roles_display(user):