Compare commits

...

1202 Commits

Author SHA1 Message Date
feng
a9d455e867 perf: ntlm_err 2025-04-27 18:53:43 +08:00
feng
d06d26ac54 perf: Display asset/account connectivity error message 2025-04-27 18:50:00 +08:00
fit2bot
e992c44e11
perf: change lfs files download (#15293)
* perf: change lfs files download

* perf: clean unused ansible module

* perf: update lfs download

* perf: Update Dockerfile with new base image tag

* perf: change download path

* perf: Update Dockerfile with new base image tag

---------

Co-authored-by: ibuler <ibuler@qq.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2025-04-27 14:35:10 +08:00
feng
24fe058fd9 perf: lina translate 2025-04-25 18:07:09 +08:00
feng
a3fef9cc54 perf: Update the key when the integration-application is updated or created 2025-04-25 17:29:44 +08:00
ibuler
471053e62a perf: change mcp integrate 2025-04-25 17:28:03 +08:00
jiangweidong
dc6308b030 perf: if the apply-asset-ticket name is 128 characters long, will raise 500 2025-04-25 17:27:13 +08:00
feng
f016ae6161 perf: add sftplog command models field index 2025-04-25 15:21:55 +08:00
feng
14a8d877e0 perf: ko translate 2025-04-25 15:04:03 +08:00
feng
ddf20570a1 perf: device support ad 2025-04-23 19:38:01 +08:00
feng
1ad9616b7f perf: gather facts gpu info 2025-04-22 17:48:21 +08:00
刘瑞斌
d7bc6bb201 chore: use uv as package-ecosystem 2025-04-21 13:36:24 +08:00
feng
f855043468 perf: luna ru translate 2025-04-21 11:34:51 +08:00
fit2bot
3159a4e794
perf: change domain to zone (#15255)
* perf: change domain to zone

* perf: change domain to zone

* perf: change some word

* perf: update gateway enabled i18n

* perf: change migrations

---------

Co-authored-by: ibuler <ibuler@qq.com>
2025-04-21 10:30:18 +08:00
feng
57fcebfdd3 fix: No data found for the carrying organization 2025-04-18 16:50:07 +08:00
feng626
c500bb4e4c Revert "Revert "perf:Stored command records in ES support accurate searching.""
This reverts commit 6bc1c5bd50.
2025-04-18 16:50:07 +08:00
feng
fd062b0da6 perf: ru translate 2025-04-18 14:52:44 +08:00
ibuler
bcb112d5c6 perf: user profile api 2025-04-18 14:11:56 +08:00
fit2bot
533dbf316c
perf: add ali rds dependencies (#15247)
* perf: add ali rds dependencies

* perf: Update Dockerfile with new base image tag

---------

Co-authored-by: Eric <xplzv@126.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2025-04-18 12:11:51 +08:00
github-actions[bot]
9cce94b709 perf: Update Dockerfile with new base image tag 2025-04-18 11:53:40 +08:00
Eric
8b815d812b perf: modify Dockerfile-base 2025-04-18 11:53:40 +08:00
github-actions[bot]
a168fc8a62 perf: Update Dockerfile with new base image tag 2025-04-18 11:29:07 +08:00
Eric
faae1a09d1 perf: lint dependencies 2025-04-18 11:29:07 +08:00
github-actions[bot]
26e819e120 perf: Update Dockerfile with new base image tag 2025-04-18 11:29:07 +08:00
Eric
79579654a1 perf: use uv tool
perf: add Homepage
perf: add env
2025-04-18 11:29:07 +08:00
feng626
6bc1c5bd50 Revert "perf:Stored command records in ES support accurate searching."
This reverts commit 3d6d2af268.
2025-04-17 20:16:06 +08:00
ibuler
36f312b943 perf: page queryset mixin 2025-04-17 19:47:51 +08:00
ibuler
11811c453b perf: page queryset mixin 2025-04-17 19:47:51 +08:00
ibuler
12fadeec58 perf: revert terminal api 2025-04-17 19:34:23 +08:00
ibuler
b49fd21e08 perf: 虚拟账号 api 2025-04-17 17:50:42 +08:00
feng
9b982eb592 perf: change secret api perm 2025-04-17 17:15:06 +08:00
wangruidong
31652ef5b1 fix: include openid in source validation logic 2025-04-17 16:24:00 +08:00
feng
8fef18b991 perf: gather account windows playbook failed_when: false 2025-04-17 15:55:49 +08:00
ibuler
c804c053d2 perf: revert api 2025-04-17 15:53:52 +08:00
ibuler
bef2282604 perf: asset list compute account amount 2025-04-17 15:15:36 +08:00
feng
cabc069045 perf: Translate 2025-04-17 15:12:16 +08:00
feng
99c9a021b7 fix: update applet host (platform failed) 2025-04-17 14:55:36 +08:00
ibuler
6cb3cc1f29 perf: 修改 DS 的一些翻译 2025-04-17 14:47:11 +08:00
feng
67422ef4ba fix: automation no account 2025-04-17 14:00:24 +08:00
gerry
3d6d2af268 perf:Stored command records in ES support accurate searching. 2025-04-17 11:43:16 +08:00
wangruidong
ee97e45cc3 fix: Allow superusers delete adhoc and playbook 2025-04-17 10:54:47 +08:00
feng
0131eaa6db perf: es search 2025-04-16 18:15:58 +08:00
feng
eaa390fd6f perf: update asset directory_services allow_empty true 2025-04-16 17:39:55 +08:00
ibuler
e2b8fd0d40 perf: change account filter by asset 2025-04-16 17:37:36 +08:00
feng
2aace05099 perf: as account username 2025-04-16 17:29:23 +08:00
ibuler
1ee70af93d perf: applet account select 2025-04-16 16:43:34 +08:00
feng
fa70fb2921 perf: Translate 2025-04-16 15:37:11 +08:00
ibuler
01a6019022 perf: swagger api 2025-04-16 14:53:51 +08:00
wangruidong
5c61a11d82 fix: add periodic_display to read_only_fields in Job serializer 2025-04-16 14:18:11 +08:00
fit2bot
67f3341310
perf: change db prefetch (#15215) 2025-04-16 13:48:12 +08:00
feng
cb49e26387 perf: refresh asset type tree 2025-04-16 11:44:07 +08:00
feng
314da330c0 perf: Asset account filter 2025-04-16 11:36:58 +08:00
halo
f1c98fda34 perf: client version 2025-04-16 10:39:10 +08:00
ibuler
1fdd1036d3 perf: directory service db 2025-04-15 20:24:10 +08:00
feng
e286997090 perf: koko translate 2025-04-15 17:26:06 +08:00
wangruidong
ce3daf5496 fix: update translation strings and improve error handling in inventory and job modules 2025-04-15 16:49:35 +08:00
feng
631570b819 perf: Asset filter 2025-04-15 16:45:50 +08:00
feng
9b1bff0847 perf: client version 2025-04-15 12:28:59 +08:00
feng
ee8a2afe16 fix: ES no data found 2025-04-15 12:07:56 +08:00
CaptainB
1a01c0537c chore: Add Dependabot configuration for pip dependencies 2025-04-15 11:47:59 +08:00
jiangweidong
64393fe695 fix: Error in using set method 2025-04-15 11:47:01 +08:00
jiangweidong
11ef4fab4e perf: Es subsequent optimization 2025-04-15 11:47:01 +08:00
jiangweidong
9f8256f885 fix: Solve the problem that log details cannot be viewed in non-default organizations 2025-04-15 11:47:01 +08:00
fit2bot
5390fbacec
perf: some swagger api (#15203)
* perf: some swagger api

* perf: update deps

* perf: Update Dockerfile with new base image tag

---------

Co-authored-by: ibuler <ibuler@qq.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2025-04-15 11:43:36 +08:00
wangruidong
8b9fe3c72b perf: add default logo handling in IntegrationApplicationSerializer 2025-04-15 11:08:15 +08:00
wangruidong
20070e0647 fix: improve crontab validation logic 2025-04-15 11:07:23 +08:00
ibuler
47b72cb35e perf: add leak password db config 2025-04-14 17:27:27 +08:00
feng
2ca0e9a5a2 perf: automation .account -> .all_accounts 2025-04-14 14:18:56 +08:00
feng
3b2ac101c8 perf: windows ad asset info 2025-04-14 10:14:38 +08:00
feng
6795f036dd perf: ad asset automation gather_facts_enabled true 2025-04-11 17:53:43 +08:00
feng
aaa1f48258 perf: koko translate 2025-04-11 17:45:58 +08:00
feng
53c5bab203 perf: user login acl remove warning notify_and_warn action 2025-04-10 15:56:31 +08:00
feng
1254d28463 perf: windows ad gather account 2025-04-10 15:33:42 +08:00
wangruidong
d6b1a577fc fix: resolve ForeignKeyViolation in AccountRisk 2025-04-09 19:30:05 +08:00
wangruidong
5ab85d3561 perf: File directory adjustment 2025-04-09 19:29:52 +08:00
wangruidong
467f4c5d4f perf: Translate 2025-04-09 19:29:52 +08:00
wangruidong
f2404319af fix: account_prefer 2025-04-09 19:29:52 +08:00
wangruidong
bbeadf7dbe perf: optimize adhoc asset selection experience 2025-04-09 19:29:52 +08:00
feng
941bd9b3f4 perf: Translate 2025-04-09 19:22:32 +08:00
feng
37a307a9d0 perf: Windows AD 2025-04-09 18:22:00 +08:00
ibuler
528f9045d0 perf: update connection token 2025-04-09 11:11:39 +08:00
ibuler
a317549a01 perf: migrations merge 2025-04-08 19:21:37 +08:00
ibuler
0f5681de7d chore: remove workflow 2025-04-08 19:21:37 +08:00
ibuler
a7c514f8d8 perf: rename some workd 2025-04-08 19:21:37 +08:00
ibuler
75ea0079a2 perf: update ad domain 2025-04-08 19:21:37 +08:00
ibuler
4cc1687bf8 perf: update ad 2025-04-08 19:21:37 +08:00
ibuler
76e57b9a3e perf: update ad 2025-04-08 19:21:37 +08:00
ibuler
ba3bce1e2e perf: perm account valid 2025-04-08 19:21:37 +08:00
ibuler
45f0343cfa perf: update ds 2025-04-08 19:21:37 +08:00
ibuler
acaa4cf2d5 perf: rename ad to ds 2025-04-08 19:21:37 +08:00
ibuler
3f452daee8 perf: ad as asset 2025-04-08 19:21:37 +08:00
feng
5e25361ee8 perf: Operate default log 2025-04-08 18:37:25 +08:00
jiangweidong
7b7604e14d Added cloud sync global released asset tab (v4.9) 2025-04-08 18:31:53 +08:00
Eric
f9037878c3 perf: add remoteapp bitmapcache settings 2025-04-08 17:36:48 +08:00
fit2bot
29ddfcac17
fix: Optimize UserConfirmDialog to send code via email (#15164)
* fix: Optimize UserConfirmDialog to send code via email

* fix: Optimize verification failure without error reporting

---------

Co-authored-by: halo <wuyihuangw@gmail.com>
Co-authored-by: Bryan <jiangjie.bai@fit2cloud.com>
2025-04-08 15:50:15 +08:00
wangruidong
519ec65ad4 perf: LDAP strict sync 2025-04-08 15:43:15 +08:00
jiangweidong
1f60e328b6 perf: Export resources to add operation logs 2025-04-08 15:37:29 +08:00
Bai
e8e0ea920b perf: change jumpserver.org to jumpserver.com 2025-04-08 14:23:28 +08:00
Aaron3S
4fd8efd043 feat: remove oracle dyn port 2025-04-08 13:50:30 +08:00
wangruidong
623c800d31 fix: failure when deleting remote account in SQL Server 2025-04-08 13:48:48 +08:00
wangruidong
d2c6e3c7a6 fix: Job audit: Search job list, filter failures based on command 2025-04-08 13:41:59 +08:00
github-actions[bot]
dc5883576d Auto-translate README 2025-04-08 13:34:21 +08:00
Bai
0a9c9fb227 perf: change readme 2025-04-08 13:26:48 +08:00
Bai
15a1a58eca perf: change support email 2025-04-08 13:20:22 +08:00
halo
782401ef86 fix: Implement function _check_code 2025-04-07 16:20:43 +08:00
maninhill
8abcd201bc chore: Update README.md 2025-04-03 15:11:38 +08:00
Bryan
cdbc10ac72 Update README.md 2025-03-31 17:37:01 +08:00
Bryan
ceeef890e6 Update README.md 2025-03-31 17:37:01 +08:00
Bryan
dc8a172884 Update README.md 2025-03-28 17:53:22 +08:00
Bryan
62115e43bb Update README.md 2025-03-28 17:53:22 +08:00
Bryan
5eced85e69 Update README.md 2025-03-28 17:53:22 +08:00
Bryan
ec99b17b76 Update README.md 2025-03-28 17:53:22 +08:00
Bryan
84569720c3 Update README.md 2025-03-28 17:53:22 +08:00
feng
65984d38f1 perf: Account filter 2025-03-28 16:34:53 +08:00
feng
f6913ac63c perf: Priacy mode 2025-03-27 18:34:11 +08:00
Halo
514b2cdfc5
feat: Email as a method for multi-factor authentication (#15134)
* feat: Email as a method for multi-factor authentication

* perf: Optimize the MFA email sending logic

* perf: Optimize some parameters

* perf: Translate
2025-03-27 17:26:38 +08:00
ibuler
b55000663e perf: 提升服务注册安全性 2025-03-27 16:25:28 +08:00
ibuler
9ed822bb3e perf: 优化获取 labels 2025-03-27 16:23:14 +08:00
feng
ea599d7695 perf: Perm the template push account 2025-03-27 14:13:29 +08:00
feng
01c5d68b35 perf: change secret change_secret_result 2025-03-27 14:08:19 +08:00
feng
2e2c331941 perf: translate 2025-03-25 18:03:10 +08:00
feng
266ea9b858 perf: Change secret 2025-03-25 16:05:47 +08:00
feng
5f2e838342 perf: koko sftp translate 2025-03-25 14:33:50 +08:00
ZhaoJiSen
544ad5532b
Merge pull request #15117 from jumpserver/pr@dev@koko_translate
perf: koko translate
2025-03-25 11:02:08 +08:00
feng
d22d715ee7 perf: koko translate 2025-03-25 10:59:45 +08:00
halo
dd2366532c perf: Use a domain account to avoid automatically creating a local account 2025-03-25 10:22:16 +08:00
ibuler
9667a3d340 perf: add crontab check min 60m 2025-03-25 09:56:37 +08:00
feng
c8e6e5d38c perf: Login language 2025-03-24 18:55:40 +08:00
feng
9d1047fae2 perf: Translate 2025-03-24 16:51:49 +08:00
feng
28f97d746d perf: Translate 2025-03-24 15:39:37 +08:00
ibuler
be72344c63 perf: update tk create 2025-03-24 10:54:17 +08:00
ibuler
d3176b68a8 perf: 优化 admin token 判断 2025-03-24 10:06:22 +08:00
ibuler
5411f65546 perf: update get permed account 2025-03-24 09:51:46 +08:00
ibuler
e3ba468004 fix: 修复 token 直连的问题 2025-03-24 09:51:46 +08:00
feng
a03a11efa4 perf: Translate 2025-03-21 15:49:42 +08:00
feng
d344495417 perf: Translate 2025-03-21 14:24:37 +08:00
github-actions[bot]
9412bd0331 Auto-translate README 2025-03-21 13:29:01 +08:00
Bai
8d73ddb1cd perf: update readme languages 2025-03-21 13:17:56 +08:00
Bai
7fe56a5e1a perf: README 2025-03-21 10:35:27 +08:00
wangruidong
db259d4e8b fix: Failed to delete account 2025-03-20 17:01:43 +08:00
feng
afc31ee5ce perf: translate 2025-03-20 15:33:53 +08:00
feng
69cc47e0cb perf: account backup 2025-03-20 13:25:40 +08:00
wangruidong
2455afc2d2 fix: Use ConfirmOrIgnore for confirmed status in account updates 2025-03-20 11:14:16 +08:00
Chenyang Shen
33cb793c19
Merge pull request #15078 from jumpserver/pr@dev@fix_face_vector_diabled
fix: fix face live can not enable
2025-03-19 20:47:39 +08:00
Aaron3S
5522ba0241 fix: fix face live can not enable 2025-03-19 20:45:35 +08:00
wangruidong
0fed338277 fix: Change secret failed, no module named libs 2025-03-19 20:23:02 +08:00
feng
8f78919b3e perf: Change secret filter days 2025-03-19 18:56:23 +08:00
wangruidong
f6fc9b1f5c fix: Update account status to confirmed 2025-03-19 18:30:09 +08:00
jiangweidong
365dbf6e14 fix: CTYun cannot sync 2025-03-19 17:59:43 +08:00
wangruidong
e5a5d5f727 fix: Account filter error 2025-03-19 16:34:42 +08:00
wangruidong
e0d6b843ee fix: Add account_deleted_accounts to RiskSummary 2025-03-19 15:25:47 +08:00
wangruidong
2a31a7d444 fix: Validate input data for account status updates 2025-03-19 14:45:17 +08:00
wangruidong
e68d5564c6 fix: Ansible could not initialize the preferred locale 2025-03-19 11:21:15 +08:00
feng
4fdb049c9d perf: translate 2025-03-19 11:07:42 +08:00
wangruidong
e5f66c4be2 fix: password_expired account, not listed as risk account 2025-03-18 19:19:51 +08:00
wangruidong
325edfe704 fix: long_time_no_login account, not listed as risk account 2025-03-18 18:59:23 +08:00
feng626
3a93aeb155
Merge pull request #15064 from jumpserver/pr@dev@license
perf: License
2025-03-18 18:56:50 +08:00
feng626
28d6f2f9ef
Merge branch 'dev' into pr@dev@license 2025-03-18 18:56:41 +08:00
feng
2bfcebd064 perf: License 2025-03-18 18:54:12 +08:00
老广
6f2cb9b3c6
Merge pull request #15063 from jumpserver/pr@dev@add_delete_account_action
perf: update some 18n
2025-03-18 17:52:05 +08:00
fit2bot
cdebfd8121
perf: add delete account action (#15059) 2025-03-18 16:57:27 +08:00
ibuler
57d05e6ff0 perf: update some 18n 2025-03-18 16:56:46 +08:00
ibuler
49378d1f13 perf: update ansible env set 2025-03-18 16:32:59 +08:00
wangruidong
e802e145af fix: Create ssh_key failed 2025-03-18 16:23:01 +08:00
ibuler
2faad88b78 perf: remove ga also 2025-03-18 16:07:49 +08:00
ibuler
33a47139b3 perf: ansible env to jms 2025-03-18 16:02:33 +08:00
wangruidong
50faa3242a perf: Translate 2025-03-18 14:51:04 +08:00
ibuler
d89164db63 perf: add delete account action 2025-03-18 14:38:54 +08:00
ibuler
f72fc19ba6 perf: change Command 2025-03-18 13:48:14 +08:00
feng
7bd03c7863 perf: change secret filter 2025-03-18 13:29:36 +08:00
wangruidong
ed95a89a77 fix: Create ssh_key set is_active error 2025-03-18 11:29:40 +08:00
Chenyang Shen
428a4470c9
Merge pull request #15054 from jumpserver/pr@dev@feat_add_translate
feat: add translate
2025-03-18 10:44:00 +08:00
Aaron3S
b206e751da feat: add translate 2025-03-18 10:40:06 +08:00
ibuler
6913518046 perf: update automation report template 2025-03-17 19:21:52 +08:00
wangruidong
f5a2f5e538 fix: Check authorized_keys change failed 2025-03-17 19:18:34 +08:00
feng
f4fa153ffa perf: push account secret 2025-03-17 18:45:25 +08:00
feng626
c70d7f709f
Merge pull request #15047 from jumpserver/pr@dev@excution
perf: Automation execution
2025-03-17 17:28:37 +08:00
feng626
ec95144907
Merge branch 'dev' into pr@dev@excution 2025-03-17 17:28:03 +08:00
feng
424ef4d9a5 perf: Automation execution 2025-03-17 17:26:09 +08:00
wangruidong
40bbc4a02c perf: Translate ExecuteAfterSaving 2025-03-17 16:32:39 +08:00
wangruidong
a784ca29c3 fix: Update counter naming for group and sudo changes in RiskSummary 2025-03-17 15:58:37 +08:00
fit2bot
b7820c6a5b
perf: update automation report 2025-03-17 10:31:49 +08:00
feng
7bac1b42e4 perf: risk add account 2025-03-14 17:41:13 +08:00
feng
0a6757946a perf: translate 2025-03-14 17:02:39 +08:00
wangruidong
e4d169cabe perf: migrations type length 2025-03-14 15:09:06 +08:00
wangruidong
c80ca5236d perf: migrations bulk_size 2025-03-14 14:57:16 +08:00
wangruidong
f513eb62a6 perf: migrations automationexecution type 2025-03-14 14:46:20 +08:00
wangruidong
94e8c62953 fix: Allow empty files in FileSerializer 2025-03-13 18:54:20 +08:00
feng
bd783e6a8d perf: asset perm expire 2025-03-13 18:25:28 +08:00
wangruidong
35bc3a0e2b fix: Account risk search failed 2025-03-13 18:21:51 +08:00
ibuler
996bee3afd perf: update gathered account sync 2025-03-13 18:03:16 +08:00
feng
7c4931b6af perf: execution type 2025-03-13 17:54:02 +08:00
wangruidong
9992fb35be perf: Translate IpGroup 2025-03-13 17:10:09 +08:00
wangruidong
cc63c956cb fix: Delete gather account failed 2025-03-13 16:25:27 +08:00
feng
15919085bc perf: change secret push record list 2025-03-13 14:58:11 +08:00
wangruidong
9e4b82bf45 perf: Translate risk handle 2025-03-13 10:00:31 +08:00
wangruidong
28f85a0186 perf: BaseManager email subject translate 2025-03-12 19:05:23 +08:00
feng
30e64ecfc1 perf: change secret 2025-03-12 18:57:03 +08:00
feng
042c3d1ba8 perf: gather account node_id filter 2025-03-12 18:05:42 +08:00
feng
94a8122eac perf: report 2025-03-12 17:57:15 +08:00
feng
88450d796f perf: translate 2025-03-12 16:45:19 +08:00
wangruidong
3d28e255c0 fix: Account risk page change secret failed 2025-03-12 16:01:42 +08:00
wangruidong
153be1508f perf: Remove account_deleted, password_error, no_admin_account others risk 2025-03-12 15:40:54 +08:00
ibuler
6e83420e67 perf: update i18n 2025-03-12 15:19:34 +08:00
wangruidong
e03d983020 fix: Gather account new-found risk failed 2025-03-12 14:54:15 +08:00
feng
0afc1e6f5b perf: translate 2025-03-12 11:02:40 +08:00
wangruidong
a9ea801862 fix: Update username and address_last_login field length in account models 2025-03-12 10:36:35 +08:00
Bai
23f9b79142 feat: support Spanish and Russian 2025-03-12 10:24:34 +08:00
ibuler
4db15d9af7 perf: update action choice field default 2025-03-12 10:23:01 +08:00
ZhaoJiSen
bc9782bd55
Merge pull request #15001 from jumpserver/pr@dev@account_risk_filter
perf: account filter node
2025-03-11 19:20:14 +08:00
feng
635e9f5079 perf: account filter node 2025-03-11 19:19:14 +08:00
Eric_Lee
71259886fe
Merge pull request #14834 from jumpserver/pr@dev@perf_replay_file
perf: use a custom local path to save session replay file
2025-03-11 19:12:42 +08:00
feng626
a26cc7ce1f
Merge pull request #15000 from jumpserver/pr@dev@connectiontoken
perf: connection token
2025-03-11 18:34:35 +08:00
feng
a2aa5e9bf9 perf: connection token 2025-03-11 18:32:38 +08:00
ZhaoJiSen
82de373f8e
Merge pull request #14998 from jumpserver/pr@dev@chage_secret
perf: change secret drop bulk record
2025-03-11 17:39:45 +08:00
feng
59e7778e4a perf: change secret drop bulk record 2025-03-11 17:37:04 +08:00
feng626
60eaec68c6
Merge pull request #14996 from jumpserver/pr@dev@fix_long_time_no_login_risk
fix: long_time_no_login account, not listed as risk account
2025-03-11 16:40:31 +08:00
wangruidong
43973122bf fix: Remove the empty contents of the log 2025-03-11 16:39:08 +08:00
wangruidong
8668955d4a fix: long_time_no_login account, not listed as risk account 2025-03-11 16:06:47 +08:00
ZhaoJiSen
5571fb6f42
Merge pull request #14993 from jumpserver/pr@dev@report
perf: report css
2025-03-11 15:10:55 +08:00
feng
62b9b6883e perf: report css 2025-03-11 15:04:40 +08:00
老广
7af1c6a2bb
Merge pull request #14991 from jumpserver/pam
fix: OIDC PKCE S256
2025-03-11 11:02:09 +08:00
Bai
3e96c2fe79 fix: OIDC PKCE S256 2025-03-11 09:14:23 +08:00
feng626
3a1d3c1f5c
Merge pull request #14990 from jumpserver/pam
Pam
2025-03-10 19:14:00 +08:00
zhaojisen
9466c7105c Perf: Perf All Report 2025-03-10 17:39:36 +08:00
feng
a3467bdabc perf: change secret 2025-03-10 17:34:20 +08:00
feng
4c547215aa perf: account integrationapplication filter 2025-03-10 16:37:53 +08:00
ibuler
2580e7a712 perf: update accounts migrations 2025-03-10 16:20:42 +08:00
ibuler
038d93e318 Merge branch 'pam' of github.com:jumpserver/jumpserver into pam 2025-03-10 15:25:46 +08:00
ibuler
f46cc0f040 perf: update user serializer 2025-03-10 15:24:41 +08:00
feng
423d6db2ac perf: change record dashboard 2025-03-10 14:48:18 +08:00
wangruidong
243083e876 perf: Translate application detail 2025-03-10 14:26:21 +08:00
wangruidong
c84bc52c70 perf: Translate check account 2025-03-10 10:43:12 +08:00
wangruidong
090ad0ba83 fix: Add drf filter set to SecretRecordMixin 2025-03-07 18:17:13 +08:00
feng
033750f108 perf: execution automation ObjectRelatedField 2025-03-07 17:05:12 +08:00
feng
405344de74 perf: Automation 2025-03-07 16:54:20 +08:00
fit2bot
763e67bd1d
perf: Integrate authentication to update user attribute logic (#14979) 2025-03-07 16:25:28 +08:00
wangruidong
e8c581b08a fix: Integrations Application: Failed to filter accounts by selecting multiple attributes when creating or editing 2025-03-07 14:45:49 +08:00
feng
47029be3da perf: Change secret 2025-03-06 19:15:16 +08:00
wangruidong
bc70c480f7 fix: Integration Application: No Java example in the help docs 2025-03-06 17:45:48 +08:00
jiangweidong
d2d9d3d841 fix: Slove the problem that the third-party auth cannot update user name 2025-03-06 17:02:56 +08:00
ibuler
56d0a6d0b0 Merge branch 'pam' of github.com:jumpserver/jumpserver into pam 2025-03-06 10:23:26 +08:00
ibuler
060e8ace70 perf: update i18n 2025-03-06 10:22:32 +08:00
feng
821622638a perf: translate 2025-03-05 17:30:26 +08:00
老广
51c67f0e9d
Merge pull request #14968 from jumpserver/pr@dev@pam
perf: update i18n
2025-03-04 19:19:38 +08:00
ibuler
70c8db839e perf: update i18n 2025-03-04 19:13:20 +08:00
ibuler
c9208d2cf7 Merge branch 'pam' of github.com:jumpserver/jumpserver into pam 2025-03-04 15:46:52 +08:00
ibuler
58177fdfb0 perf: update some i18n 2025-03-04 15:46:45 +08:00
wangruidong
76dd2f8153 fix: Creating an integration application with the same name results in a server error. 2025-03-03 18:28:09 +08:00
wangruidong
edd998da20 fix: Premailer failed to parse the report HTML. 2025-03-03 16:46:43 +08:00
feng
8b2276ce08 perf: koko sftp superuser perm 2025-03-03 16:17:16 +08:00
wangruidong
f42f46ffe4 fix: User list: some quick filters are ineffective. 2025-03-03 14:56:32 +08:00
ibuler
b2f8f9d248 perf: update pam i18n 2025-03-03 14:46:54 +08:00
ibuler
8105681304 perf: update i18n 2025-03-03 14:44:52 +08:00
feng
b4d3f6099a perf: translate 2025-03-03 10:52:21 +08:00
feng
9b48da11dc perf: translate 2025-02-28 18:55:35 +08:00
feng
904154a62b perf: translate 2025-02-28 18:50:18 +08:00
feng
7181dad5ad perf: Translate 2025-02-28 18:40:47 +08:00
jiangweidong
2a250d13d8 fix: Solve the problem that some messages cannot be sent from unauthenticated email 2025-02-28 17:44:36 +08:00
feng
caa1e2e1ac perf: Translate 2025-02-28 17:05:36 +08:00
wangruidong
5b044299b9 fix: Back account translate 2025-02-28 14:55:42 +08:00
wangruidong
d48a12e266 perf: Translate 2025-02-28 11:04:29 +08:00
feng
35ec9dc2f4 perf: Translate 2025-02-27 18:50:06 +08:00
老广
33f3281a1f
Merge pull request #14943 from jumpserver/pr@dev@update_pam
merge: with dev
2025-02-27 17:14:59 +08:00
ibuler
93810fb0db merge: with dev 2025-02-27 17:05:35 +08:00
ibuler
8fc2b86189 Merge branch 'pam' of github.com:jumpserver/jumpserver into pam 2025-02-27 17:00:00 +08:00
ibuler
6217733aba perf: update some i18n 2025-02-27 16:59:52 +08:00
feng
4b4d7b6787 perf: During MFA authentication, if the current code has been used and successfully authenticated, it cannot be used again for authentication 2025-02-27 15:35:56 +08:00
ibuler
acbc3e1e44 Merge branch 'pam' of github.com:jumpserver/jumpserver into pam 2025-02-27 13:27:43 +08:00
ibuler
110b3a334d perf: update i18n 2025-02-27 13:27:35 +08:00
wangruidong
a498b22e80 perf: Update account change secret status and date on successful secret change 2025-02-26 16:20:39 +08:00
wangruidong
385bf47b11 perf: Add last login date update for account on session save 2025-02-26 16:20:39 +08:00
ibuler
16400082e7 perf: update i18n 2025-02-26 15:34:04 +08:00
wangruidong
cbc009cb3f fix: Return Response object for successful account risk handling 2025-02-26 10:31:33 +08:00
feng
919cdeae20 perf: report to iframe 2025-02-25 19:14:13 +08:00
feng
456b96a369 perf: Translate 2025-02-25 14:49:22 +08:00
wangruidong
ff9ad2680f perf: Update confirmation backend definitions to use ConfirmType 2025-02-25 10:53:19 +08:00
Bai
96a0cbc35d fix: import error 2025-02-25 10:53:19 +08:00
feng
08963ebb40 perf: Translate 2025-02-24 19:26:34 +08:00
wangruidong
908e6cb81a fix: Migrate account backup automation and execution data 2025-02-24 18:50:06 +08:00
feng
c206f5d09c perf: user filter error 2025-02-24 15:33:23 +08:00
github-actions[bot]
f0c7a7508b perf: Update Dockerfile with new base image tag 2025-02-24 15:09:38 +08:00
Bai
d386189c77 fix: pyproject.toml 2025-02-24 15:09:38 +08:00
ibuler
f0fa381b9f perf: update action run on 2025-02-24 14:51:53 +08:00
Bai
8b2af5ee0a fix: make messages errors 2025-02-24 14:28:31 +08:00
ibuler
0541c0a9d4 perf: squash migrations 2025-02-24 14:08:47 +08:00
feng
e4c7eb8035 perf: Account filter 2025-02-24 11:50:48 +08:00
Bai
e33aeaa338 fix: migrations files modify 2025-02-24 11:26:20 +08:00
feng
1282fffff7 perf: Change secret 2025-02-21 17:46:56 +08:00
fit2bot
3f4141ca0b
merge: with pam (#14911)
* perf: change i18n

* perf: pam

* perf: change translate

* perf: add check account

* perf: add date field

* perf: add account filter

* perf: remove some js

* perf: add account status action

* perf: update pam

* perf: 修改 discover account

* perf: update filter

* perf: update gathered account

* perf: 修改账号同步

* perf: squash migrations

* perf: update pam

* perf: change i18n

* perf: update account risk

* perf: 更新风险发现

* perf: remove css

* perf: Admin connection token

* perf: Add a switch to check connectivity after changing the password, and add a custom ssh command for push tasks

* perf: Modify account migration files

* perf: update pam

* perf: remove to check account dir

* perf: Admin connection token

* perf: update check account

* perf: 优化发送结果

* perf: update pam

* perf: update bulk update create

* perf: prepaire using thread timer for bulk_create_decorator

* perf: update bulk create decorator

* perf: 优化 playbook manager

* perf: 优化收集账号的报表

* perf: Update poetry

* perf: Update Dockerfile with new base image tag

* fix: Account migrate 0012 file

* perf: 修改备份

* perf: update pam

* fix: Expand resource_type filter to include raw type

* feat: PAM Service (#14552)

* feat: PAM Service

* perf: import package name

---------

Co-authored-by: jiangweidong <1053570670@qq.com>

* perf: Change secret dashboard (#14551)

Co-authored-by: feng <1304903146@qq.com>

* perf: update migrations

* perf: 修改支持 pam

* perf: Change secret record table dashboard

* perf: update status

* fix: Automation send report

* perf: Change secret report

* feat: windows accounts gather

* perf: update change status

* perf: Account backup

* perf: Account backup report

* perf: Account migrate

* perf: update service to application

* perf: update migrations

* perf: update logo

* feat: oracle accounts gather (#14571)

* feat: oracle accounts gather

* feat: sqlserver accounts gather

* feat: postgresql accounts gather

* feat: mysql accounts gather

---------

Co-authored-by: wangruidong <940853815@qq.com>

* feat: mongodb accounts gather

* perf: Change secret

* perf: Migrate

* perf: Merge conflicting migration files

* perf: Change secret

* perf: Automation filter org

* perf: Account push

* perf: Random secret string

* perf: Enhance SQL query and update risk handling in accounts

* perf: Ticket filter assignee_id

* perf: 修改 account remote

* perf: 修改一些 adhoc 任务

* perf: Change secret

* perf: Remove push account extra api

* perf: update status

* perf: The entire organization can view activity log

* fix: risk field check

* perf: add account details api

* perf: add demo mode

* perf: Delete gather_account

* perf: Perfect solution to account version problem

* perf: Update status action to handle multiple accounts

* perf: Add GatherAccountDetailField and update serializers

* perf: Display account history in combination with password change records

* perf: Lina translate

* fix: Update mysql_filter to handle nested user info

* perf: Admin connection token validate_permission account

* perf: copy move account

* perf: account filter risk

* perf: account risk filter

* perf: Copy move account failed message

* fix: gather account sync account to asset

* perf: Pam dashboard

* perf: Account dashboard total accounts

* perf: Pam dashboard

* perf: Change secret filter account secret_reset

* perf: 修改 risk filter

* perf: pam translate

* feat: Check for leaked duplicate passwords. (#14711)

* feat: Check for leaked duplicate passwords.

* perf: Use SQLite instead of txt as leak password database

---------

Co-authored-by: jiangweidong <1053570670@qq.com>
Co-authored-by: 老广 <ibuler@qq.com>

* perf: merge with remote

* perf: Add risk change_password_add handle

* perf: Pam dashboard

* perf: check account manager import

* perf: 重构扫描

* perf: 修改 db

* perf: Gather account manager

* perf: update change db lib

* perf: dashboard

* perf: Account gather

* perf: 修改 asset get queryset

* perf: automation report

* perf: Pam account

* perf: Pam dashboard api

* perf: risk add account

* perf: 修改 risk check

* perf: Risk account

* perf: update risk add reopen action

* perf: add pylintrc

* Revert "perf: automation report"

This reverts commit 22aee54207.

* perf: check account engine

* perf: Perf: Optimism Gather Report Style

* Perf: Remove unuser actions

* Perf: Perf push account

* perf: perf gather account

* perf: Automation report

* perf: Push account recorder

* perf: Push account record

* perf: Pam dashboard

* perf: perf

* perf: update intergration

* perf: integrations application detail add account tab page

* feat: Custom change password supports configuration of interactive items

* perf: Go and Python demo code

* perf: Custom secret change

* perf: add user filter

* perf: translate

* perf: Add demo code docs

* perf: update some i18n

* perf: update some i18n

* perf: Add Java, Node, Go, and cURL demo code

* perf: Translate

* perf: Change secret translate

* perf: Translate

* perf: update some i18n

* perf: translate

* perf: Ansible playbook

* perf: update some choice

* perf: update some choice

* perf: update account serializer remote unused code

* perf: conflict

* perf: update import

---------

Co-authored-by: ibuler <ibuler@qq.com>
Co-authored-by: feng <1304903146@qq.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: wangruidong <940853815@qq.com>
Co-authored-by: jiangweidong <1053570670@qq.com>
Co-authored-by: feng626 <57284900+feng626@users.noreply.github.com>
Co-authored-by: zhaojisen <1301338853@qq.com>
2025-02-21 16:39:57 +08:00
wangruidong
d516349a68 perf: Translate 2025-02-20 18:24:27 +08:00
Chenyang Shen
8730fa8dee
Merge pull request #14900 from jumpserver/pr@dev@translate
perf: translate
2025-02-19 19:26:09 +08:00
Aaron3S
9a5a775652 feat: add luna i18n 2025-02-19 19:17:21 +08:00
feng
8304ae9070 perf: translate 2025-02-19 16:27:10 +08:00
wangruidong
9533861e24 fix: VariableSerializer allow null in default_value 2025-02-18 15:26:11 +08:00
Aaron3S
abbfbcde83 feat: add i18n 2025-02-18 15:10:13 +08:00
wangruidong
046a9d41bf fix: Removing labels from asset details will cause an error. 2025-02-17 18:21:28 +08:00
fit2bot
363bb20da7
feat: Chen i18n (#14851)
Co-authored-by: jiangweidong <1053570670@qq.com>
2025-02-17 11:25:54 +08:00
wangruidong
2b7c8b9c07 fix: Upgrading v3 to v4 generates two ticket flow 2025-02-14 16:10:20 +08:00
Aaron3S
db040bbd06 feat: add translate 2025-02-14 11:30:56 +08:00
feng
a761ec9aa1 perf: Translate 2025-02-13 16:55:09 +08:00
feng
c0ffe45ce9 perf: deepseek 2025-02-13 16:40:29 +08:00
wangruidong
404d58a9c9 fix: When the organization does not exist, close ticket with an error. 2025-02-13 16:31:47 +08:00
feng
f64eab7a15 perf: Translate 2025-02-13 11:48:59 +08:00
feng
46f94fd138 perf: Chat ai help text 2025-02-13 11:30:07 +08:00
wangruidong
2f1c0090b7 fix: markdown render issue 2025-02-12 15:48:05 +08:00
feng
b0d6a09276 perf: translate 2025-02-10 19:15:39 +08:00
feng
d8db76cc7b perf: DeepSeek 2025-02-08 15:40:24 +08:00
wangruidong
b35a55ed54 fix: Cannot set original org when exception occurs 2025-02-08 11:13:14 +08:00
Bai
dc5ecfcc4b fix: setting field encrypt issue 2025-02-06 17:14:10 +08:00
Eric
594d5b8128 perf: use a custom local path to save session replay file 2025-01-23 15:55:43 +08:00
wangruidong
543dde57ab perf: modify average_time_cost calculation in job model 2025-01-14 18:28:31 +08:00
w940853815
c088437fe5 Revert "perf: Optimize average_time_cost calculation in job model"
This reverts commit eafb5ecfb3.
2025-01-14 18:18:44 +08:00
feng
e721ec147c perf: luna translate 2025-01-14 17:30:00 +08:00
wangruidong
5d18d6dee0 fix: Add with_expired param to permission utils 2025-01-14 16:41:39 +08:00
feng
ecfd338428 perf: Lina translate 2025-01-14 15:16:17 +08:00
Eric
4b28b079dc perf: fix rdp file resolution value 2025-01-14 13:57:11 +08:00
wangruidong
c1c3236a30 fix: Add redirect_url check in base view 2025-01-14 11:54:17 +08:00
feng
4b19750581 perf: Client version 2025-01-14 10:32:07 +08:00
wangruidong
eafb5ecfb3 perf: Optimize average_time_cost calculation in job model 2025-01-13 17:12:11 +08:00
Bai
583486e26e fix: radius user auth skip backend 2025-01-13 15:49:23 +08:00
Bai
8198620a2e feat: add gitignore 2025-01-09 10:50:57 +08:00
wangruidong
c0b301d52b fix: ldap ha periodic task did not execute as expected 2025-01-08 18:34:53 +08:00
feng
7791d6222a perf: translate 2025-01-08 14:33:04 +08:00
Bai
b740d9d42f fix: circle imported for perms-api 2025-01-08 10:35:13 +08:00
wangruidong
48d0187604 fix: circular import 2025-01-07 14:01:38 +08:00
wangruidong
6217018427 perf: Translate var_name help_text 2025-01-06 11:36:11 +08:00
jiangweidong
923f40e523 feat: VMware automatically syncs folders to node-translation 2025-01-03 18:50:12 +08:00
Bai
1f1fe2084b fix: koko press r dont refresh user perm-nodes 2025-01-03 17:13:32 +08:00
刘瑞斌
b8b1a6ac9c chore: update readme 2024-12-30 16:04:32 +08:00
wangruidong
35f88722af fix: Add type check for secure command execution 2024-12-24 15:58:56 +08:00
Bai
7e6d2749ae fix: core download page error 2024-12-20 16:35:24 +08:00
Bai
be57b101ff fix: set default ldap user dn cache time (0) 2024-12-20 16:35:03 +08:00
Bai
41c8cb6307 fix: api prometheus count 2024-12-20 11:14:55 +08:00
Bai
3a7ae01ede fix: add settings for license version and facelive 2024-12-19 17:37:39 +08:00
老广
d17ca4f6a7 Revert "perf: update const import"
This reverts commit 2956f2e4b7.
2024-12-19 16:03:29 +08:00
Aaron3S
5a14bb13d0 feat: remove mfa check when unbind face code 2024-12-19 15:50:38 +08:00
ibuler
2956f2e4b7 perf: update const import 2024-12-19 15:49:13 +08:00
feng
e983ac3cbc perf: Translate 2024-12-19 15:18:39 +08:00
ibuler
fab156dc5f perf: update login success redirect 2024-12-19 14:34:49 +08:00
wangruidong
f6f897317e perf: default_value field allow blank 2024-12-19 12:01:08 +08:00
Aaron3S
a0441cd6ea feat: add translate 2024-12-19 11:13:13 +08:00
Chenyang Shen
e9abd1e72d
Merge pull request #14688 from jumpserver/pr@dev@fix_face_openid
fix: fix openid user can't login with face verify
2024-12-19 11:08:45 +08:00
Aaron3S
9fcb4ecba0 fix: fix openid user can't login with face verify 2024-12-19 10:56:44 +08:00
feng
4b637ad86e perf: Client version 3.0.0 2024-12-18 19:33:45 +08:00
jiangweidong
829f867962 perf: The command amount does not record operation logs. 2024-12-18 19:33:02 +08:00
feng
7f965b55f4 perf: Translate 2024-12-18 18:21:43 +08:00
Chenyang Shen
0e0be618e5
Merge pull request #14684 from jumpserver/pr@dev@feat_refresh_cache_facelive
feat: refresh facelive cache
2024-12-18 18:10:39 +08:00
Aaron3S
9577af3221 feat: refresh facelive cache 2024-12-18 18:08:13 +08:00
Aaron3S
a6b7cc9d1b fix: fix 401 error on face verify when use openid login 2024-12-18 18:07:42 +08:00
feng
7a9a71197a perf: Client login 2024-12-18 18:01:38 +08:00
jiangweidong
3cd68ba0a9 perf: push account without increasing version. 2024-12-18 16:51:39 +08:00
jiangweidong
02bdd0f07d perf: push account without increasing version. 2024-12-18 16:51:39 +08:00
jiangweidong
98cf6f82b7 perf: create account add activity log 2024-12-18 15:54:57 +08:00
wangruidong
27fd5d51b9 perf: Translate 2024-12-18 15:53:47 +08:00
wangruidong
095ca91e30 feat: add 'labels' to DomainSerializer fields_m2m 2024-12-18 10:57:30 +08:00
wangruidong
d05514962a fix: calc platform asset count 2024-12-17 19:08:55 +08:00
Bai
c4066a03fa fix: login show system org 2024-12-17 19:08:31 +08:00
Aaron3S
a7d4c4ca2a feat: change face online killer name 2024-12-17 18:53:09 +08:00
Chenyang Shen
5b0f8f63a3
Merge pull request #14670 from jumpserver/pr@dev@feat_add_some_translate
feat: add some translate
2024-12-17 18:24:01 +08:00
Aaron3S
c4bcae68bf feat: add some translate 2024-12-17 18:03:13 +08:00
Aaron3S
29ca50f97e feat: add face online acl check for exchange token 2024-12-17 17:18:37 +08:00
feng
49aaf8d53e perf: Remove the login status after the client logs in 2024-12-17 15:32:39 +08:00
feng
931e15173b perf: perm asset api date_updated order 2024-12-17 14:43:19 +08:00
feng
4018a59b2e perf: Account backup filter org 2024-12-17 11:23:32 +08:00
Chenyang Shen
88905bd28d
Merge pull request #14664 from jumpserver/pr@dev@feat_add_face_verify_on_exchange_token
feat: add face verify on exchange connect token
2024-12-16 19:00:56 +08:00
Aaron3S
abad98a190 feat: add face verify on exchange connect token 2024-12-16 18:54:32 +08:00
Chenyang Shen
7419139b29
Merge pull request #14663 from jumpserver/pr@dev@feat_exclude_some_action_for_acl
feat: exclude face action for login acl and command acl
2024-12-16 18:22:09 +08:00
Aaron3S
a1fd3b1ecb feat: exclude face action for login acl and command acl 2024-12-16 18:17:00 +08:00
wangruidong
8a8a7f9947 fix: filter custom assets in secret type check 2024-12-16 17:37:05 +08:00
Chenyang Shen
f9e6fc98fb
Merge pull request #14654 from jumpserver/pr@dev@feat_update_migrations
feat: update migrations
2024-12-13 12:42:17 +08:00
Aaron3S
0dd015bcba feat: update migrations 2024-12-13 12:40:06 +08:00
Aaron3S
d1ea31c9a4 feat: face online 2024-12-12 18:31:21 +08:00
feng
e2bf56e624 perf: translate 2024-12-12 15:49:37 +08:00
feng
26040a5560 perf: pt_br translate 2024-12-12 14:40:59 +08:00
Bai
54726f0a2d perf: Passkey Model field token max_length 1024 2024-12-12 14:29:23 +08:00
Eric
7fd88b95f9 perf: update lion i18n 2024-12-12 11:21:19 +08:00
feng
4f271d6405 perf: RBAC remove assets gpt custom 2024-12-11 19:05:33 +08:00
feng
fe17a8c3a0 perf: The entire organization can view activity log 2024-12-11 18:45:41 +08:00
fit2bot
ee5e97e860
perf: add rdp connection speed option (#14641)
* perf: add rdp connection speed option

* perf: remove print code

---------

Co-authored-by: Eric <xplzv@126.com>
2024-12-11 18:42:05 +08:00
fit2bot
dddfc66efd
perf: add encrypted configuration API (#14632)
* perf: 添加加密配置API

* perf: modify url

---------

Co-authored-by: Eric <xplzv@126.com>
2024-12-11 11:34:09 +08:00
Bai
d005bd804f fix: user orgs add field: is_system 2024-12-10 19:19:06 +08:00
Bai
08de04fdbc fix: fixed an issue when third-part user auth 2024-12-10 16:41:38 +08:00
Bai
9ed7c41514 fix: fixed an issue when third-part user auth 2024-12-10 16:41:38 +08:00
Eric
1a81b76a46 perf: add new Qwerty for keyboard layout 2024-12-10 15:26:18 +08:00
github-actions[bot]
cf99a7a031 perf: Update Dockerfile with new base image tag 2024-12-10 15:25:37 +08:00
Bai
64551b13a1 feat: deps add ipython==8.30.0 2024-12-10 15:25:37 +08:00
Chenyang Shen
c715300416
Merge pull request #14623 from jumpserver/pr@dev@separate_face_module
feat: Separate the face recognition module.
2024-12-09 17:00:34 +08:00
feng
d9031ae02b perf: Ticket filter assignee_id 2024-12-09 16:58:17 +08:00
Aaron3S
0d2ba5c518 feat: Separate the face recognition module. 2024-12-09 16:57:05 +08:00
Bai
817957dbac fix: fixed an issue where auth backend could pass inspect 2024-12-09 15:38:20 +08:00
feng
3796af78a6 perf: Random secret string 2024-12-09 15:25:31 +08:00
github-actions[bot]
1191e4ab2d perf: Update Dockerfile with new base image tag 2024-12-09 14:21:01 +08:00
吴小白
1c6fcc5826 feat: migrating boto to boto3 2024-12-09 14:21:01 +08:00
Chenyang Shen
4728f95634
Merge pull request #14610 from jumpserver/pr@dev@feat_face_login_acl
feat: login asset face verify acl
2024-12-09 11:34:09 +08:00
Aaron3S
013502186b feat: login asset face verify acl 2024-12-09 11:19:04 +08:00
feng
a6d040cd34 perf: Automation filter org 2024-12-06 18:00:56 +08:00
Bai
398758baa6 fix: when oidc enabled and use_state user login raise 400 2024-12-06 16:26:28 +08:00
吴小白
e29bddd89e feat: bump python from 3.11.10 to 3.11.11 2024-12-06 10:29:41 +08:00
Bai
e35c915ee3 perf: add workflows auto release docs 2024-12-06 10:20:24 +08:00
Bryan
de2dd583d0 Update README.zh-hant.md 2024-12-05 14:49:42 +08:00
Bryan
43f1d7eeae Update README.pt-br.md 2024-12-05 14:49:42 +08:00
Bryan
9bb63e0933 Update README.zh-hans.md 2024-12-05 14:49:42 +08:00
Bryan
c9e03fd5d8 Update README.ja.md 2024-12-05 14:49:42 +08:00
github-actions[bot]
7a147242c9 Auto-translate README 2024-12-05 14:49:42 +08:00
github-actions[bot]
392c261a96 Auto-translate README 2024-12-05 14:49:42 +08:00
Bai
2bbccae0f5 perf: readme 2024-12-05 14:39:23 +08:00
Bai
606fa9bfbc feat: change action 2024-12-05 14:24:38 +08:00
fit2bot
96e7b165dd
Auto-translate README (#14584)
* Auto-translate README

* Auto-translate README

* Auto-translate README

* Auto-translate README

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-12-05 14:17:27 +08:00
Bai
148413d280 feat: add auto translate readme 2024-12-05 13:34:04 +08:00
Bai
a46a81d477 feat: add auto translate readme 2024-12-05 13:34:04 +08:00
feng
ff0f9eb6eb perf: Change secret update version 2024-12-05 10:50:07 +08:00
jiangweidong
d8dfaf0868 fix: Solve the problem of version increase caused by push account 2024-12-04 18:50:30 +08:00
Bai
3267c8074b feat: add actions for translate readme 2024-12-04 17:45:21 +08:00
Bai
7b14d680b2 feat: add actions for translate readme 2024-12-04 17:24:12 +08:00
Bai
0980808bb7 fix: compile languages error 2024-12-03 18:14:42 +08:00
Bai
0519f15bbf fix: compile languages error 2024-12-03 18:07:55 +08:00
Eric
f6742eb4c6 perf: add dbeaver-patch version 2024-12-03 17:36:59 +08:00
fit2bot
f8d11013fc
feat: support pt-br language (#14567)
Co-authored-by: Bai <baijiangjie@gmail.com>
2024-12-03 17:11:08 +08:00
jiangweidong
7875777ed1 fix: Resolving Azure test connection failure issues 2024-12-03 14:34:48 +08:00
jiangweidong
0ca81a8f30 fix: To resolve the 500 error during local updates after an account is deleted from Vault 2024-12-03 14:34:07 +08:00
Bryan
09accbd922
perf: update issue template sorted (#14563)
* perf: update issue template sorted

* perf: update issue template sorted

* Rename 1_bug_report_cn.yml to 2_bug_report_cn.yml

* perf: update issue template sorted

* Rename 1_question.yml to 2_question.yml

* Update and rename 2_feature_request.yml to 3_feature_request.yml

* Rename 2_bug_report_cn.yml to 4_bug_report_cn.yml

* Rename 3_question_cn.yml to 5_question_cn.yml

* Update and rename 2_feature_request_cn.yml to 6_feature_request_cn.yml
2024-12-03 11:21:29 +08:00
fit2bot
945204c45b
perf: Script to Add a Non-existent release_assets Field (#14558)
* perf: Script to Add a Non-existent release_assets Field

* perf: docstring

---------

Co-authored-by: jiangweidong <1053570670@qq.com>
2024-12-02 16:51:52 +08:00
Bai
2d62dc0657 fix: azure vault max_workers 2024-12-02 11:08:59 +08:00
fit2bot
fa61688c28
feat: Vault adds Amazon Secrets Manager (#14515)
* feat: Vault adds Amazon Secrets Manager

* perf: optimizing the code

---------

Co-authored-by: jiangweidong <1053570670@qq.com>
2024-11-29 17:51:28 +08:00
halo
801edc7cc9 perf: After optimizing the execution of the azure vault task, the data is out of sync 2024-11-29 16:27:35 +08:00
Bai
d0617a0ea4 fix: login log get ipv6 error 2024-11-29 14:59:01 +08:00
Chenyang Shen
1191ed1793
Merge pull request #14546 from jumpserver/pr@dev@feat_move_face_to_profile
feat: move face setiing to profile
2024-11-28 18:19:32 +08:00
Aaron3S
4036420d0e feat: move face setiing to profile 2024-11-28 18:06:57 +08:00
jiangweidong
35a1655905 perf: Oauth2.0 support two methods for passing authentication credentials. 2024-11-26 14:12:56 +08:00
feng
d4dc31aefa perf: Modify default_context COPYRIGHT 2024-11-25 15:30:26 +08:00
wangruidong
04ec34364f perf: Add viewAssetOnlineSessionInfo conf 2024-11-25 15:28:57 +08:00
Aaron3S
01b8c1f7a8 fix: Fix the uncaught exception when face capture fails 2024-11-25 10:17:28 +08:00
Bai
77598a0f23 perf: update readme 2024-11-22 16:43:44 +08:00
wangruidong
eafb074fda refactor: API endpoint 2024-11-22 15:14:26 +08:00
Bryan
d4d903f5c6
perf: Update README.md (#14516)
* perf: Update README.md

* perf: Update README.md
2024-11-22 10:38:32 +08:00
吴小白
c9c55b5fcb fix: add libldap2-dev 2024-11-21 20:53:11 +08:00
wangruidong
f7313bfcc1 perf: Audits job api disable periodic task 2024-11-21 18:56:16 +08:00
Bai
d2f7376f78 fix: job execution stop failed 2024-11-21 18:38:10 +08:00
wangruidong
6db56eb2aa fix: view ops job celery log no perms 2024-11-21 18:14:45 +08:00
fit2bot
442290703a
fix: pyfreerdp verify account, the default value of gateway_args field is wrong (#14490)
* fix: pyfreerdp verify account, the default value of gateway_args field is wrong

* fix: pyfreerdp verify account, the default value of gateway_args field is wrong

---------

Co-authored-by: Ewall555 <a03216@foxmail.com>
2024-11-21 14:26:22 +08:00
feng
e491a724ed perf: Video player download 2024-11-21 14:25:50 +08:00
feng
230924baac fix: Vault proxy 2024-11-21 13:40:33 +08:00
wangruidong
0ae2f04f28 fix: view ops job celery log no perms 2024-11-21 13:24:29 +08:00
feng
68a490d305 perf: Hide azure vault 2024-11-21 13:02:40 +08:00
wangruidong
6abfeee683 feat: Add periodic display and validate job params 2024-11-20 22:07:56 +08:00
Aaron3S
1a03f7b265 feat: add license edition check 2024-11-20 20:09:11 +08:00
feng
2dae2b3789 perf: Translate 2024-11-20 18:21:31 +08:00
Aaron3S
bdbbebab76 feat: perf face capture page 2024-11-20 17:54:27 +08:00
Chenyang Shen
33170887f4
Merge pull request #14495 from jumpserver/pr@dev@feat_add_check_api_white_list
feat: add 'face_context' to check_api white list
2024-11-20 17:52:06 +08:00
Aaron3S
88302c8846 feat: add 'face_context' to check_api white list 2024-11-20 16:38:22 +08:00
feng
4068b5c76a perf: Change secret ssh_key_change_strategy modify the default value 2024-11-20 16:27:21 +08:00
feng
9966ad4c71 perf: Dynamic update vault 2024-11-20 15:58:20 +08:00
Aaron3S
9cfe974c52 feat: 添加 mfa middleware 白名单 2024-11-20 14:18:52 +08:00
feng
d9a9f890f5 perf: Lina AzureKeyVault translate 2024-11-20 14:08:27 +08:00
fit2bot
e2904ab042
perf: Custom SMS (files) support obtaining more user information. (#14486)
* perf: Custom SMS (files) support obtaining more user information.

* perf: Remove the useless modules

* perf: modify

---------

Co-authored-by: jiangweidong <1053570670@qq.com>
2024-11-20 10:29:14 +08:00
Aaron3S
f92c557235 feat: 增加人脸识别超时控制 2024-11-20 10:27:04 +08:00
halo
cfadbc164c perf: If the cloud vault initialization fails, the task will not be executed. 2024-11-20 10:15:14 +08:00
feng
374a102bc4 perf: Translate 2024-11-19 18:58:43 +08:00
feng
84e1411c22 fix: Clone endpoint 500 2024-11-19 18:09:00 +08:00
wangruidong
e28bf170d1 perf: MFA Translate 2024-11-19 17:55:11 +08:00
wangruidong
7c9e3a1362 perf: Optimize summary calculation 2024-11-19 17:55:11 +08:00
feng
fba80342a5 perf: Translate 2024-11-19 17:54:45 +08:00
Aaron3S
5eeff0aabf feat: 设置人脸上下文存活时间 2024-11-19 17:34:44 +08:00
Aaron3S
5b4de02fff feat: 增加绑定成功失败提示 2024-11-19 17:30:31 +08:00
wangruidong
b6a5854fa2 perf: Optimize summary calculation 2024-11-19 16:13:38 +08:00
Chenyang Shen
9771d3c817
Merge pull request #14476 from jumpserver/pr@dev@feat_add_face_i18n
FEAT: Add face recognition translation
2024-11-19 15:11:03 +08:00
Aaron3S
b33a0cf0b1 feat: 添加人脸识别翻译 2024-11-19 15:08:39 +08:00
Chenyang Shen
f9fa6ad9c1
Merge pull request #14474 from jumpserver/pr@dev@feat_update_face_capture_page
feat: Optimized the face collection page
2024-11-19 15:01:45 +08:00
Aaron3S
4b2db2b6a1 feat: 优化人脸采集页面 2024-11-19 14:28:31 +08:00
Halo
822b353a40
perf: Translate (#14468)
* feat: azure key vault

* perf: add azure-keyvault-secrets

* perf:azure kv api

* perf: Translate

* perf: Update Dockerfile with new base image tag

* perf: Error when secret is empty

* perf: Translate

* perf: Update Dockerfile with new base image tag

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-11-18 18:48:33 +08:00
feng
2908d4ee5f perf: Delete asset fail 2024-11-18 10:08:37 +08:00
wangruidong
482c4ced0c perf: Translate 2024-11-15 18:00:35 +08:00
halo
b2a5e457a9 fix: vault synchronization task exception 2024-11-15 17:46:17 +08:00
wangruidong
343c3607fa fix: modify job audit rbac 2024-11-15 15:47:20 +08:00
wangruidong
f03263eedf fix: Radius login failed 2024-11-15 15:44:05 +08:00
Aaron3S
98d7ecbf3e fix: 修改错误的url地址 2024-11-13 17:35:43 +08:00
halo
477ccda8ca perf: VAULT_BACKEND cannot be modified from the frontend 2024-11-13 17:31:47 +08:00
wangruidong
fcdc2b9510 fix: Solve audit job and variable bugs 2024-11-13 17:31:17 +08:00
wangruidong
1ee57cfda0 perf: ticket info add org name 2024-11-12 18:15:31 +08:00
wangruidong
804bd289a4 fix: Other people can delete adhoc or playbook 2024-11-12 17:44:21 +08:00
Aaron3S
86273865c8 feat: 增加人脸识别功能 2024-11-12 17:41:39 +08:00
Eric
5142f0340c perf: add license info for component config 2024-11-12 16:52:45 +08:00
Bai
7c80c52d02 fix: Set the default language to en 2024-11-12 15:43:57 +08:00
Bai
eb30b61ca9 fix: Set the default language to en 2024-11-12 15:38:01 +08:00
wangruidong
dd5a272cdf perf: Add task handler for ops job with creator assignment 2024-11-12 15:16:01 +08:00
wangruidong
5b27acf4ef perf: Admin and auditor can view and stop task 2024-11-12 11:25:12 +08:00
Eric
1a41a7450e perf: vnc proxy port to 15900 2024-11-11 19:46:24 +08:00
fit2bot
e1b501c7d4
feat: azure key vault (#14406)
* feat: azure key vault

* perf: add azure-keyvault-secrets

* perf:azure kv api

* perf: Translate

* perf: Update Dockerfile with new base image tag

* perf: Error when secret is empty

* perf: Translate

---------

Co-authored-by: halo <wuyihuangw@gmail.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-11-11 19:41:47 +08:00
Eric
b660bfb7ff perf: add nec vnc component and endpoint migrations 2024-11-11 18:54:52 +08:00
wangruidong
5724912480 perf: Add check for SECURITY_COMMAND_EXECUTION settings in ops tasks 2024-11-11 18:14:43 +08:00
feng
11b3bafd5a perf: Translate 2024-11-08 15:34:37 +08:00
wangruidong
9f90838df1 perf: Optimize username handling in push_account 2024-11-07 10:47:01 +08:00
wangruidong
b01916001e fix: User import fails if MFA field is set to Disabled (0) 2024-11-07 10:45:05 +08:00
fit2bot
c96ae1022b
feat: Supports running adhoc,playbook with variable (#14417)
* perf:Create a job that supports adding node parameters

* feat: add variable model

* feat: Modify Variable and AdHoc models,

* feat: Parameters can be set when running job

* feat: Supports setting  variable type

* feat: Supports running adhoc with parameters

* feat: Supports running playbook with parameters

* fix: Translate

* feat: Support setting variables for scheduled tasks

* perf: Translate

---------

Co-authored-by: wangruidong <940853815@qq.com>
2024-11-07 10:38:34 +08:00
jiangweidong
8f11167db0 perf: i18n - Supports automatic release of assets and prevents accidental release of network errors 2024-11-06 15:07:26 +08:00
老广
a53397b76f
Update llm-code-review.yml 2024-11-05 18:20:38 +08:00
老广
8f13224454
Create llm-code-review.yml 2024-11-04 18:34:06 +08:00
Bai
8f4dd25e69 feat: DEFAULT_EXPIRED_YEARS put in public settings API 2024-11-01 18:24:54 +08:00
Bai
9c8762e3a0 feat: support configuration DEFAULT_EXPIRED_YEARS 2024-11-01 15:48:57 +08:00
Bai
a8cf788122 feat: add GitHub Action to automatically publish release notes to Discord changelog channel. 2024-11-01 15:24:07 +08:00
Bai
7355a4f152 feat: add GitHub Action to automatically publish release notes to Discord changelog channel. 2024-11-01 14:21:48 +08:00
ibuler
2cf80e6615 perf: login success to call client 2024-10-31 18:36:42 +08:00
ibuler
9a18ed631c fix: oracle platform create error 2024-10-30 16:33:18 +08:00
Bai
1e16f1cb9f fix: console dashboard proportion describe 2024-10-29 19:09:50 +08:00
fit2bot
35b8b080ab
perf: add to cron.d (#14375)
* perf: add to cron.d

* perf: Update Dockerfile with new base image tag

---------

Co-authored-by: ibuler <ibuler@qq.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-10-29 16:15:07 +08:00
ibuler
4219d54db3 perf: add cron in dockerfile 2024-10-29 15:39:54 +08:00
ibuler
c3620254b3 perf: change docker file 2024-10-29 15:32:57 +08:00
fit2bot
d30de0b6a0
perf: update chrome applets hang (#14353)
* perf: update chrome applets hang

* perf: remove debug print

---------

Co-authored-by: Eric <xplzv@126.com>
2024-10-29 15:19:15 +08:00
github-actions[bot]
af91b6faeb perf: Update Dockerfile with new base image tag 2024-10-29 15:18:24 +08:00
ibuler
49b84b019d perf: using poetry mirror 2024-10-29 15:18:24 +08:00
ibuler
a0ee520572 perf: remove cache 2024-10-29 15:18:24 +08:00
fit2bot
972afe0bfe
perf: revert old deps (#14371)
* perf: revert old deps

* perf: update poetry

* perf: Update Dockerfile with new base image tag

---------

Co-authored-by: ibuler <ibuler@qq.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-10-29 14:24:31 +08:00
wangruidong
e47e9b0a11 fix: Unique basename 2024-10-29 11:38:46 +08:00
fit2bot
87e54d8823
perf: add cron (#14364)
* perf: add cron

* Update Dockerfile-base

* perf: Update Dockerfile with new base image tag

---------

Co-authored-by: ibuler <ibuler@qq.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-10-29 10:56:42 +08:00
jiangweidong
a73c8d8285 fix: Wechat ticket message some url cannot clicked 2024-10-25 15:05:13 +08:00
Eric
b0dd8d044d perf: add error msg when applet task failed 2024-10-24 14:55:33 +08:00
jiangweidong
7c55c42582 perf: Links in WeCom messages can be opened without re-logging in. 2024-10-22 17:02:59 +08:00
fit2bot
cc1fcd2b98
perf: move storage sdk to core (#14318)
* perf: move storage sdk to core

* perf: Update Dockerfile with new base image tag

---------

Co-authored-by: ibuler <ibuler@qq.com>
Co-authored-by: Bai <baijiangjie@gmail.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-10-22 15:20:10 +08:00
fit2bot
8434d8d5ba
perf: update dependency (#14307)
* perf: update dependency

* perf: remove source build

* perf: Update Dockerfile with new base image tag

* perf: use cache build

* perf: Update Dockerfile with new base image tag

* fix: variable incorrectly defined

* perf: Update Dockerfile with new base image tag

* fix: openpyxl fixed version

* perf: Update Dockerfile with new base image tag

* perf: remove cache

* perf: Update Dockerfile with new base image tag

* perf: update pyproject.toml

* perf: Update Dockerfile with new base image tag

* perf: remove cache

* perf: Update Dockerfile with new base image tag

---------

Co-authored-by: 吴小白 <296015668@qq.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-10-22 14:16:19 +08:00
feng
044fd238b8 perf: Remove ssh_key_change_strategy add value 2024-10-21 15:25:38 +08:00
feng
be096a1319 perf: List preference translate 2024-10-18 14:36:13 +08:00
吴小白
6fa14833b3 perf: use python embed 2024-10-18 11:02:49 +08:00
feng
1f32ab274c fix: Error subpub_msg log 2024-10-17 15:17:32 +08:00
feng
b0f86e43a6 perf: Translate 2024-10-17 12:05:25 +08:00
ibuler
9b0c81333f perf: debug pub sub 2024-10-17 10:16:44 +08:00
Eric
05fc966444 perf: add koko i18n 2024-10-16 18:25:42 +08:00
Eric
b87650038f perf: update code 2024-10-16 18:11:00 +08:00
wangruidong
d4f69a7ff8 perf: Translate 2024-10-16 17:59:18 +08:00
ibuler
0e1e26c29c perf: disable f1 key 2024-10-16 17:01:10 +08:00
Huaqing Chen
1b8cdbc4dd 修复websocket不能使用Authorization Header的问题 2024-10-15 14:13:38 +08:00
feng
2a781c228f perf: Cas user cannot bind organization 2024-10-15 10:50:20 +08:00
ZhaoJiSen
35d6b0f16a
Merge pull request #14299 from jumpserver/pr@dev@change_password_length
perf: Change secret remove redundant checks
2024-10-14 16:45:27 +08:00
feng
ca8987fef6 perf: Change secret remove redundant checks 2024-10-14 16:39:31 +08:00
ZhaoJiSen
b385133071
Merge pull request #14297 from jumpserver/pr@dev@translate
perf: Translate
2024-10-14 16:09:21 +08:00
feng
aa78a03efa perf: Translate 2024-10-14 16:05:38 +08:00
wangruidong
31f8a19392 perf: Translate account history 2024-10-14 15:31:17 +08:00
wangruidong
7a528b499a perf: import data validate platform 2024-10-14 14:05:24 +08:00
Eric
1c6ce422cf perf: update tinker v0.1.9 2024-10-12 16:30:28 +08:00
Eric
f9cf2ea2e5 perf: fix api error when deleting offline panda components 2024-10-12 16:15:23 +08:00
Aaron3S
575b3a617f feat: 添加 chen 翻译 2024-10-12 15:44:38 +08:00
wangruidong
b7362d3f51 fix: adhoc execute alert msg 2024-10-12 15:43:03 +08:00
ZhaoJiSen
6ee3860124
Merge pull request #14287 from jumpserver/pr@dev@translate
perf: Translate
2024-10-12 14:40:23 +08:00
feng
7e111da529 perf: Translate 2024-10-12 14:35:18 +08:00
wangruidong
578458f734 perf: site msg content optimize 2024-10-11 11:28:56 +08:00
Bai
bd56697d6d perf: DEFAULT_PAGE_SIZE same as MAX_LIMIT_PER_PAGE 2024-10-10 18:00:01 +08:00
wangruidong
aad824d127 perf: add created_by field 2024-10-09 16:14:22 +08:00
wangruidong
63f828da0b perf: Default endpoint cannot be disabled 2024-10-09 16:12:37 +08:00
wangruidong
7c211b3fb6 perf: Translate 2024-10-08 15:01:53 +08:00
feng
3881edd2ba perf: Optimize file audit download prompt 2024-09-29 16:12:49 +08:00
feng
b882b12d04 perf: Check the validity of the connection token 2024-09-27 17:10:08 +08:00
wangruidong
addd2e7d1c perf: Endpoint add is_active field 2024-09-27 16:00:05 +08:00
Bai
ad6d2e1cd7 fix: Fixed the issue that the workbench user login log only displays failed logs 2024-09-27 14:34:23 +08:00
github-actions[bot]
5f07271afa perf: Update Dockerfile with new base image tag 2024-09-27 14:30:48 +08:00
Bai
efdcd4c708 perf: upgrade geoip2 and .mmdb 2024-09-27 14:30:48 +08:00
jiangweidong
b62763bca3 perf: Cloud Sync IP Policy Updated to Preferred Option i18n 2024-09-27 14:29:09 +08:00
wangruidong
e95da730f2 perf: Koko can display assets custom name 2024-09-27 14:25:55 +08:00
fit2bot
43fa3f420a
fix: Addressing the issue of unauthorized execution of system tools (#14209)
* fix: Addressing the issue of unauthorized execution of system tools

* perf: Optimization conditions

---------

Co-authored-by: jiangweidong <1053570670@qq.com>
2024-09-27 14:17:16 +08:00
wangruidong
0311446384 perf: playbook clone with file 2024-09-27 14:13:35 +08:00
feng
f7030e4fee perf: Login encryption key cache added 2024-09-26 15:11:35 +08:00
ZhaoJiSen
fce8cc375f
Merge pull request #14230 from jumpserver/pr@dev@max_password_length
perf: The maximum length of the randomly generated password is changed to 36
2024-09-25 11:00:45 +08:00
feng
920199c6df perf: The maximum length of the randomly generated password is changed to 36 2024-09-25 10:52:16 +08:00
feng
d09eb3c4fa perf: Lock username is not case sensitive 2024-09-23 14:11:55 +08:00
ibuler
6e8affcdd6 perf: ops db migrate 2024-09-19 21:39:55 +08:00
wangruidong
647736f4e3 fix: SAML2 500 error caused by duplicate email or username 2024-09-19 17:49:53 +08:00
ZhaoJiSen
cbc09d84df
Merge pull request #14202 from jumpserver/pr@dev@password_rule
perf: Password rule import csv help_text
2024-09-19 16:54:52 +08:00
feng
4c957dd03b perf: Password rule import csv help_text 2024-09-19 16:51:43 +08:00
wangruidong
d34b65890f fix: import account failed 2024-09-19 15:12:05 +08:00
Bai
b53968ac00 delete: ansible log in logging.py 2024-09-19 15:11:31 +08:00
ZhaoJiSen
f2ccb15101
Merge pull request #14198 from jumpserver/pr@dev@saml
perf: Bind user group support str
2024-09-19 12:01:20 +08:00
feng
db5bf046fc perf: Bind user group support str 2024-09-19 11:58:45 +08:00
ibuler
59c87483e6 perf: filter gateway with new params 2024-09-19 11:33:02 +08:00
github-actions[bot]
26420b78f8 perf: Update Dockerfile with new base image tag 2024-09-19 11:18:04 +08:00
wangruidong
e47bdc093e perf: trigger core base image build 2024-09-19 11:18:04 +08:00
wangruidong
3dde80a60a fix: Password reset is only required for AUTH_BACKEND_MODEL 2024-09-19 11:08:11 +08:00
feng
e373a79d63 perf: Gateway type asset filter 2024-09-19 10:45:59 +08:00
wangruidong
744a5cd0e3 perf: Modify relative file path 2024-09-19 10:41:12 +08:00
wangruidong
37ca4a46ee perf: add clean_site_packages.sh file path to build-base-image.yml 2024-09-19 10:25:08 +08:00
wangruidong
0dc9214f98 fix: LDAP HA the login log did not record the authentication backend 2024-09-18 18:53:03 +08:00
wangruidong
513508654b fix: minio test failed 2024-09-18 18:51:25 +08:00
feng
ef2b12fa0f perf: Export template with prompts 2024-09-18 18:26:38 +08:00
feng
4e719ecacd perf: TimerExecution translate 2024-09-18 15:02:17 +08:00
ibuler
755a124b50 perf: checkout repo 2024-09-14 18:32:23 +08:00
zhaojisen
d6888776e7 perf: translate 2024-09-14 18:20:46 +08:00
wangruidong
29e233e715 perf: RemoteApp machine deployOption translate 2024-09-14 18:19:22 +08:00
wangruidong
99c3696d96 fix: Failed to import csv data 2024-09-14 18:17:15 +08:00
ibuler
ed6de83e8c perf: workflow push with full name 2024-09-14 18:09:30 +08:00
Eric
134f1a440c perf: replay part file download 2024-09-14 18:06:05 +08:00
ibuler
7da82242fe perf: github action workflow 2024-09-14 17:47:34 +08:00
Eric
2fd50d2425 perf: update compilemessages check ci 2024-09-14 17:38:07 +08:00
fit2bot
41a3e89248
chore: using pull pull request not push event (#14164)
* perf: diff with head not dev

* chore: using pull pull request not push event

---------

Co-authored-by: ibuler <ibuler@qq.com>
2024-09-14 17:21:49 +08:00
Bai
b125297c37 feat: GitHub Actions add compilemessages checked 2024-09-14 17:04:35 +08:00
ibuler
24255b69ee perf: diff with head not dev 2024-09-14 17:01:02 +08:00
ibuler
3bb51b39c4 perf: github action fetch branches 2024-09-14 16:51:25 +08:00
ibuler
b54da7d3b3 perf: workflow build base image 2024-09-14 16:42:06 +08:00
ibuler
534af0abf0 perf: build workflow 2024-09-14 16:32:15 +08:00
ibuler
8b0073333b perf: change git workflow 2024-09-14 16:25:06 +08:00
ZhaoJiSen
d8af2274f4
Merge pull request #14154 from jumpserver/pr@dev@koko_zh_translate
perf: KOKO zh translate
2024-09-14 15:34:27 +08:00
ibuler
3dd828d703 perf: workflow build base image 2024-09-14 15:34:20 +08:00
feng
fa6b4a5b63 perf: KOKO zh translate 2024-09-14 15:32:52 +08:00
ZhaoJiSen
8bd86c77f9
Merge pull request #14151 from jumpserver/pr@dev@win_rdp_ping
fix: Windows rdp ping fail
2024-09-14 14:48:17 +08:00
feng
3828e89cf8 fix: Windows rdp ping fail 2024-09-14 14:47:14 +08:00
wangruidong
e531b040ef fix: compilemessages error 2024-09-13 22:38:58 +08:00
wangruidong
3eee84a34e fix: delete ReplayStorage error 2024-09-13 19:09:21 +08:00
wangruidong
ab29df5991 fix: command search input error 2024-09-13 19:05:38 +08:00
wangruidong
b042f00688 fix: command search input error 2024-09-13 19:05:11 +08:00
github-actions[bot]
5beebaf51c perf: Update Dockerfile with new base image tag 2024-09-13 19:03:39 +08:00
wangruidong
50f075cc7e fix: Historical sessions download failed 2024-09-13 19:03:39 +08:00
Bai
e997236159 perf: Modify gunicorn log file rotate yesterday dir 2024-09-13 18:37:13 +08:00
ZhaoJiSen
c8b1d892e3
Merge pull request #14145 from jumpserver/pr@dev@account_already_exists
fix: Translate Account already exists
2024-09-13 18:33:25 +08:00
feng
9cb9e7328b fix: Translate Account already exists 2024-09-13 18:32:06 +08:00
feng
85129da942 perf: Postgresql add ssl mode 2024-09-13 17:49:14 +08:00
ZhaoJiSen
1cb00b1db4
Merge pull request #14138 from jumpserver/pr@dev@markdown_html
fix: Internal letter hyperlinks cannot be redirected
2024-09-13 11:07:08 +08:00
feng
c3798bfa95 fix: Internal letter hyperlinks cannot be redirected 2024-09-13 11:05:22 +08:00
github-actions[bot]
1d280599ae perf: Update Dockerfile with new base image tag 2024-09-12 18:55:01 +08:00
feng
ee8d7cdcac perf: Upgrade ansible postgresql 2024-09-12 18:55:01 +08:00
Aaron3S
1b4114fd5f perf: Optimize chen translation 2024-09-12 18:49:04 +08:00
Chenyang Shen
3c6c476f2e
Merge pull request #14135 from jumpserver/pr@dev@feat_add_some_chen_i18n
feat: add chen i18n
2024-09-12 18:41:47 +08:00
Aaron3S
f19e3fedbd feat: add chen i18n 2024-09-12 18:38:16 +08:00
Bai
542e64278f perf: fix migrate adhoc playbook 2024-09-12 16:33:36 +08:00
wangruidong
cd76294e81 fix: migrate ops adhoc and playbook unique_together error 2024-09-12 16:33:36 +08:00
wangruidong
4f9158b2ad fix: ldap test config msg error 2024-09-12 10:41:17 +08:00
ZhaoJiSen
e319f20296
Merge pull request #14126 from jumpserver/pr@dev@third_party_user_login_failed
perf: Third-party user login failed
2024-09-12 10:23:05 +08:00
feng
b00f3a851c perf: Third-party user login failed 2024-09-12 10:19:40 +08:00
wangruidong
ab529fd22c fix: i18n compilemessages error 2024-09-12 10:07:12 +08:00
wangruidong
c2784c44ad feat: LDAP HA 2024-09-11 18:26:11 +08:00
feng
512e727ac6 feat: Postgresql support ssl 2024-09-11 18:12:25 +08:00
wangruidong
2dd0154967 perf: modify only_myself to only_mine 2024-09-11 18:02:35 +08:00
wangruidong
f55869a449 feat: Support playbook, adhoc share 2024-09-11 17:52:06 +08:00
wangruidong
b6f3c23787 perf: task description translate 2024-09-11 14:57:07 +08:00
github-actions[bot]
6982ab1efc perf: Update Dockerfile with new base image tag 2024-09-10 15:54:05 +08:00
wangruidong
db4d841bb0 perf: add xpack task description 2024-09-10 15:54:05 +08:00
wangruidong
ef91ebb468 perf: clean expired and account is null SyncInstanceTask record 2024-09-10 15:50:11 +08:00
fit2bot
6264319c51
perf: When connected through a gateway, you can use nc to forward data (#14110)
Co-authored-by: feng <1304903146@qq.com>
Co-authored-by: Bryan <jiangjie.bai@fit2cloud.com>
2024-09-09 19:00:48 +08:00
fit2bot
1417abecfb
perf: Add task description (#14033)
Co-authored-by: ZhaoJiSen <97007455+ZhaoJiSen@users.noreply.github.com>
2024-09-09 18:54:33 +08:00
老广
bd548b3fe2 Revert "perf: update deps"
This reverts commit 76b6489636.
2024-09-09 15:33:57 +08:00
ZhaoJiSen
94cef9ea6e
Merge pull request #14106 from jumpserver/pr@dev@translate
perf: Translate
2024-09-09 14:29:04 +08:00
feng626
a338613b5a Merge branch 'dev' into pr@dev@translate 2024-09-09 14:27:38 +08:00
feng
0d833a966c perf: Translate 2024-09-09 14:15:16 +08:00
ibuler
76b6489636 perf: update deps 2024-09-09 11:39:51 +08:00
fit2bot
763fe778d5
perf: finish this feat (#14079)
* perf: basic finished

* perf: finish this feat

* perf: add datetime demo

---------

Co-authored-by: ibuler <ibuler@qq.com>
2024-09-09 11:26:24 +08:00
ibuler
cf1dc79c68 perf: applet host tips 2024-09-09 10:25:01 +08:00
ibuler
7973239424 perf: support change gateway platform 2024-09-06 17:35:57 +08:00
feng
1baacd0b2c perf: Disable delete admin user 2024-09-06 15:37:12 +08:00
feng
054d385ffc perf: Acl action add notify and warn 2024-09-06 11:07:30 +08:00
wangruidong
50d3a4906a feat: Add announcement start and end dates 2024-09-06 10:54:27 +08:00
wangruidong
c8b7008d42 perf: Translate 2024-09-06 10:54:12 +08:00
kebyn
e94520a3fd fix: 修复非标准实现 X-Forwarded-For 时的问题 2024-09-06 10:31:58 +08:00
wangruidong
55e8e34226 fix: 500 error caused by duplicate email or username 2024-09-06 10:22:37 +08:00
wangruidong
8755ece633 perf: Translate 2024-09-05 19:24:19 +08:00
feng
c545e2a3aa perf: Support SAML2, OIDC user authentication services, mapping user group field information 2024-09-04 18:42:47 +08:00
wangruidong
1068662ab1 perf: Optimize asset connection speed with es command storage 2024-09-02 13:52:23 +08:00
ZhaoJiSen
75141741a1
Merge pull request #14062 from jumpserver/pr@dev@translate
perf: Translate
2024-08-30 15:18:45 +08:00
feng
9da507bb62 perf: Translate 2024-08-30 15:16:44 +08:00
fit2bot
160293365a
perf: Regularly delete useless password change push records (#14026)
* perf: If the user Home page does not exist, push will fail

* perf: Change secret add uid parameter

* perf: Regularly delete useless password change push records

---------

Co-authored-by: feng <1304903146@qq.com>
2024-08-30 15:01:40 +08:00
wangruidong
7a19007aba perf: ldap import user error msg 2024-08-30 14:55:22 +08:00
ibuler
f866b93f96 perf: refresh oracle ports if need 2024-08-29 19:06:55 +08:00
feng
b9e64747ac perf: View the internal message and convert the content into markdown 2024-08-29 17:28:12 +08:00
fit2bot
25a473dc99 chore: update checkout action 2024-08-29 17:22:25 +08:00
Bai
e3bf015aa9 fix: user role can't open page of password & ssh key 2024-08-29 14:44:36 +08:00
Bryan
6d3d4a08af
Update README.md (#14043)
* Update README.md

* Update README.md

* Update README.md

* Update README.md

* Update README.md

* Update README.md

* Update README.md
2024-08-28 17:55:56 +08:00
Bai
9554de4ea6 fix: User check password need reset 2024-08-28 15:08:24 +08:00
ZhaoJiSen
6157ff7b7d
Merge pull request #14032 from jumpserver/pr@dev@koko_translate
perf: Koko translate
2024-08-27 17:41:30 +08:00
feng
774fd176fd perf: Koko translate 2024-08-27 17:39:37 +08:00
ibuler
b489db8054 perf: add a script activate user manually 2024-08-21 14:55:57 +08:00
ZhaoJiSen
6b9fa6e01f
Merge pull request #14010 from jumpserver/pr@dev@view_asset
perf: View asset mini api add domain platform field
2024-08-21 11:34:20 +08:00
ZhaoJiSen
9b59954393
Merge pull request #14011 from jumpserver/pr@dev@test_asset
perf: No permission to test asset connectivity
2024-08-21 11:33:55 +08:00
feng
ecaf19563f perf: No permission to test asset connectivity 2024-08-21 11:28:39 +08:00
feng
c431e96eaf perf: View asset mini api add domain platform field 2024-08-21 10:35:07 +08:00
ZhaoJiSen
d86f241450
Merge pull request #14005 from jumpserver/pr@dev@translate
perf: Translate
2024-08-20 11:30:37 +08:00
feng
3252db31fe perf: Translate 2024-08-20 11:28:30 +08:00
Bai
dac118dd26 perf: delete organization message 2024-08-19 16:06:37 +08:00
fit2bot
181eb621c0
perf: Remove kubernetes tree api (#13995)
* perf: Remove kubernetes tree api

* perf: Update Dockerfile with new base image tag

---------

Co-authored-by: feng <1304903146@qq.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: feng626 <57284900+feng626@users.noreply.github.com>
2024-08-19 16:04:00 +08:00
ibuler
828582333d perf: remove ansible receptcel 2024-08-19 11:16:58 +08:00
github-actions[bot]
657f7f822b perf: Update Dockerfile with new base image tag 2024-08-19 10:50:39 +08:00
ibuler
93627e4f9d perf: clean site packages 2024-08-19 10:50:39 +08:00
feng
2adb2519fa perf: Push password change and select finally use rdp to test connectivity. rdp does not support gateway 2024-08-19 10:20:33 +08:00
wangruidong
32ec48ac14 perf: Improve performance by optimizing ES index creation 2024-08-16 18:19:04 +08:00
Bai
b3a0d81740 fix: job periodic task double run 2024-08-15 20:17:20 +08:00
ibuler
2b160fbbc2 revert: entrypoint.sh 2024-08-15 20:14:24 +08:00
wangruidong
60fcf5fcd3 perf: luna connect asset name set custom value 2024-08-15 19:47:56 +08:00
Bai
7c2e50435d perf: i18n for ldap user import 2024-08-15 15:50:56 +08:00
ZhaoJiSen
57f91d0973
Merge pull request #13980 from jumpserver/pr@dev@no_account_found
perf: Lina translate
2024-08-15 14:49:41 +08:00
feng
49c033e003 perf: Lina translate 2024-08-15 14:47:55 +08:00
Ewall555
6476a8fee8 perf: Translate ticket cancel button 2024-08-15 14:35:43 +08:00
wangruidong
c10db2ab0f perf: LDAP import user Translate 2024-08-15 14:35:06 +08:00
wangruidong
647beffc01 fix: no accounts no send msg 2024-08-14 19:25:11 +08:00
feng
ac0c6ef3d5 perf: Storage update comment failed 2024-08-14 19:18:10 +08:00
feng
e13741827d fix: Domain detail asset amount mistake 2024-08-14 17:37:03 +08:00
wangruidong
29caf0154e perf: Translate batch approval 2024-08-14 17:36:23 +08:00
wangruidong
fbdcc437e6 perf: ticket msg field value set truncate string length 2024-08-14 16:45:43 +08:00
feng
b38e5df1aa perf: Translate 2024-08-14 16:37:57 +08:00
feng
0a39ba0a75 fix: Use only_sudo failed 2024-08-14 16:15:57 +08:00
wangruidong
c56e1bdbbe fix: call get_verify_state_failed_response NotImplementedError 2024-08-13 18:51:15 +08:00
feng
6b00ba271f perf: Replace Feishu to obtain user information interface 2024-08-13 18:13:08 +08:00
wangruidong
bddb1de2f8 perf: Comment translate 2024-08-13 18:04:29 +08:00
wangruidong
32ae77c42d perf: add TERMINAL_SSH_KEY_LIMIT_COUNT conf 2024-08-13 17:39:03 +08:00
feng
3b1701b1aa perf: Translate 2024-08-12 18:41:05 +08:00
feng
3b9bcc719e perf: Reset password: optimize form frame 2024-08-12 15:16:06 +08:00
feng
8e6aa4524d perf: Ip type translate 2024-08-09 17:10:11 +08:00
feng
cea63e6083 perf: Setting user attributes is invalid 2024-08-09 16:53:13 +08:00
feng
5d2d8ca487 perf: Translate 2024-08-08 19:17:34 +08:00
fit2bot
81146f44f7 perf: set default version 2024-08-08 17:54:43 +08:00
fit2bot
9adaa27f6c
perf: Luna login expire message (#13917)
* perf: Luna login expire message

* perf: Login timeout open in a new window

---------

Co-authored-by: feng <1304903146@qq.com>
2024-08-08 17:54:02 +08:00
feng
01c565f93f perf: Activity log no display 2024-08-08 16:39:44 +08:00
fit2bot
cb97afffab chore: remove build test 2024-08-08 16:06:51 +08:00
github-actions[bot]
1b55bf1670 perf: Update Dockerfile with new base image tag 2024-08-08 16:06:28 +08:00
fit2bot
b1c68165bb perf: update check version to v1.0.3 2024-08-08 16:06:28 +08:00
wangruidong
5d3e633e83 perf: ldap import msg modify 2024-08-07 19:12:42 +08:00
Eric
c863bf63b1 perf: update lina i18n 2024-08-07 17:45:55 +08:00
wangruidong
c71a6ae4ba fix: ssh_key search failed 2024-08-07 17:04:38 +08:00
wangruidong
38e3d9de8b feat: Allow users to customize asset name and comment 2024-08-07 16:44:01 +08:00
Eric
0c73acd4b9 perf: support only su or sudo 2024-08-07 10:57:09 +08:00
wangruidong
581a5c73a6 perf: object storage builtin comment i18n 2024-08-06 10:44:15 +08:00
feng
e1ed1d7c4c perf: Reset password remove sensitive data 2024-08-05 18:25:11 +08:00
Eric
805e7d1d5f perf: Check whether the applet is available. 2024-08-05 18:18:54 +08:00
feng
1957c2983b perf: Ticket set serial number add lock 2024-08-05 17:53:43 +08:00
Bai
6b1ceae6c5 perf: delete blank line 2024-08-05 16:29:54 +08:00
wangruidong
2a5c41dfaf feat: support configuring multiple SSH keys for users 2024-08-05 15:22:54 +08:00
wangruidong
7a38c9136e feat: Allow users to customize asset name and comment 2024-08-05 14:50:24 +08:00
ZhaoJiSen
9a3fdf76fc
Merge pull request #13876 from jumpserver/pr@dev@translate
perf: Translate
2024-08-05 14:33:41 +08:00
feng
136db61011 perf: Translate 2024-08-05 14:31:08 +08:00
ibuler
0d338f80c5 perf: ee dockerfile 2024-08-05 14:23:32 +08:00
feng
bd3909ad27 perf: Third-party user login settings default organization 2024-08-02 15:52:05 +08:00
Eric
96399f8315 perf: update tinker v0.1.7 2024-08-02 14:10:03 +08:00
ibuler
4e90d17484 perf: poetry mirror 2024-08-01 18:18:08 +08:00
ibuler
13de75c41f perf: docker file poetry mirror 2024-08-01 17:37:15 +08:00
ibuler
a77ebc5fee perf: pkg download
perf: resource download

perf: resource download
2024-08-01 16:15:36 +08:00
ibuler
99ce82a6a0 perf: build 2024-08-01 16:15:16 +08:00
wangruidong
ec95d25704 perf: Remove applets, no longer display remote application connection methods 2024-08-01 15:59:00 +08:00
Eric
7c6e83d124 perf: reformat code 2024-07-31 15:09:53 +08:00
ibuler
ad5e88f1e3 perf: display migrate log 2024-07-31 15:09:33 +08:00
wangruidong
b1e958d806 fix: stop job failed 2024-07-30 18:53:16 +08:00
feng
8506ae9edd perf: When account push change secret windows only modify the type equal to password 2024-07-30 18:33:42 +08:00
wangruidong
ceb2a9bb17 fix: Arbitrary File Read in Ansible Play 2024-07-30 18:19:01 +08:00
feng
8d83c953d3 perf: Support WeCom DingTalk FeiShu Lark Slack attribute mapping 2024-07-30 17:48:26 +08:00
Eric
9825f9fbd2 perf: Check if CORE_HOST should ignore SSL 2024-07-30 16:57:04 +08:00
feng
41b2ce06a8 perf: Approval process role selection supports multiple strategies 2024-07-30 16:06:01 +08:00
feng
920cfdac5c perf: Saml2 callback url miss port 2024-07-26 18:17:40 +08:00
Bai
8abf7876cc perf: graceful restart gunicorn worker timeout 30 2024-07-26 14:05:27 +08:00
wangruidong
2e625f2c33 feat: add assets amount field to platform page 2024-07-26 13:45:05 +08:00
halo
88037b2038 perf: Email service authentication username is optional 2024-07-26 11:23:15 +08:00
Bai
457021040a perf: Modifying the label matching logic of an AppletHost (random) 2024-07-25 19:04:57 +08:00
fit2bot
4887b21d35
fix: message publish_task args,kwargs can json encode (#13797)
* fix: message publish_task args,kwargs can json encode

* perf: Update Dockerfile with new base image tag

---------

Co-authored-by: wangruidong <940853815@qq.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-07-24 15:45:27 +08:00
fit2bot
03a66fd563
perf: Modify error message for desktop client login (#13763)
* perf: Modify error message for desktop client login

* perf: Update Dockerfile with new base image tag

---------

Co-authored-by: wangruidong <940853815@qq.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-07-24 10:59:54 +08:00
fit2bot
ef656a8dfd
perf: change docker file build (#13761)
* perf: change docker file build

* perf: Update Dockerfile with new base image tag

---------

Co-authored-by: ibuler <ibuler@qq.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-07-23 16:38:00 +08:00
Eric
5e45129e32 perf: add luna i18n 2024-07-23 15:40:32 +08:00
feng
ea64b01da6 perf: You can modify sudo permissions multiple times 2024-07-22 17:27:20 +08:00
feng
c3b863c2be perf: You can modify sudo permissions multiple times 2024-07-22 17:27:20 +08:00
feng
6a7896b712 perf: Gpt3 to gpt-4o-mini 2024-07-19 11:56:07 +08:00
feng
83c1f8e4d3 perf: The gateway password contains ! Password parsing failed 2024-07-19 10:41:54 +08:00
Bai
9d3fdd37a3 perf: user authentication supports configuration redirection 2024-07-19 10:37:52 +08:00
Bai
419195895e perf: update readme 2024-07-18 19:43:38 +08:00
feng
c92188887d perf: Create authorization to add template account Push account parameters 2024-07-18 19:15:46 +08:00
feng
dcfc4e6e7b perf: The locked IP shows the username + translate 2024-07-17 18:10:22 +08:00
feng
836adab5d0 perf: Feishu lark support attributes settings 2024-07-17 16:59:57 +08:00
wangruidong
e93227a53c fix: The asset cannot be obtained during online synchronization 2024-07-17 15:52:40 +08:00
fit2bot
d6f6bb9c1b
fix: session viewset api permission validation (#13750)
* fix: session viewset api permission validation

* fix: some api permission validation

---------

Co-authored-by: Bai <baijiangjie@gmail.com>
2024-07-17 15:35:34 +08:00
feng
85825165fc perf: Translate 2024-07-17 11:37:19 +08:00
fit2bot
66047c7926
perf: Migrate (#13741)
Co-authored-by: feng <1304903146@qq.com>
2024-07-17 10:18:53 +08:00
Bai
456bcd2d3f fix: i18n error 2024-07-17 10:01:21 +08:00
Bai
259f68a806 fix: i18n error 2024-07-17 09:54:47 +08:00
feng
4e6231ab19 perf: Notification remove kael magnus 2024-07-16 19:34:18 +08:00
fit2bot
d7bbfdcce6
perf: Translate (#13731)
Co-authored-by: feng <1304903146@qq.com>
2024-07-16 18:38:47 +08:00
吴小白
a0cc9e5db5 fix: deploy applet host 2024-07-16 12:49:33 +08:00
wangruidong
ea6cd853de perf: 社区版移除magnus 2024-07-16 10:40:33 +08:00
fit2bot
53a388a7e0
fix: View user perms bug (#13721)
Co-authored-by: feng <1304903146@qq.com>
2024-07-15 17:50:37 +08:00
fit2bot
13b1938efb
perf: Community supports custom platforms (#13719)
Co-authored-by: feng <1304903146@qq.com>
2024-07-15 17:31:44 +08:00
ibuler
6677985e4a perf: support user email login 2024-07-15 16:23:52 +08:00
ibuler
cfa1034161 perf: community add postgre support 2024-07-15 16:19:24 +08:00
ibuler
815973fb63 perf: split user model to many file 2024-07-15 10:54:17 +08:00
吴小白
92d369aaca perf: remove receptor 2024-07-12 18:38:26 +08:00
jiangweidong
281a2d9679 fix: custom sms send success but prompt fails 2024-07-12 18:37:46 +08:00
feng
e9f4615caa perf: Optimize the password reset page experience for new users (the password field will be lengthened) 2024-07-12 15:17:49 +08:00
jiangweidong
c0d2efa72a perf: async sms task params can json 2024-07-12 15:16:41 +08:00
gerry-fit
247f4d5c19 perf: Enterprise Edition Hide Footer Copyright Content 2024-07-11 16:10:42 +08:00
fit2bot
29c29b17d4
perf: Translate (#13686)
Co-authored-by: feng <1304903146@qq.com>
2024-07-10 19:03:19 +08:00
wangruidong
5608f7d20d fix: 定时清理任务不生效问题 2024-07-10 16:13:47 +08:00
Bai
aa8ae36255 perf: README 2024-07-10 14:55:46 +08:00
feng
2292e6f2eb perf: save_passwd_change filter user source local and passwords not emtpy 2024-07-10 14:20:33 +08:00
fit2bot
bf82a1c721
fix: Operator write failed (#13677)
Co-authored-by: feng <1304903146@qq.com>
2024-07-10 11:24:26 +08:00
Bryan
8ef84bbc03 Update README.md 2024-07-10 11:13:42 +08:00
fit2bot
e36d51cc0b
perf: country code api (#13672)
* perf: remove notification migrations

* perf: country code api

---------

Co-authored-by: ibuler <ibuler@qq.com>
2024-07-09 19:23:41 +08:00
feng
5c1d0238e1 39.102.214.101 ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOzDdXgVjgKrT+ZF5DXcNZqJnkjwvt0M5FbVpvbTOU/v
perf: save_passwd_change filter user source local and passwords not emtpy
2024-07-09 19:08:22 +08:00
wangruidong
c6befe4c4b fix: creat linux asset protocol default value is sftp 2024-07-09 19:04:19 +08:00
Bai
5a57c296a1 feat: add db table data analyzer util 2024-07-09 18:49:51 +08:00
wangruidong
34ddfd24be fix: import role template csv failed 2024-07-09 18:15:44 +08:00
wangruidong
39051ef0fd fix: import role template csv failed 2024-07-09 17:04:59 +08:00
fit2bot
ddd813241c
perf: JobExecutionViewSet add SECURITY_COMMAND_EXECUTION permission (#13662)
Co-authored-by: feng <1304903146@qq.com>
2024-07-09 16:34:15 +08:00
mmagi
60f7cbef9a fix: 主机硬件信息硬盘大小避免多次挂载重复计入 2024-07-09 15:52:15 +08:00
wangruidong
4adc981a21 perf: update date_updated when update user roles 2024-07-09 15:51:15 +08:00
mmagi
c42913c15e fix: win主机硬件信息内存单位与其他主机一致;cpu信息字段与其他主机一致 2024-07-09 15:49:35 +08:00
halo
bb6d60b46d perf: 优化创建子节点时锁置后 2024-07-09 15:15:06 +08:00
fit2bot
afe7f03c16
perf: login style change (#13539)
* perf: login style change

* perf: login style change

* perf: login style change

---------

Co-authored-by: zhaojisen <1301338853@qq.com>
2024-07-09 15:02:37 +08:00
fit2bot
ba8d3be9a6
fix: Operatelog plaintext storage AKSK (#13506)
* fix: Operatelog plaintext storage AKSK

* perf: Encrypt some field when saving operatelog

* fix: Operatelog plaintext storage AKSK

---------

Co-authored-by: jiangweidong <1053570670@qq.com>
2024-07-09 14:52:00 +08:00
Eric
d14d8869ac perf: add connection options for mongodb 2024-07-09 14:00:59 +08:00
wangruidong
2f7391efc3 perf: modify migrations 2024-07-09 11:38:47 +08:00
ibuler
75fa96b29c perf: remove notification migrations 2024-07-09 11:25:49 +08:00
maninhill
c56ab9bc1e chore: Update README.zh-CN.md 2024-07-09 11:11:20 +08:00
fit2bot
443e492fd4
perf: Asset type cloud add community version (#13640)
Co-authored-by: feng <1304903146@qq.com>
2024-07-09 10:59:56 +08:00
ibuler
b8c223d525 perf: can set xpack disable force 2024-07-09 10:56:20 +08:00
吴小白
a509afe24b fix: FromAsCasing keywords 2024-07-09 10:35:59 +08:00
fit2bot
9654add528
perf: Translate (#13633)
Co-authored-by: feng <1304903146@qq.com>
2024-07-08 15:43:56 +08:00
Bryan
d0a9409078 Update README.md 2024-07-08 14:51:07 +08:00
fit2bot
5836583490
fix: The account gather results do not have the last login time (#13625)
Co-authored-by: feng <1304903146@qq.com>
2024-07-08 11:42:24 +08:00
fit2bot
57d689bee6
perf: Translate (#13620)
Co-authored-by: feng <1304903146@qq.com>
2024-07-05 18:09:39 +08:00
ZhaoJiSen
8a3fb6bd4d
Merge pull request #13616 from jumpserver/pr@dev@translate
perf: Translate
2024-07-05 16:50:00 +08:00
feng
78bd3f581a perf: Translate 2024-07-05 16:36:55 +08:00
fit2bot
d07c476507
perf: Translate (#13612)
Co-authored-by: feng <1304903146@qq.com>
2024-07-04 18:14:34 +08:00
fit2bot
50d196eda4
perf: Job api add filter options (#13610)
Co-authored-by: feng <1304903146@qq.com>
2024-07-04 16:03:51 +08:00
ibuler
823d9af91d perf: upgrade to v4, more international and more standardized. 2024-07-04 10:06:43 +08:00
Bryan
3731123369 Update README.md 2024-07-04 09:47:56 +08:00
Bryan
1a68c4b44a Update README.md 2024-07-04 09:47:56 +08:00
Bryan
0f79006b59 Update README.md 2024-07-04 09:47:56 +08:00
maninhill
c95ad5a31c chore: Update README.md 2024-07-04 09:44:01 +08:00
maninhill
e25a96d359 chore: Update README.md 2024-07-03 22:56:08 +08:00
maninhill
04284adc87 chore: Update README.md 2024-07-03 22:40:32 +08:00
maninhill
7ee7d50f22 chore: Update README.md 2024-07-03 18:54:56 +08:00
fit2bot
3d015398c3
perf: Luna translate (#13599)
Co-authored-by: feng <1304903146@qq.com>
2024-07-03 17:50:55 +08:00
wangruidong
da8b328f80 fix: bulk delete playbook 500 error 2024-07-03 17:15:50 +08:00
fit2bot
82a6702c90
perf: Translate (#13594)
Co-authored-by: feng <1304903146@qq.com>
2024-07-03 16:50:09 +08:00
fit2bot
ad267bcd35
perf: Translate (#13593)
Co-authored-by: feng <1304903146@qq.com>
Co-authored-by: feng626 <57284900+feng626@users.noreply.github.com>
2024-07-03 16:42:02 +08:00
maninhill
15dc922bca chore: Update README.md 2024-07-03 16:10:03 +08:00
Aaron3S
22405d46d6 feat: add chen translate 2024-07-03 15:54:19 +08:00
wangruidong
35b0741068 fix: modify render_to_json rbac 2024-07-03 15:53:25 +08:00
刘瑞斌
d7b8174fd0 chore: use unseq list 2024-07-03 15:35:14 +08:00
ibuler
43cfb11bca perf: tag export format use name:value style 2024-07-03 15:34:53 +08:00
wangruidong
f955cebaa0 perf: Translate 2024-07-03 15:17:10 +08:00
fit2bot
5d7ec054e6
perf: Community limit (#13584)
Co-authored-by: feng <1304903146@qq.com>
2024-07-03 15:01:37 +08:00
Bryan
6088a38eed Update README.md 2024-07-03 14:51:51 +08:00
fit2bot
e1a84e76bb
perf: some translation (#13585)
* perf: applet host platform

* perf: some translation

---------

Co-authored-by: ibuler <ibuler@qq.com>
2024-07-03 14:51:21 +08:00
wangruidong
19f9179e7f perf: update migrations same with v3 2024-07-03 14:46:17 +08:00
Bryan
aa4a8d5b42 Update README.md 2024-07-03 14:44:28 +08:00
老广
10ba31086c Update README.md 2024-07-03 14:40:49 +08:00
fit2bot
fa8312bc65
perf: Translate (#13575)
Co-authored-by: feng <1304903146@qq.com>
2024-07-02 18:30:23 +08:00
maninhill
512e727dd4 chore: Update CONTRIBUTING.md 2024-07-02 18:25:57 +08:00
wangruidong
a529609275 fix: Resolve issue with update permissions preventing data import 2024-07-02 16:34:10 +08:00
fit2bot
a8973330fe
fix: Account tempale cannot push params (#13571)
Co-authored-by: feng <1304903146@qq.com>
2024-07-02 16:12:27 +08:00
ibuler
d42acc3848 perf: applet host platform 2024-07-02 15:15:13 +08:00
fit2bot
912cefbc85
perf: Lina LoginLogTotal translate (#13569)
Co-authored-by: feng <1304903146@qq.com>
2024-07-02 15:10:33 +08:00
feng
2bb475d0ce perf: Account push log optimization 2024-07-02 14:55:38 +08:00
ibuler
22788ff2da perf: remove org api scope 2024-07-02 14:11:16 +08:00
fit2bot
5594b25ae0
perf: Login confirm return failed (#13560)
Co-authored-by: feng <1304903146@qq.com>
2024-07-02 11:02:30 +08:00
Aaron3S
4733d89807 perf: 优化 chen 翻译 2024-07-02 10:19:36 +08:00
wangruidong
c718fe1a9d perf: modify user login ACL msg 2024-07-01 19:30:42 +08:00
wangruidong
237b4a82c9 fix: handle 500 error when re-binding DingTalk user to another user 2024-07-01 18:03:47 +08:00
fit2bot
76e0cbb8ac
perf: update lion i18n (#13556)
* perf: update lion i18n

* perf: update i18n

---------

Co-authored-by: Eric <xplzv@126.com>
2024-07-01 16:22:46 +08:00
fit2bot
b3a670d380
perf: Lina mfa translate (#13555)
Co-authored-by: feng <1304903146@qq.com>
Co-authored-by: feng626 <57284900+feng626@users.noreply.github.com>
2024-07-01 16:12:13 +08:00
wangruidong
db243d050e fix: playbook FileNotFoundError 2024-07-01 15:51:14 +08:00
wangruidong
cd2648291e perf: Translate 2024-07-01 15:15:53 +08:00
wangruidong
4a49bde1f0 perf: add compatibility for macOS environment 2024-07-01 15:02:32 +08:00
wangruidong
d9754496d0 perf: add bubblewrap 2024-07-01 15:02:32 +08:00
fit2bot
6753b5fd19
perf: Translate (#13550)
Co-authored-by: feng <1304903146@qq.com>
2024-07-01 11:42:33 +08:00
fit2bot
aeb320ba30
perf: Lina translate (#13548)
Co-authored-by: feng <1304903146@qq.com>
2024-07-01 10:15:06 +08:00
ibuler
e712e8ccfc perf: ansible version and set user lang 2024-06-28 18:59:44 +08:00
fit2bot
1d6f827296
perf: Migrate (#13540)
Co-authored-by: feng <1304903146@qq.com>
2024-06-28 18:36:24 +08:00
ibuler
772c9b385c perf: lang setting from core 2024-06-28 16:40:25 +08:00
wangruidong
f5053728e7 perf: Translate Tags 2024-06-28 15:28:57 +08:00
wangruidong
f67fd29499 perf: Community update user list API to exclude specific fields 2024-06-28 14:54:07 +08:00
fit2bot
138ea35620
fix: Got perm tree 500 (#13533)
Co-authored-by: feng <1304903146@qq.com>
2024-06-28 11:39:09 +08:00
wangruidong
bf56549f01 perf: Task log optimize 2024-06-28 10:25:10 +08:00
Eric
908181af64 perf: clean mp4 replay file
perf: refactor code to clean files
2024-06-27 18:12:37 +08:00
fit2bot
7b4d3c44f8
fix: Asset perm calculate failed (#13530)
Co-authored-by: feng <1304903146@qq.com>
Co-authored-by: feng626 <57284900+feng626@users.noreply.github.com>
2024-06-27 18:05:08 +08:00
feng
b7a6454d65 fix: Asset perm calculate failed 2024-06-27 17:56:41 +08:00
ibuler
6d81fa7fdf perf: change default login title 2024-06-27 17:55:57 +08:00
wangruidong
0e8833cce3 perf: Translate Client connection 2024-06-27 11:27:43 +08:00
fit2bot
24d9e65532
fix: Asset 003 migrate failed (#13519)
Co-authored-by: feng <1304903146@qq.com>
2024-06-26 21:42:43 +08:00
ibuler
bca9bdf619 perf: translate some word 2024-06-26 19:29:56 +08:00
fit2bot
cd39e20808
perf: Applet host provider add filterset (#13517)
Co-authored-by: feng <1304903146@qq.com>
2024-06-26 19:07:28 +08:00
fit2bot
9c8680d3f4
perf: Translate (#13514)
Co-authored-by: feng <1304903146@qq.com>
2024-06-26 17:06:23 +08:00
wangruidong
dd84ca8f85 fix: Virtualapp rbac config 2024-06-26 16:43:32 +08:00
Eric
96c1f689c0 perf: modify dbeave remoteapp dislay name 2024-06-26 16:19:09 +08:00
wangruidong
84855bfd7e perf: Translate Lark 2024-06-26 16:13:16 +08:00
fit2bot
40c5a218a9
perf: Translate (#13509)
Co-authored-by: feng <1304903146@qq.com>
Co-authored-by: feng626 <57284900+feng626@users.noreply.github.com>
2024-06-26 15:18:37 +08:00
wangruidong
8e87972a76 perf: Translate ticket action 2024-06-26 14:37:32 +08:00
fit2bot
3faee9b80c
perf: change some translate (#13505)
* perf: some word translate


---------

Co-authored-by: ibuler <ibuler@qq.com>
2024-06-25 19:36:35 +08:00
fit2bot
5a1389a187
perf: Translate (#13504)
Co-authored-by: feng <1304903146@qq.com>
2024-06-25 19:10:32 +08:00
Eric
565c2f493c perf: add lion i18n 2024-06-25 19:04:40 +08:00
Bai
8d48593fc4 perf: Update README 2024-06-25 16:44:00 +08:00
fit2bot
b50c96fcd6
perf: Update README (#13497)
* perf: Update README

* perf: Update README

* perf: Update README

---------

Co-authored-by: Bai <baijiangjie@gmail.com>
2024-06-25 16:29:14 +08:00
ibuler
85700a2a26 perf: some word translate 2024-06-25 15:36:25 +08:00
Bryan
66615b7dd3 Update README.md 2024-06-25 15:34:31 +08:00
Bryan
2c1a1fa31e Update README.md 2024-06-25 15:34:31 +08:00
wangruidong
bbc442b56e perf: Translate CanDragSelect tips 2024-06-25 15:31:30 +08:00
Bryan
1ca579f4f0 Update README.md 2024-06-25 15:01:47 +08:00
Bryan
9e3b23179c Update README.md 2024-06-25 15:01:47 +08:00
Bryan
9fd861d047 Update README.md 2024-06-25 15:01:47 +08:00
fit2bot
4abfcb27d1
perf: Update README (#13491)
* perf: Update README

* perf: Update README

* perf: Update README

* perf: Update README

* perf: Update README

---------

Co-authored-by: Bai <baijiangjie@gmail.com>
Co-authored-by: Bryan <jiangjie.bai@fit2cloud.com>
2024-06-25 11:22:38 +08:00
老广
3463761693 Update README.md 2024-06-25 10:28:14 +08:00
Eric
c311adc1da chore: update mysqlclient 2.2.4 2024-06-24 20:12:09 +08:00
ibuler
ee258707c8 perf: change initial passwd to ChangeMe 2024-06-24 20:11:49 +08:00
Eric
17d96669fe perf: remove pymysql 2024-06-24 20:11:23 +08:00
fit2bot
3fade107d5
perf: Translate (#13489)
Co-authored-by: feng <1304903146@qq.com>
Co-authored-by: feng626 <57284900+feng626@users.noreply.github.com>
2024-06-24 20:10:34 +08:00
wangruidong
f91ec6fa6a perf: favor & disfavor trans 2024-06-24 19:38:19 +08:00
Bai
dfff41e9d6 perf: Update README 2024-06-24 19:25:41 +08:00
Bai
478e81b8fa perf: Update README 2024-06-24 19:25:41 +08:00
wangruidong
9b14f2aa1f perf: ftp log has_file trans 2024-06-24 19:18:26 +08:00
Bryan
18e648af6e
Update README.md (#13481)
* Update README.md

* perf: Update README

* perf: Update README

* perf: Update README

* perf: Update README

* perf: Update README

* perf: Update README

* perf: Update README

* perf: Update README

* perf: Update README

* perf: Update README

* perf: Update README

* perf: Update README

* perf: Update README
2024-06-24 19:16:30 +08:00
老广
45bd69585a Update README.md 2024-06-24 18:24:21 +08:00
maninhill
42a0cde450 chore: Update README.md 2024-06-24 18:22:38 +08:00
Bai
a9ef21ea3f fix: poetry lock 2024-06-24 17:07:34 +08:00
Bai
13d24a12db fix: i18n messages 2024-06-24 17:07:34 +08:00
wangruidong
2bd09f246d fix: raise http 400 when batch delete in component settings 2024-06-24 16:25:54 +08:00
fit2bot
23c81cf5eb
perf: Migrate asset dameng json (#13477)
Co-authored-by: feng <1304903146@qq.com>
2024-06-24 16:24:40 +08:00
Bryan
e95284335e
Merge pull request #13472 from jumpserver/v4
Merge v4 to dev
2024-06-24 15:50:29 +08:00
Bai
1c7f82e65a Merge v4 to dev (Update poetry lock) 2024-06-24 15:47:13 +08:00
Bai
dfde50c768 Merge v4 to dev 2024-06-24 15:43:40 +08:00
ibuler
8bfbebf29e Merge branch 'v4' of github.com:jumpserver/jumpserver into v4 2024-06-24 14:51:42 +08:00
ibuler
8157f9891f perf: 优化批量删除判断 2024-06-24 14:50:53 +08:00
wangruidong
ad95adc833 perf: trans LDAP msg 2024-06-24 14:24:06 +08:00
ibuler
f7e55c9b89 merge: with remote 2024-06-24 11:06:32 +08:00
ibuler
11b125655d perf: passkey help msg 2024-06-24 11:05:41 +08:00
fit2bot
c6628a1959
perf: Translate (#13471)
Co-authored-by: feng <1304903146@qq.com>
2024-06-24 10:57:10 +08:00
fit2bot
ae7dbbedcc
perf: Edit rbac perms (#13468)
Co-authored-by: feng <1304903146@qq.com>
Co-authored-by: feng626 <57284900+feng626@users.noreply.github.com>
2024-06-21 19:08:37 +08:00
fit2bot
407a77f61b perf: trans StopLogOutput 2024-06-21 18:05:39 +08:00
fit2bot
e06f9a03d6
perf: System setting perm (#13463)
Co-authored-by: feng <1304903146@qq.com>
2024-06-20 19:19:14 +08:00
fit2bot
07edbea54e
perf: Update ah translate (#13459)
Co-authored-by: feng <1304903146@qq.com>
2024-06-20 16:56:41 +08:00
ibuler
856e501a15 Merge branch 'v4' of github.com:jumpserver/jumpserver into v4 2024-06-20 11:11:19 +08:00
ibuler
8cf900f9de perf: 生产授权树 2024-06-20 11:10:57 +08:00
fit2bot
a54605ac79
perf: Translate (#13454)
Co-authored-by: feng <1304903146@qq.com>
2024-06-19 19:36:47 +08:00
wangruidong
92790d711e perf: Add log download button 2024-06-19 15:56:02 +08:00
fit2bot
5b548d8d57
perf: Translate (#13450)
Co-authored-by: feng <1304903146@qq.com>
2024-06-19 14:48:48 +08:00
fit2bot
afdf777386
perf: Translate account (#13445)
Co-authored-by: feng <1304903146@qq.com>
2024-06-18 19:39:27 +08:00
ibuler
cd2af0dcf7 perf: english traslation 2024-06-18 18:47:06 +08:00
Bai
523468f7af fix: Add API Exception traceback log if settings.DEBUG_DEV enabled 2024-06-18 11:58:44 +08:00
Bai
9385d04812 fix: Add API Exception traceback log if settings.DEBUG_DEV enabled 2024-06-18 11:55:56 +08:00
fit2bot
2ee435a8ec
perf: Translate (#13438)
Co-authored-by: feng <1304903146@qq.com>
2024-06-17 19:47:05 +08:00
fit2bot
f3a827b76b
perf: Translate (#13437)
Co-authored-by: feng <1304903146@qq.com>
2024-06-17 19:22:45 +08:00
fit2bot
50ceca9f06
perf: Perms system settings (#13435)
Co-authored-by: feng <1304903146@qq.com>
2024-06-17 17:40:03 +08:00
Bai
8a5e86dfa7 fix: migrations assets 0003 json.loads error 2024-06-17 10:39:38 +08:00
fit2bot
6ffae48ab2
perf: Translate (#13431)
Co-authored-by: feng <1304903146@qq.com>
2024-06-14 19:15:41 +08:00
fit2bot
9ff78c8569
perf: Lina translate (#13427)
Co-authored-by: feng <1304903146@qq.com>
2024-06-14 15:16:00 +08:00
fit2bot
d6718d7b78
perf: Device asset remove_account_enabled false (#13426)
Co-authored-by: feng <1304903146@qq.com>
2024-06-14 14:47:13 +08:00
fit2bot
32966b260a
perf: Refactor OperateLogStore separator logic for database compatibility (#13424)
Co-authored-by: feng <1304903146@qq.com>
Co-authored-by: feng626 <57284900+feng626@users.noreply.github.com>
2024-06-14 14:25:14 +08:00
feng
6c59888d77 perf: Refactor OperateLogStore separator logic for database compatibility 2024-06-14 14:09:53 +08:00
Bai
1c1d839b82 fix: i18n compilemessages 2024-06-14 09:53:08 +08:00
wangruidong
7d295cc675 perf: rbac applet modify translate 2024-06-13 18:26:10 +08:00
fit2bot
75496cbe91
perf: Account backup failed (#13420)
Co-authored-by: feng <1304903146@qq.com>
2024-06-13 18:20:00 +08:00
wangruidong
11f6a029de perf: modify translate 2024-06-13 17:45:39 +08:00
Bai
e40c66c7ed fix: select_for_update error for out join sql 2024-06-13 15:50:11 +08:00
ibuler
2a33337963 Merge branch 'v4' of github.com:jumpserver/jumpserver into v4 2024-06-13 13:04:07 +08:00
ibuler
bd1a768743 perf: change some translation 2024-06-13 13:02:03 +08:00
fit2bot
0c0ec098ae
perf: Modify some translations (#13412)
Co-authored-by: wangruidong <940853815@qq.com>
2024-06-12 19:38:45 +08:00
fit2bot
37ad7b32e4
perf: Cloud account translate (#13413)
Co-authored-by: feng <1304903146@qq.com>
2024-06-12 19:38:07 +08:00
ibuler
2640963938 Merge branch 'v4' of github.com:jumpserver/jumpserver into v4 2024-06-12 19:29:14 +08:00
ibuler
6bc9181c25 fix: some bugs 2024-06-12 19:25:36 +08:00
fit2bot
d8379195e6
perf: Platform automation add remove account method (#13411)
Co-authored-by: feng <1304903146@qq.com>
2024-06-12 19:23:40 +08:00
Bai
9195c658a0 perf: Update djangojs.po file 2024-06-12 18:36:04 +08:00
wangruidong
3fb261b5c8 fix: Role details - multiple instances of English in permissions list 2024-06-12 17:30:03 +08:00
ibuler
aa16c3d3a1 Merge branch 'v4' of github.com:jumpserver/jumpserver into v4 2024-06-12 16:19:26 +08:00
ibuler
7be6cf2b73 perf: tags update 2024-06-12 16:19:16 +08:00
fit2bot
60738da053
perf: Automation remove account task fail (#13406)
Co-authored-by: feng <1304903146@qq.com>
2024-06-12 15:30:52 +08:00
Aaron3S
507ad10389 feat: add some translations for chen 2024-06-12 15:27:53 +08:00
wangruidong
67bc16238c perf: i18n 2024-06-12 10:25:22 +08:00
Aaron3S
db88f6c9b4 perf: 优化 chen 翻译文件 2024-06-11 18:19:30 +08:00
fit2bot
8b7f60d43e
fix: Cloud sync related issues (#13396)
Co-authored-by: feng <1304903146@qq.com>
2024-06-11 17:42:13 +08:00
ibuler
cd1f6a9137 Merge branch 'v4' of github.com:jumpserver/jumpserver into v4 2024-06-06 16:56:47 +08:00
ibuler
7973d066a3 revert: asset permission real accounts serializer 2024-06-06 16:56:07 +08:00
wangruidong
ad65097a8f perf: Crontab i18n 2024-06-06 14:49:56 +08:00
ibuler
1b05f56598 Merge branch 'v4' of github.com:jumpserver/jumpserver into v4 2024-06-05 19:34:04 +08:00
ibuler
3468f8cd40 perf: 修改授权的账号 2024-06-05 19:33:40 +08:00
Bryan
5c81e974cd
Update README.md 2024-06-05 18:00:13 +08:00
Bryan
b638cf7417
Update README.md 2024-06-05 17:53:26 +08:00
Bryan
1db1961cc0
Update README.md 2024-06-05 17:25:45 +08:00
Bryan
811afdcf1a
Update README.md 2024-06-05 17:25:12 +08:00
Bryan
1f87ce2a47
Update README.md 2024-06-05 17:09:04 +08:00
Bryan
8213e38e6a
Update README.md 2024-06-05 17:01:31 +08:00
ibuler
263bcbb566 Merge branch 'v4' of github.com:jumpserver/jumpserver into v4 2024-06-04 19:08:51 +08:00
ibuler
050ddc88f2 perf: labels 支持颜色 2024-06-04 19:08:16 +08:00
feng
38e8791d9f perf: Community Edition Remove db client 2024-06-04 16:24:37 +08:00
fit2bot
deb8474c1b
feat: add oracle database web-cli connect method (#13358)
Co-authored-by: Aaron3S <chenyang@fit2cloud.com>
2024-06-04 14:51:47 +08:00
fit2bot
12740ead08
perf: ticket translate (#13357)
Co-authored-by: feng <1304903146@qq.com>
2024-06-04 11:01:33 +08:00
Bai
6322559bd7 perf: i18n updated 2024-06-03 19:15:40 +08:00
Aaron3S
6c5eb00fb6 feat: 增加 chen 翻译文件 2024-06-03 16:42:23 +08:00
feng
dad2f8eb65 perf: koko translate 2024-06-03 14:29:57 +08:00
Bai
c8679f48f5 perf: Update i18n 2024-06-03 11:44:16 +08:00
fit2bot
510dc1eaf2
perf: translate (#13347)
Co-authored-by: feng <1304903146@qq.com>
2024-06-03 10:26:12 +08:00
wangruidong
a313753757 perf: action i18n 2024-05-31 16:34:03 +08:00
wangruidong
53f106b30d perf: i18n 2024-05-31 14:39:56 +08:00
吴小白
0d27bfcfa9 perf: 添加 arm64 缺失依赖 2024-05-31 10:56:48 +08:00
wangruidong
ba6660216c perf: celery task log format 2024-05-31 10:15:11 +08:00
吴小白
3536af2051 perf: 按要求移除重复构建代码 2024-05-31 10:13:22 +08:00
fit2bot
21bb0a8162
fix: translate json (#13322)
Co-authored-by: feng <1304903146@qq.com>
2024-05-30 14:20:12 +08:00
jiangweidong
d718398791
feat: refactoring sync module (#13293) 2024-05-29 18:44:32 +08:00
吴小白
0b65e3ffda perf: 按要求移除重复构建代码 2024-05-28 18:50:11 +08:00
feng
91a1da57e9 perf: mfa interface optimization, mobile phone number can be empty 2024-05-28 17:27:17 +08:00
ibuler
f95cbd6977 merge: with remote 2024-05-27 16:46:13 +08:00
ibuler
f16ec02c40 perf: 修改翻译 2024-05-27 16:43:26 +08:00
wangruidong
0ea2339ad5 perf: notification i18n 2024-05-27 15:24:31 +08:00
Bai
8ebdd59e00 Merge branch 'v4_bain' into v4 2024-05-27 11:35:16 +08:00
Bai
c4e30737a4 perf: i18n bai 2024-05-27 11:34:53 +08:00
ibuler
f127aca5f8 Merge branch 'v4' of github.com:jumpserver/jumpserver into v4 2024-05-27 11:20:59 +08:00
ibuler
7333c8e094 perf: 修改 json 空格数量 2024-05-27 11:20:35 +08:00
Bai
a1e9382275 perf: i18n bai 2024-05-27 11:13:13 +08:00
ibuler
097a6c5c5f perf: 修改 label 为 tag 2024-05-27 11:07:36 +08:00
fit2bot
4e023057cc
perf: ticket translate (#13291)
Co-authored-by: feng <1304903146@qq.com>
2024-05-27 11:00:54 +08:00
ibuler
4034e2152c Merge branch 'v4' of github.com:jumpserver/jumpserver into v4 2024-05-24 18:43:07 +08:00
ibuler
e8d6c6b711 merge: with remote 2024-05-24 18:42:37 +08:00
Bai
43215d27c5 perf: migrations i18n 2024-05-24 14:54:56 +08:00
Bai
e20db96331 Merge branch 'v4_baisse' into v4 2024-05-24 14:42:27 +08:00
Bai
564ad40b99 perf: i18n System Settings End 2024-05-24 14:41:28 +08:00
ibuler
32ef4c79da perf: 修改翻译 2024-05-23 19:00:28 +08:00
wangruidong
af4f6ebb26 perf: dashboard i18n 2024-05-23 18:23:10 +08:00
wangruidong
33b688b021 perf: modify task log i18n 2024-05-23 18:12:52 +08:00
fit2bot
b179770dbf
perf: del extra translate (#13274)
Co-authored-by: feng <1304903146@qq.com>
Co-authored-by: feng626 <57284900+feng626@users.noreply.github.com>
2024-05-22 19:24:25 +08:00
Bai
e7f92ec0d7 merged: Merge to v4 (bai) 2024-05-22 18:12:35 +08:00
Bai
79449a8a02 perf: i18n System Settings -> Authentication-OIDC 2024-05-22 17:51:45 +08:00
fit2bot
f259509ef8
fix: notification migrate file (#13269)
Co-authored-by: feng <1304903146@qq.com>
2024-05-22 10:47:55 +08:00
fit2bot
82977f9023
perf: tickets notifications add default data migrate file (#13268)
Co-authored-by: feng <1304903146@qq.com>
2024-05-21 19:24:26 +08:00
Bai
4a5205c5ac perf: i18n System Settings -> Features 2024-05-21 16:11:13 +08:00
ibuler
714b4ef7f4 merge: with remote 2024-05-21 14:47:37 +08:00
ibuler
df091f0ee1 perf: 添加 terminal 的 migrate 2024-05-21 14:44:48 +08:00
wangruidong
7037cf56ec perf: i18n 2024-05-21 10:47:15 +08:00
Eric
f683d195e4 perf: update lina i18n 2024-05-20 19:06:28 +08:00
Bai
5ab55b823c perf: i18n System Settings -> General,Org,Notifications 2024-05-20 18:39:57 +08:00
吴小白
0f2c769e8d
Merge pull request #13255 from jumpserver/pr@v4@fix_docker_build
fix: 修正企业镜像缺失依赖
2024-05-20 15:02:56 +08:00
吴小白
1d53f292ae fix: 修正企业镜像缺失依赖 2024-05-20 14:57:15 +08:00
fit2bot
a15335cac9
perf: lina ja translate (#13252)
Co-authored-by: feng <1304903146@qq.com>
2024-05-17 18:46:30 +08:00
wangruidong
f33cf07859 perf: i18n 2024-05-17 18:30:19 +08:00
fit2bot
bce55421ce
perf: MenuAccountTemplates zh translate (#13250)
Co-authored-by: feng <1304903146@qq.com>
2024-05-17 17:56:09 +08:00
fit2bot
c3449cd6bc
perf: account translate (#13249)
Co-authored-by: feng <1304903146@qq.com>
2024-05-17 16:47:02 +08:00
ibuler
4e903ce19b Merge branch 'v4' of github.com:jumpserver/jumpserver into v4 2024-05-17 16:32:08 +08:00
ibuler
90826b358c perf: 修改翻译 2024-05-17 16:31:39 +08:00
吴小白
7d46aa9892 perf: 优化发布机部署 2024-05-17 15:22:16 +08:00
ibuler
49d2bd93b7 perf: merge with remote 2024-05-17 09:59:12 +08:00
ibuler
9f103a88d6 perf: 修改 org name 2024-05-17 09:58:12 +08:00
fit2bot
ce33bdc370
perf: account related translate (#13241)
Co-authored-by: feng <1304903146@qq.com>
2024-05-16 19:39:03 +08:00
fit2bot
cdf1f81c8a
perf: account automation translate (#13240)
Co-authored-by: feng <1304903146@qq.com>
2024-05-16 19:12:24 +08:00
Eric
79edff5fca perf: 支持 loki log 2024-05-16 19:00:47 +08:00
fit2bot
a7316bc7c1
perf: translate (#13228)
Co-authored-by: feng <1304903146@qq.com>
2024-05-15 18:43:31 +08:00
jiangweidong
09f802b00d
perf: cloud sync module adjustment (#13197)
* perf: cloud sync module adjustment

* perf: cloud sync module adjustment
2024-05-14 10:36:17 +08:00
wangruidong
a644b84bb1 perf: dashboard zh,ja,zh_hant i18n 2024-05-13 18:31:54 +08:00
吴小白
b6f48111e3 feat: 更新 Dockerfile 2024-05-13 18:25:06 +08:00
wangruidong
3a6e4e7fb6 perf: file transfer boot step help tips i18n 2024-05-13 18:18:27 +08:00
wangruidong
e42a98ff95 perf: celery task log css optimize 2024-05-13 18:16:36 +08:00
ibuler
8fe511cec6 perf: remove *.mo 2024-05-13 17:35:00 +08:00
Bai
ffb3cd13cb perf: Delete locale dir 2024-05-13 17:26:38 +08:00
Bai
b1abf8a339 perf: Merge local v4 2024-05-13 15:58:59 +08:00
ibuler
89d20c8a4d perf: 修改一些翻译 2024-05-13 15:28:11 +08:00
ibuler
d66f923c0c perf: 修改翻译 2024-05-13 10:40:08 +08:00
ibuler
d3c14428a1 perf: remove an or a 2024-05-13 10:35:21 +08:00
ibuler
c104f85b18 perf: 修改翻译 2024-05-13 10:29:26 +08:00
fit2bot
755d8124ac
perf: account backup translate (#13198)
Co-authored-by: feng <1304903146@qq.com>
2024-05-11 19:48:15 +08:00
Bai
a029cc8ed5 perf: i18n Personal Settings -> All sub menu 2024-05-11 18:55:03 +08:00
ibuler
111dfa8c29 perf: 修改大小写 2024-05-11 16:03:40 +08:00
ibuler
5f892c3afe perf: change i18n 2024-05-11 14:41:58 +08:00
fit2bot
313202fe41
perf: 合并 migrations (#13187)
* perf: 修改 Migrations

* perf: 合并 migrations

* perf: remove unuse

* perf: change to file

---------

Co-authored-by: ibuler <ibuler@qq.com>
2024-05-11 11:22:01 +08:00
Bai
af1adc3baa perf: i18n Personal Settings -> All sub menu 2024-05-11 11:17:02 +08:00
ibuler
be214c84d1 merge: with dev 2024-05-10 18:10:18 +08:00
fit2bot
082614e7b0
perf: A wave of migration Japan, Taiwan China font (#13188)
Co-authored-by: feng <1304903146@qq.com>
2024-05-10 15:13:40 +08:00
wangruidong
83835747c5 perf: i18n 2024-05-10 10:06:44 +08:00
Bai
2a7b48c83d perf: i18n Personal Settings -> Profile 2024-05-09 15:26:38 +08:00
ibuler
a9068496d9 perf: 打算压缩 migratons 2024-05-09 11:01:01 +08:00
fit2bot
8bad88e798
perf: 优化账号相关翻译 (#13177)
Co-authored-by: feng <1304903146@qq.com>
2024-05-08 18:04:46 +08:00
吴小白
9f45eeeb1f fix: 优化构建缓存 2024-05-07 11:43:40 +08:00
吴小白
60110982f1 fix: 修正构建错误 2024-04-30 11:24:17 +08:00
ibuler
259204bfe2 Merge branch 'v4' of github.com:jumpserver/jumpserver into v4 2024-04-30 10:42:31 +08:00
wangruidong
c55e9679db perf: dashboard i18n 2024-04-30 10:31:22 +08:00
ibuler
c05a3c315a Merge branch 'v4' of github.com:jumpserver/jumpserver into v4 2024-04-29 15:07:25 +08:00
ibuler
dbdf586f5b add i18n 2024-04-29 15:06:53 +08:00
Bai
b1bd4db3e9 perf: Update poetry.lock 2024-04-29 11:47:18 +08:00
fit2bot
7806a13db5
feat: 默认数据库使用 PostgreSQL (#13088)
Co-authored-by: 吴小白 <296015668@qq.com>
Co-authored-by: Bryan <jiangjie.bai@fit2cloud.com>
2024-04-29 11:46:16 +08:00
Bai
928f564109 merge: into dev from v4: poetry lock file updated 2024-04-29 11:20:37 +08:00
Bai
328f718fe8 merge: into dev from v4: i18n file 2024-04-29 11:16:09 +08:00
Bai
cb4402c610 merge: into dev from v4 2024-04-29 11:07:43 +08:00
feng626
fbc4cb9046
Revert "perf: remove ticket model" (#13145)
This reverts commit 94567b86f0.
2024-04-28 18:26:49 +08:00
feng
94567b86f0 perf: remove ticket model 2024-04-28 18:02:57 +08:00
ibuler
8aa707427f pref: remove user source choice 2024-04-28 16:01:09 +08:00
ibuler
7d64b8419f Merge branch 'v4' of github.com:jumpserver/jumpserver into v4 2024-04-28 14:04:55 +08:00
ibuler
fad9249810 perf: 再次修改菜单 2024-04-28 14:04:28 +08:00
wangruidong
bb4fbc3a1c perf: index page typo 2024-04-24 18:05:03 +08:00
ibuler
d7916a62f0 perf: 修改翻译 2024-04-23 18:11:05 +08:00
ibuler
da27e1b93c perf: 修改翻译 2024-04-23 14:16:21 +08:00
ibuler
99b24cad00 perf: add open sans font 2024-04-22 15:08:28 +08:00
ibuler
9dbdd6ac60 perf: change i18n 2024-04-18 20:12:01 +08:00
ibuler
f8953441e3 Merge branch 'v4' of github.com:jumpserver/jumpserver into v4 2024-04-17 14:09:25 +08:00
ibuler
5b41eddacc perf: 修改翻译 2024-04-17 13:49:43 +08:00
ibuler
a432af1a6d perf: 修改翻译 2024-04-15 16:34:02 +08:00
吴小白
f987515b89 perf: 优化发布机日志输出 2024-04-15 12:04:26 +08:00
ibuler
2afabd65f9 Merge branch 'v4' of github.com:jumpserver/jumpserver into v4 2024-04-09 18:43:29 +08:00
ibuler
85cb80cbfe perf: remove gpt category 2024-04-09 18:43:06 +08:00
吴小白
8500f186f6 perf: 优化镜像构建 2024-04-08 18:52:22 +08:00
ibuler
97f60a61e0 perf: 修改翻译 2024-04-08 14:32:14 +08:00
Bryan
08ac8b0857
Update README.md 2024-04-02 15:27:28 +08:00
ibuler
255817f5c6 perf: 修改翻译 2024-04-02 14:27:18 +08:00
ibuler
19b196eb1f Merge branch 'v4' of github.com:jumpserver/jumpserver into v4 2024-04-01 19:28:26 +08:00
ibuler
40db6485dd perf: change i18n 2024-04-01 19:28:08 +08:00
Bryan
b23e99885e
Update README.md 2024-04-01 18:47:29 +08:00
Bryan
22fbbb92da
Update README.md 2024-04-01 18:41:34 +08:00
Bryan
99c94166bb
Update README.md 2024-04-01 18:38:59 +08:00
Bryan
169254a1c7
Update README.md 2024-04-01 18:22:11 +08:00
Bryan
bda6037b2a
Update README.md 2024-04-01 18:21:35 +08:00
Bryan
1cf0b15528
Update README.md 2024-04-01 18:08:47 +08:00
Bryan
ff3865d1a7
Update README.md 2024-04-01 18:01:56 +08:00
Bryan
10435788bc
Update README.md 2024-04-01 17:37:11 +08:00
Bai
02750e56d9 perf: Update README.md 2024-04-01 17:09:08 +08:00
ibuler
a1d53cba44 Merge branch 'v4' of github.com:jumpserver/jumpserver into v4 2024-03-29 19:01:01 +08:00
Bai
b921ca8c9d perf: Update README.md 2024-03-29 18:46:11 +08:00
ibuler
29b38632e2 Merge branch 'v4' of github.com:jumpserver/jumpserver into v4 2024-03-29 15:30:31 +08:00
ibuler
56193f833f perf: change some translation 2024-03-29 15:29:04 +08:00
Bryan
2c8b977001
Update README.md 2024-03-28 19:05:42 +08:00
Bryan
4827fcf243
Update README.md 2024-03-28 19:02:39 +08:00
Bryan
9140ed6969
Update README.md 2024-03-28 18:01:16 +08:00
Bryan
24e7597c67
Update README.md 2024-03-28 17:59:37 +08:00
Bryan
833dd654b2
Update README.md 2024-03-28 17:57:02 +08:00
Bryan
ae74154071
Update README.md 2024-03-28 17:52:37 +08:00
Bryan
fb1631c1c7
Update README.md 2024-03-28 17:49:53 +08:00
Bryan
1c6832b9b2
Update README.md 2024-03-28 17:11:55 +08:00
Bryan
77d06037bb
Update README.md 2024-03-28 17:05:12 +08:00
Bai
136e62b97d perf: Update README.md 2024-03-28 17:02:52 +08:00
Bryan
24c36087dd
Update README.md 2024-03-28 16:33:24 +08:00
Bryan
73f9d721fe
Update README.md 2024-03-28 16:10:32 +08:00
Bryan
792f8b2d1f
Update README.md 2024-03-28 16:08:05 +08:00
Bryan
6871d194a8
Update README.md 2024-03-28 16:07:22 +08:00
Bryan
12c26e4551
Update README.md 2024-03-28 15:56:55 +08:00
ibuler
3426f650fa Merge branch 'v4' of github.com:jumpserver/jumpserver into v4 2024-03-28 14:40:31 +08:00
ibuler
f224dc241e perf: 修改翻译 2024-03-28 14:31:27 +08:00
Bryan
f6effb3c40
Update README.md 2024-03-28 14:21:02 +08:00
Bryan
6bbdcc060d
perf: Update README.md 2024-03-28 11:36:24 +08:00
Bryan
14411d8c86
Update README.md 2024-03-28 11:05:04 +08:00
Bryan
cca2bfee4e
perf: Update README.md 2024-03-28 10:42:01 +08:00
Bryan
c6cc68601b
perf: Update README.md 2024-03-28 10:40:48 +08:00
Bai
06f33e4bdc perf: Update README.md 2024-03-27 18:15:29 +08:00
ibuler
616b38158a Merge branch 'v4' of github.com:jumpserver/jumpserver into v4 2024-03-27 17:51:12 +08:00
ibuler
c22f88ae42 perf: 修改翻译 2024-03-27 17:50:44 +08:00
Bai
3bf401f029 perf: Update README.md 2024-03-26 19:34:06 +08:00
Bai
0b8b74b7a4 perf: Update README.md 2024-03-26 18:48:10 +08:00
Bai
e1bd0ee3d7 perf: Update README.md 2024-03-26 18:30:30 +08:00
Bai
4b0d95ed0c perf: Update README.md 2024-03-26 17:40:22 +08:00
Bai
fedb146025 perf: Update README.md 2024-03-26 16:57:52 +08:00
Bai
695a5eb470 perf: Update README.md 2024-03-26 16:53:04 +08:00
ibuler
f6e4d909ff Merge branch 'v4' of github.com:jumpserver/jumpserver into v4 2024-03-22 10:17:36 +08:00
ibuler
6c0299b05a perf: 优化语言更改 2024-03-21 19:13:00 +08:00
Bai
fb02095568 perf: Remove settigns.LITE 2024-03-19 19:22:29 +08:00
Bai
d5675ce498 perf: update poetry.lock 2024-03-19 18:10:02 +08:00
ibuler
ccbb860de1 Merge branch 'v4' of github.com:jumpserver/jumpserver into v4 2024-03-19 17:54:00 +08:00
ibuler
5e104a3dd2 perf: 修改翻译 2024-03-19 17:53:29 +08:00
Bai
51890c94cc perf: 对ce、ee依赖库进行分组 2024-03-19 16:35:15 +08:00
Bai
06259a2d63 perf: 对ce、ee依赖库进行分组 2024-03-19 16:31:45 +08:00
ibuler
d04ac09e82 perf: 修改 groups api 2024-03-15 19:18:49 +08:00
ibuler
cae9f03892 perf: 修改 platform 翻译 2024-03-13 18:40:35 +08:00
ibuler
bffcd6107c perf: 修改翻译 2024-03-12 18:47:44 +08:00
ibuler
056e0c816b perf: 修改翻译 2024-03-12 17:04:26 +08:00
ibuler
ea67312877 perf: 修改翻译 2024-03-11 19:20:18 +08:00
ibuler
327cdc8604 perf: 修改翻译 2024-03-11 14:33:14 +08:00
ibuler
6f37cc4d01 merge: with remote 2024-03-07 18:36:03 +08:00
ibuler
003dd49ed6 perf: 修改翻译 2024-03-07 18:33:13 +08:00
Bai
46d57f02e7 perf: i18n settings-systemtask done. 2024-03-07 17:29:03 +08:00
Bai
30915a93e5 perf: i18n settings-tools done. 2024-03-07 16:15:09 +08:00
Bai
c64480dc33 perf: i18n settings-Interface not done. 2024-03-06 17:49:42 +08:00
ibuler
4a9b1aff96 Merge branch 'v4' of github.com:jumpserver/jumpserver into v4 2024-03-06 17:08:31 +08:00
ibuler
542e94ec9c perf: 修改翻译 2024-03-06 17:08:24 +08:00
fit2bot
9341558f61
perf: translate (#12764)
Co-authored-by: feng <1304903146@qq.com>
2024-03-06 16:51:07 +08:00
ibuler
6ea13b2c0d perf: 修改翻译 2024-03-05 19:01:44 +08:00
ibuler
e57512f4fe perf: 添加配置支持 Lite 2024-03-04 19:18:26 +08:00
ibuler
348f67f4a4 Merge branch 'v4' of github.com:jumpserver/jumpserver into v4 2024-03-04 15:46:18 +08:00
ibuler
83bdf07600 perf: 修改翻译 2024-03-04 15:42:14 +08:00
fit2bot
dfe4eddbbc
perf: translate (#12746)
Co-authored-by: feng <1304903146@qq.com>
Co-authored-by: feng626 <57284900+feng626@users.noreply.github.com>
2024-03-04 14:44:19 +08:00
ibuler
1caed59f76 merge: with remote 2024-03-04 14:03:05 +08:00
Bai
6db4e88a2c perf: 更新 poetry.lock 文件 2024-03-04 11:32:04 +08:00
fit2bot
133daeb664
perf: translate (#12739)
Co-authored-by: feng <1304903146@qq.com>
2024-03-01 15:42:21 +08:00
wangruidong
a4a8d1ecf0 perf: modify menu translate 2024-03-01 13:53:29 +08:00
ibuler
88a08a74f7 perf: 修改翻译 2024-02-29 14:57:38 +08:00
feng
c9e12a3027 perf: Modify some translations 2024-02-28 18:23:10 +08:00
fit2bot
82aa4a65ab
perf: account translate (#12723)
Co-authored-by: feng <1304903146@qq.com>
Co-authored-by: feng626 <57284900+feng626@users.noreply.github.com>
2024-02-28 17:32:29 +08:00
ibuler
d46237f1bf merge: with remote 2024-02-28 16:32:46 +08:00
ibuler
1744f94910 perf: 修改翻译 2024-02-28 16:27:41 +08:00
Bai
e308812429 perf: i18n settings-Security done. 2024-02-27 19:20:48 +08:00
fit2bot
2328ef0b0c
perf: account translate (#12719)
Co-authored-by: feng <1304903146@qq.com>
2024-02-27 19:17:40 +08:00
Bai
000c5770f2 perf: i18n settings-RemoteApp done. 2024-02-27 17:13:59 +08:00
Bai
9e1a3598ab perf: i18n settings-Components done. 2024-02-27 16:43:44 +08:00
Bai
7268f60343 perf: i18n settings-Storage done. 2024-02-27 14:59:22 +08:00
Bai
c8b274031f perf: i18n settings-Auth done. 2024-02-27 11:25:42 +08:00
Bai
10394dbb1c perf: i18n settings-Auth done. 2024-02-26 16:53:40 +08:00
Bai
859bb91fc7 perf: i18n settings-Features done. 2024-02-26 15:47:05 +08:00
fit2bot
0fd0d33704
perf: i18n settings-notifications done. (#12702)
Co-authored-by: Bai <baijiangjie@gmail.com>
Co-authored-by: Bryan <jiangjie.bai@fit2cloud.com>
2024-02-23 18:09:18 +08:00
fit2bot
ad0f489834
perf: translate (#12701)
Co-authored-by: feng <1304903146@qq.com>
2024-02-23 17:52:16 +08:00
fit2bot
b1fa870de7
perf: 工作台相关翻译 (#12700)
Co-authored-by: feng <1304903146@qq.com>
Co-authored-by: feng626 <57284900+feng626@users.noreply.github.com>
2024-02-23 17:22:47 +08:00
Bai
7c5e2ae8ea perf: i18n settings-org done. 2024-02-23 15:17:17 +08:00
Bai
c0e4065a45 perf: i18n settings-basic done. 2024-02-23 14:31:09 +08:00
fit2bot
35448eea9f
perf: 翻译 (#12690)
Co-authored-by: feng <1304903146@qq.com>
2024-02-22 17:46:12 +08:00
wangruidong
430f45a3ec perf: permissions i18n modify 2024-02-22 17:36:45 +08:00
Bai
71b6fd5326 perf: 修改翻译 2024-02-22 17:29:46 +08:00
wangruidong
251db733b2 perf: i18n modify 2024-02-22 10:30:15 +08:00
ibuler
d799725b8a perf: 修改翻译 2024-02-20 19:01:32 +08:00
ibuler
9d80aed468 perf: 修改一些翻译 2024-02-06 15:51:39 +08:00
老广
96f92f0908
Merge pull request #12643 from jumpserver/pr@v4@perf_i18n
perf: 优化翻译
2024-02-05 14:18:48 +08:00
ibuler
314e4301f3 perf: 修改翻译 2024-02-05 14:13:07 +08:00
ibuler
b284bb60f5 merge: with dev 2024-02-05 09:49:43 +08:00
ibuler
f99396ec50 perf: 修改翻译 2024-02-04 19:31:51 +08:00
ibuler
886cf6ed1f perf: 暂存 2024-02-04 16:46:33 +08:00
ibuler
74dd6e97a2 perf: 优化翻译 2024-02-04 10:23:54 +08:00
fit2bot
46fde2f1aa
perf: 整合翻译 (#12630)
* stash

* stash

* perf: 整合翻译

* perf: 整理了一遍

---------

Co-authored-by: ibuler <ibuler@qq.com>
2024-02-01 16:02:31 +08:00
fit2bot
e2a3c360ea
perf: 修改一次性翻译长度 (#12557)
Co-authored-by: feng <1304903146@qq.com>
2024-01-17 11:38:58 +08:00
fit2bot
9968617758
perf: 修改优化结构 (#12554)
* perf: 修改优化结构

* perf: 修改结构

---------

Co-authored-by: ibuler <ibuler@qq.com>
2024-01-17 11:08:39 +08:00
feng
1cec27ed70 perf: 添加进度条 2024-01-17 10:36:45 +08:00
feng
f0dfff0625 feat: gpt translate 2024-01-16 16:19:37 +08:00
fit2bot
fdaec3c959
perf: Modify i18n for settings module. (#12543)
Co-authored-by: Bai <baijiangjie@gmail.com>
2024-01-15 04:41:50 -07:00
ibuler
fcb4c6a972 perf: Add sort json script 2024-01-15 17:12:13 +08:00
ibuler
513974bbed perf: remove some category 2024-01-15 16:38:16 +08:00
ibuler
00d6effd69 pref: stash 2024-01-11 18:33:52 +08:00
ibuler
c06c68d5da perf: 统一翻译 2024-01-11 14:40:44 +08:00
1570 changed files with 125139 additions and 59641 deletions

View File

@ -8,3 +8,6 @@ celerybeat.pid
.vagrant/
apps/xpack/.git
.history/
.idea
.venv/
.env

4
.gitattributes vendored
View File

@ -1,4 +0,0 @@
*.mmdb filter=lfs diff=lfs merge=lfs -text
*.mo filter=lfs diff=lfs merge=lfs -text
*.ipdb filter=lfs diff=lfs merge=lfs -text

10
.github/dependabot.yml vendored Normal file
View File

@ -0,0 +1,10 @@
version: 2
updates:
- package-ecosystem: "uv"
directory: "/"
schedule:
interval: "weekly"
day: "monday"
time: "09:30"
timezone: "Asia/Shanghai"
target-branch: dev

74
.github/workflows/build-base-image.yml vendored Normal file
View File

@ -0,0 +1,74 @@
name: Build and Push Base Image
on:
pull_request:
branches:
- 'dev'
- 'v*'
paths:
- poetry.lock
- pyproject.toml
- Dockerfile-base
- package.json
- go.mod
- yarn.lock
- pom.xml
- install_deps.sh
- utils/clean_site_packages.sh
types:
- opened
- synchronize
- reopened
jobs:
build-and-push:
runs-on: ubuntu-22.04
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
ref: ${{ github.event.pull_request.head.ref }}
- name: Set up QEMU
uses: docker/setup-qemu-action@v3
with:
image: tonistiigi/binfmt:qemu-v7.0.0-28
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to DockerHub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Extract date
id: vars
run: echo "IMAGE_TAG=$(date +'%Y%m%d_%H%M%S')" >> $GITHUB_ENV
- name: Extract repository name
id: repo
run: echo "REPO=$(basename ${{ github.repository }})" >> $GITHUB_ENV
- name: Build and push multi-arch image
uses: docker/build-push-action@v6
with:
platforms: linux/amd64,linux/arm64
push: true
file: Dockerfile-base
tags: jumpserver/core-base:${{ env.IMAGE_TAG }}
- name: Update Dockerfile
run: |
sed -i 's|-base:.* AS stage-build|-base:${{ env.IMAGE_TAG }} AS stage-build|' Dockerfile
- name: Commit changes
run: |
git config --global user.name 'github-actions[bot]'
git config --global user.email 'github-actions[bot]@users.noreply.github.com'
git add Dockerfile
git commit -m "perf: Update Dockerfile with new base image tag"
git push origin ${{ github.event.pull_request.head.ref }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@ -0,0 +1,31 @@
name: Check I18n files CompileMessages
on:
pull_request:
branches:
- 'dev'
paths:
- 'apps/i18n/core/**/*.po'
types:
- opened
- synchronize
- reopened
jobs:
compile-messages-check:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Build and check compilemessages
uses: docker/build-push-action@v6
with:
platforms: linux/amd64
push: false
file: Dockerfile
target: stage-build
tags: jumpserver/core:stage-build

24
.github/workflows/discord-release.yml vendored Normal file
View File

@ -0,0 +1,24 @@
name: Publish Release to Discord
on:
release:
types: [published]
jobs:
send_discord_notification:
runs-on: ubuntu-latest
if: startsWith(github.event.release.tag_name, 'v4.')
steps:
- name: Send release notification to Discord
env:
WEBHOOK_URL: ${{ secrets.DISCORD_CHANGELOG_WEBHOOK }}
run: |
# 获取标签名称和 release body
TAG_NAME="${{ github.event.release.tag_name }}"
RELEASE_BODY="${{ github.event.release.body }}"
# 使用 jq 构建 JSON 数据,以确保安全传递
JSON_PAYLOAD=$(jq -n --arg tag "# JumpServer $TAG_NAME Released! 🚀" --arg body "$RELEASE_BODY" '{content: "\($tag)\n\($body)"}')
# 使用 curl 发送 JSON 数据
curl -X POST -H "Content-Type: application/json" -d "$JSON_PAYLOAD" "$WEBHOOK_URL"

24
.github/workflows/docs-release.yml vendored Normal file
View File

@ -0,0 +1,24 @@
name: Auto update docs changelog
on:
release:
types: [published]
jobs:
update_docs_changelog:
runs-on: ubuntu-latest
if: startsWith(github.event.release.tag_name, 'v4.')
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Update docs changelog
env:
TAG_NAME: ${{ github.event.release.tag_name }}
DOCS_TOKEN: ${{ secrets.DOCS_TOKEN }}
run: |
git config --global user.name 'BaiJiangJie'
git config --global user.email 'jiangjie.bai@fit2cloud.com'
git clone https://$DOCS_TOKEN@github.com/jumpservice/documentation.git
cd documentation/utils
bash update_changelog.sh

View File

@ -1,55 +0,0 @@
name: "Run Build Test"
on:
push:
paths:
- 'Dockerfile'
- 'Dockerfile-*'
- 'pyproject.toml'
- 'poetry.lock'
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: docker/setup-qemu-action@v3
- uses: docker/setup-buildx-action@v3
- name: Check Dockerfile
run: |
test -f Dockerfile-ce || cp -f Dockerfile Dockerfile-ce
- name: Build CE Image
uses: docker/build-push-action@v5
with:
context: .
push: false
file: Dockerfile-ce
tags: jumpserver/core-ce:test
platforms: linux/amd64
build-args: |
APT_MIRROR=http://deb.debian.org
PIP_MIRROR=https://pypi.org/simple
PIP_JMS_MIRROR=https://pypi.org/simple
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Prepare EE Image
run: |
sed -i 's@^FROM registry.fit2cloud.com@# FROM registry.fit2cloud.com@g' Dockerfile-ee
sed -i 's@^COPY --from=build-xpack@# COPY --from=build-xpack@g' Dockerfile-ee
- name: Build EE Image
uses: docker/build-push-action@v5
with:
context: .
push: false
file: Dockerfile-ee
tags: jumpserver/core-ee:test
platforms: linux/amd64
build-args: |
APT_MIRROR=http://deb.debian.org
PIP_MIRROR=https://pypi.org/simple
PIP_JMS_MIRROR=https://pypi.org/simple
cache-from: type=gha
cache-to: type=gha,mode=max

View File

@ -0,0 +1,63 @@
name: "Run Build Test"
on:
push:
paths:
- 'Dockerfile'
- 'Dockerfile*'
- 'Dockerfile-*'
- 'pyproject.toml'
- 'poetry.lock'
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
component: [core]
version: [v4]
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.repository_owner }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Prepare Build
run: |
sed -i 's@^FROM registry.fit2cloud.com/jumpserver@FROM ghcr.io/jumpserver@g' Dockerfile-ee
- name: Build CE Image
uses: docker/build-push-action@v5
with:
context: .
push: true
file: Dockerfile
tags: ghcr.io/jumpserver/${{ matrix.component }}:${{ matrix.version }}-ce
platforms: linux/amd64
build-args: |
VERSION=${{ matrix.version }}
APT_MIRROR=http://deb.debian.org
PIP_MIRROR=https://pypi.org/simple
outputs: type=image,oci-mediatypes=true,compression=zstd,compression-level=3,force-compression=true
cache-from: type=gha
cache-to: type=gha,mode=max
- name: Build EE Image
uses: docker/build-push-action@v5
with:
context: .
push: false
file: Dockerfile-ee
tags: ghcr.io/jumpserver/${{ matrix.component }}:${{ matrix.version }}
platforms: linux/amd64
build-args: |
VERSION=${{ matrix.version }}
APT_MIRROR=http://deb.debian.org
PIP_MIRROR=https://pypi.org/simple
outputs: type=image,oci-mediatypes=true,compression=zstd,compression-level=3,force-compression=true
cache-from: type=gha
cache-to: type=gha,mode=max

View File

@ -0,0 +1,28 @@
name: LLM Code Review
permissions:
contents: read
pull-requests: write
on:
pull_request:
types: [opened, reopened, synchronize]
jobs:
llm-code-review:
runs-on: ubuntu-latest
steps:
- uses: fit2cloud/LLM-CodeReview-Action@main
env:
GITHUB_TOKEN: ${{ secrets.FIT2CLOUDRD_LLM_CODE_REVIEW_TOKEN }}
OPENAI_API_KEY: ${{ secrets.ALIYUN_LLM_API_KEY }}
LANGUAGE: English
OPENAI_API_ENDPOINT: https://dashscope.aliyuncs.com/compatible-mode/v1
MODEL: qwen2-1.5b-instruct
PROMPT: "Please check the following code differences for any irregularities, potential issues, or optimization suggestions, and provide your answers in English."
top_p: 1
temperature: 1
# max_tokens: 10000
MAX_PATCH_LENGTH: 10000
IGNORE_PATTERNS: "/node_modules,*.md,/dist,/.github"
FILE_PATTERNS: "*.java,*.go,*.py,*.vue,*.ts,*.js,*.css,*.scss,*.html"

45
.github/workflows/translate-readme.yml vendored Normal file
View File

@ -0,0 +1,45 @@
name: Translate README
on:
workflow_dispatch:
inputs:
source_readme:
description: "Source README"
required: false
default: "./readmes/README.en.md"
target_langs:
description: "Target Languages"
required: false
default: "zh-hans,zh-hant,ja,pt-br,es,ru"
gen_dir_path:
description: "Generate Dir Name"
required: false
default: "readmes/"
push_branch:
description: "Push Branch"
required: false
default: "pr@dev@translate_readme"
prompt:
description: "AI Translate Prompt"
required: false
default: ""
gpt_mode:
description: "GPT Mode"
required: false
default: "gpt-4o-mini"
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Auto Translate
uses: jumpserver-dev/action-translate-readme@main
env:
GITHUB_TOKEN: ${{ secrets.PRIVATE_TOKEN }}
OPENAI_API_KEY: ${{ secrets.GPT_API_TOKEN }}
GPT_MODE: ${{ github.event.inputs.gpt_mode }}
SOURCE_README: ${{ github.event.inputs.source_readme }}
TARGET_LANGUAGES: ${{ github.event.inputs.target_langs }}
PUSH_BRANCH: ${{ github.event.inputs.push_branch }}
GEN_DIR_PATH: ${{ github.event.inputs.gen_dir_path }}
PROMPT: ${{ github.event.inputs.prompt }}

5
.gitignore vendored
View File

@ -44,3 +44,8 @@ data/*
test.py
.history/
.test/
*.mo
apps.iml
*.db
*.mmdb
*.ipdb

View File

@ -1,3 +1,4 @@
[settings]
line_length=120
known_first_party=common,users,assets,perms,authentication,jumpserver,notification,ops,orgs,rbac,settings,terminal,tickets

2
.pylintrc Normal file
View File

@ -0,0 +1,2 @@
[MESSAGES CONTROL]
disable=missing-module-docstring,missing-class-docstring,missing-function-docstring,too-many-ancestors

View File

@ -1,5 +1,10 @@
# Contributing
As a contributor, you should agree that:
- The producer can adjust the open-source agreement to be more strict or relaxed as deemed necessary.
- Your contributed code may be used for commercial purposes, including but not limited to its cloud business operations.
## Create pull request
PR are always welcome, even if they only contain small fixes like typos or a few lines of code. If there will be a significant effort, please document it as an issue and get a discussion going before starting to work on it.

68
Dockerfile Normal file
View File

@ -0,0 +1,68 @@
FROM jumpserver/core-base:20250427_062456 AS stage-build
ARG VERSION
WORKDIR /opt/jumpserver
ADD . .
RUN echo > /opt/jumpserver/config.yml \
&& \
if [ -n "${VERSION}" ]; then \
sed -i "s@VERSION = .*@VERSION = '${VERSION}'@g" apps/jumpserver/const.py; \
fi
RUN set -ex \
&& export SECRET_KEY=$(head -c100 < /dev/urandom | base64 | tr -dc A-Za-z0-9 | head -c 48) \
&& . /opt/py3/bin/activate \
&& cd apps \
&& python manage.py compilemessages
FROM python:3.11-slim-bullseye
ENV LANG=en_US.UTF-8 \
PATH=/opt/py3/bin:$PATH
ARG DEPENDENCIES=" \
libldap2-dev \
libx11-dev"
ARG TOOLS=" \
cron \
ca-certificates \
default-libmysqlclient-dev \
openssh-client \
sshpass \
bubblewrap"
ARG APT_MIRROR=http://deb.debian.org
RUN set -ex \
&& sed -i "s@http://.*.debian.org@${APT_MIRROR}@g" /etc/apt/sources.list \
&& ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
&& apt-get update > /dev/null \
&& apt-get -y install --no-install-recommends ${DEPENDENCIES} \
&& apt-get -y install --no-install-recommends ${TOOLS} \
&& mkdir -p /root/.ssh/ \
&& echo "Host *\n\tStrictHostKeyChecking no\n\tUserKnownHostsFile /dev/null\n\tCiphers +aes128-cbc\n\tKexAlgorithms +diffie-hellman-group1-sha1\n\tHostKeyAlgorithms +ssh-rsa" > /root/.ssh/config \
&& echo "no" | dpkg-reconfigure dash \
&& apt-get clean all \
&& rm -rf /var/lib/apt/lists/* \
&& echo "0 3 * * * root find /tmp -type f -mtime +1 -size +1M -exec rm -f {} \; && date > /tmp/clean.log" > /etc/cron.d/cleanup_tmp \
&& chmod 0644 /etc/cron.d/cleanup_tmp
COPY --from=stage-build /opt /opt
COPY --from=stage-build /usr/local/bin /usr/local/bin
COPY --from=stage-build /opt/jumpserver/apps/libs/ansible/ansible.cfg /etc/ansible/
WORKDIR /opt/jumpserver
VOLUME /opt/jumpserver/data
ENTRYPOINT ["./entrypoint.sh"]
EXPOSE 8080
STOPSIGNAL SIGQUIT
CMD ["start", "all"]

61
Dockerfile-base Normal file
View File

@ -0,0 +1,61 @@
FROM python:3.11-slim-bullseye
ARG TARGETARCH
COPY --from=ghcr.io/astral-sh/uv:0.6.14 /uv /uvx /usr/local/bin/
# Install APT dependencies
ARG DEPENDENCIES=" \
ca-certificates \
wget \
g++ \
make \
pkg-config \
default-libmysqlclient-dev \
freetds-dev \
gettext \
libkrb5-dev \
libldap2-dev \
libsasl2-dev"
ARG APT_MIRROR=http://deb.debian.org
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked,id=core \
--mount=type=cache,target=/var/lib/apt,sharing=locked,id=core \
set -ex \
&& rm -f /etc/apt/apt.conf.d/docker-clean \
&& echo 'Binary::apt::APT::Keep-Downloaded-Packages "true";' > /etc/apt/apt.conf.d/keep-cache \
&& sed -i "s@http://.*.debian.org@${APT_MIRROR}@g" /etc/apt/sources.list \
&& apt-get update > /dev/null \
&& apt-get -y install --no-install-recommends ${DEPENDENCIES} \
&& echo "no" | dpkg-reconfigure dash
# Install bin tools
ARG CHECK_VERSION=v1.0.4
RUN set -ex \
&& wget https://github.com/jumpserver-dev/healthcheck/releases/download/${CHECK_VERSION}/check-${CHECK_VERSION}-linux-${TARGETARCH}.tar.gz \
&& tar -xf check-${CHECK_VERSION}-linux-${TARGETARCH}.tar.gz \
&& mv check /usr/local/bin/ \
&& chown root:root /usr/local/bin/check \
&& chmod 755 /usr/local/bin/check \
&& rm -f check-${CHECK_VERSION}-linux-${TARGETARCH}.tar.gz
# Install Python dependencies
WORKDIR /opt/jumpserver
ARG PIP_MIRROR=https://pypi.org/simple
ENV POETRY_PYPI_MIRROR_URL=${PIP_MIRROR}
ENV ANSIBLE_COLLECTIONS_PATHS=/opt/py3/lib/python3.11/site-packages/ansible_collections
ENV LANG=en_US.UTF-8 \
PATH=/opt/py3/bin:$PATH
ENV UV_LINK_MODE=copy
RUN --mount=type=cache,target=/root/.cache \
--mount=type=bind,source=pyproject.toml,target=pyproject.toml \
--mount=type=bind,source=requirements/clean_site_packages.sh,target=clean_site_packages.sh \
--mount=type=bind,source=requirements/collections.yml,target=collections.yml \
--mount=type=bind,source=requirements/static_files.sh,target=utils/static_files.sh \
set -ex \
&& uv venv \
&& uv pip install -i${PIP_MIRROR} -r pyproject.toml \
&& ln -sf $(pwd)/.venv /opt/py3 \
&& bash utils/static_files.sh \
&& bash clean_site_packages.sh

View File

@ -1,137 +0,0 @@
FROM python:3.11-slim-bullseye as stage-1
ARG TARGETARCH
ARG VERSION
ENV VERSION=$VERSION
WORKDIR /opt/jumpserver
ADD . .
RUN echo > /opt/jumpserver/config.yml \
&& cd utils && bash -ixeu build.sh
FROM python:3.11-slim-bullseye as stage-2
ARG TARGETARCH
ARG BUILD_DEPENDENCIES=" \
g++ \
make \
pkg-config"
ARG DEPENDENCIES=" \
freetds-dev \
libffi-dev \
libjpeg-dev \
libkrb5-dev \
libldap2-dev \
libpq-dev \
libsasl2-dev \
libssl-dev \
libxml2-dev \
libxmlsec1-dev \
libxmlsec1-openssl \
freerdp2-dev \
libaio-dev"
ARG TOOLS=" \
ca-certificates \
curl \
default-libmysqlclient-dev \
default-mysql-client \
git \
git-lfs \
unzip \
xz-utils \
wget"
ARG APT_MIRROR=http://mirrors.ustc.edu.cn
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked,id=core-apt \
--mount=type=cache,target=/var/lib/apt,sharing=locked,id=core-apt \
sed -i "s@http://.*.debian.org@${APT_MIRROR}@g" /etc/apt/sources.list \
&& rm -f /etc/apt/apt.conf.d/docker-clean \
&& ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
&& apt-get update \
&& apt-get -y install --no-install-recommends ${BUILD_DEPENDENCIES} \
&& apt-get -y install --no-install-recommends ${DEPENDENCIES} \
&& apt-get -y install --no-install-recommends ${TOOLS} \
&& echo "no" | dpkg-reconfigure dash
WORKDIR /opt/jumpserver
ARG PIP_MIRROR=https://pypi.tuna.tsinghua.edu.cn/simple
RUN --mount=type=cache,target=/root/.cache \
--mount=type=bind,source=poetry.lock,target=/opt/jumpserver/poetry.lock \
--mount=type=bind,source=pyproject.toml,target=/opt/jumpserver/pyproject.toml \
set -ex \
&& python3 -m venv /opt/py3 \
&& pip install poetry -i ${PIP_MIRROR} \
&& poetry config virtualenvs.create false \
&& . /opt/py3/bin/activate \
&& poetry install
FROM python:3.11-slim-bullseye
ARG TARGETARCH
ENV LANG=zh_CN.UTF-8 \
PATH=/opt/py3/bin:$PATH
ARG DEPENDENCIES=" \
libjpeg-dev \
libpq-dev \
libx11-dev \
freerdp2-dev \
libxmlsec1-openssl"
ARG TOOLS=" \
ca-certificates \
curl \
default-libmysqlclient-dev \
default-mysql-client \
iputils-ping \
locales \
netcat-openbsd \
nmap \
openssh-client \
patch \
sshpass \
telnet \
vim \
bubblewrap \
wget"
ARG APT_MIRROR=http://mirrors.ustc.edu.cn
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked,id=core-apt \
--mount=type=cache,target=/var/lib/apt,sharing=locked,id=core-apt \
sed -i "s@http://.*.debian.org@${APT_MIRROR}@g" /etc/apt/sources.list \
&& rm -f /etc/apt/apt.conf.d/docker-clean \
&& ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime \
&& apt-get update \
&& apt-get -y install --no-install-recommends ${DEPENDENCIES} \
&& apt-get -y install --no-install-recommends ${TOOLS} \
&& mkdir -p /root/.ssh/ \
&& echo "Host *\n\tStrictHostKeyChecking no\n\tUserKnownHostsFile /dev/null\n\tCiphers +aes128-cbc\n\tKexAlgorithms +diffie-hellman-group1-sha1\n\tHostKeyAlgorithms +ssh-rsa" > /root/.ssh/config \
&& echo "no" | dpkg-reconfigure dash \
&& echo "zh_CN.UTF-8" | dpkg-reconfigure locales \
&& sed -i "s@# export @export @g" ~/.bashrc \
&& sed -i "s@# alias @alias @g" ~/.bashrc
ARG RECEPTOR_VERSION=v1.4.5
RUN set -ex \
&& wget -O /opt/receptor.tar.gz https://github.com/ansible/receptor/releases/download/${RECEPTOR_VERSION}/receptor_${RECEPTOR_VERSION/v/}_linux_${TARGETARCH}.tar.gz \
&& tar -xf /opt/receptor.tar.gz -C /usr/local/bin/ \
&& chown root:root /usr/local/bin/receptor \
&& chmod 755 /usr/local/bin/receptor \
&& rm -f /opt/receptor.tar.gz
COPY --from=stage-2 /opt/py3 /opt/py3
COPY --from=stage-1 /opt/jumpserver/release/jumpserver /opt/jumpserver
COPY --from=stage-1 /opt/jumpserver/release/jumpserver/apps/libs/ansible/ansible.cfg /etc/ansible/
WORKDIR /opt/jumpserver
ARG VERSION
ENV VERSION=$VERSION
VOLUME /opt/jumpserver/data
EXPOSE 8080
ENTRYPOINT ["./entrypoint.sh"]

View File

@ -1,5 +1,30 @@
ARG VERSION
FROM registry.fit2cloud.com/jumpserver/xpack:${VERSION} as build-xpack
FROM registry.fit2cloud.com/jumpserver/core-ce:${VERSION}
ARG VERSION=dev
FROM registry.fit2cloud.com/jumpserver/xpack:${VERSION} AS build-xpack
FROM jumpserver/core:${VERSION}-ce
COPY --from=build-xpack /opt/xpack /opt/jumpserver/apps/xpack
ARG TOOLS=" \
g++ \
curl \
iputils-ping \
netcat-openbsd \
nmap \
telnet \
vim \
wget"
RUN set -ex \
&& apt-get update \
&& apt-get -y install --no-install-recommends ${TOOLS} \
&& apt-get clean all \
&& rm -rf /var/lib/apt/lists/*
WORKDIR /opt/jumpserver
ARG PIP_MIRROR=https://pypi.org/simple
RUN set -ex \
&& uv pip install -i${PIP_MIRROR} --group xpack
COPY --from=build-xpack /opt/xpack /opt/jumpserver/apps/xpack

199
README.md
View File

@ -1,125 +1,118 @@
<p align="center">
<a href="https://jumpserver.org"><img src="https://download.jumpserver.org/images/jumpserver-logo.svg" alt="JumpServer" width="300" /></a>
</p>
<h3 align="center">广受欢迎的开源堡垒机</h3>
<div align="center">
<a name="readme-top"></a>
<a href="https://jumpserver.com" target="_blank"><img src="https://download.jumpserver.org/images/jumpserver-logo.svg" alt="JumpServer" width="300" /></a>
## An open-source PAM tool (Bastion Host)
<p align="center">
<a href="https://www.gnu.org/licenses/gpl-3.0.html"><img src="https://img.shields.io/github/license/jumpserver/jumpserver" alt="License: GPLv3"></a>
<a href="https://hub.docker.com/u/jumpserver"><img src="https://img.shields.io/docker/pulls/jumpserver/jms_all.svg" alt="Docker pulls"></a>
<a href="https://github.com/jumpserver/jumpserver/releases/latest"><img src="https://img.shields.io/github/v/release/jumpserver/jumpserver" alt="Latest release"></a>
<a href="https://github.com/jumpserver/jumpserver"><img src="https://img.shields.io/github/stars/jumpserver/jumpserver?color=%231890FF&style=flat-square" alt="Stars"></a>
</p>
[![][license-shield]][license-link]
[![][discord-shield]][discord-link]
[![][docker-shield]][docker-link]
[![][github-release-shield]][github-release-link]
[![][github-stars-shield]][github-stars-link]
[English](/README.md) · [中文(简体)](/readmes/README.zh-hans.md) · [中文(繁體)](/readmes/README.zh-hant.md) · [日本語](/readmes/README.ja.md) · [Português (Brasil)](/readmes/README.pt-br.md) · [Español](/readmes/README.es.md) · [Русский](/readmes/README.ru.md)
</div>
<br/>
## What is JumpServer?
JumpServer is an open-source Privileged Access Management (PAM) tool that provides DevOps and IT teams with on-demand and secure access to SSH, RDP, Kubernetes, Database and RemoteApp endpoints through a web browser.
<p align="center">
9 年时间,倾情投入,用心做好一款开源堡垒机。
</p>
<picture>
<source media="(prefers-color-scheme: light)" srcset="https://github.com/user-attachments/assets/dd612f3d-c958-4f84-b164-f31b75454d7f">
<source media="(prefers-color-scheme: dark)" srcset="https://github.com/user-attachments/assets/28676212-2bc4-4a9f-ae10-3be9320647e3">
<img src="https://github.com/user-attachments/assets/dd612f3d-c958-4f84-b164-f31b75454d7f" alt="Theme-based Image">
</picture>
------------------------------
JumpServer 是广受欢迎的开源堡垒机,是符合 4A 规范的专业运维安全审计系统。
JumpServer 堡垒机帮助企业以更安全的方式管控和登录各种类型的资产,包括:
## Quickstart
- **SSH**: Linux / Unix / 网络设备 等;
- **Windows**: Web 方式连接 / 原生 RDP 连接;
- **数据库**: MySQL / MariaDB / PostgreSQL / Oracle / SQLServer / ClickHouse 等;
- **NoSQL**: Redis / MongoDB 等;
- **GPT**: ChatGPT 等;
- **云服务**: Kubernetes / VMware vSphere 等;
- **Web 站点**: 各类系统的 Web 管理后台;
- **应用**: 通过 Remote App 连接各类应用。
Prepare a clean Linux Server ( 64 bit, >= 4c8g )
## 产品特色
```sh
curl -sSL https://github.com/jumpserver/jumpserver/releases/latest/download/quick_start.sh | bash
```
- **开源**: 零门槛,线上快速获取和安装;
- **无插件**: 仅需浏览器,极致的 Web Terminal 使用体验;
- **分布式**: 支持分布式部署和横向扩展,轻松支持大规模并发访问;
- **多云支持**: 一套系统,同时管理不同云上面的资产;
- **多租户**: 一套系统,多个子公司或部门同时使用;
- **云端存储**: 审计录像云端存储,永不丢失;
Access JumpServer in your browser at `http://your-jumpserver-ip/`
- Username: `admin`
- Password: `ChangeMe`
## UI 展示
[![JumpServer Quickstart](https://github.com/user-attachments/assets/0f32f52b-9935-485e-8534-336c63389612)](https://www.youtube.com/watch?v=UlGYRbKrpgY "JumpServer Quickstart")
![UI展示](https://docs.jumpserver.org/zh/v3/img/dashboard.png)
## Screenshots
<table style="border-collapse: collapse; border: 1px solid black;">
<tr>
<td style="padding: 5px;background-color:#fff;"><img src= "https://github.com/jumpserver/jumpserver/assets/32935519/99fabe5b-0475-4a53-9116-4c370a1426c4" alt="JumpServer Console" /></td>
<td style="padding: 5px;background-color:#fff;"><img src= "https://github.com/user-attachments/assets/7c1f81af-37e8-4f07-8ac9-182895e1062e" alt="JumpServer PAM" /></td>    
</tr>
<tr>
<td style="padding: 5px;background-color:#fff;"><img src= "https://github.com/jumpserver/jumpserver/assets/32935519/a424d731-1c70-4108-a7d8-5bbf387dda9a" alt="JumpServer Audits" /></td>
<td style="padding: 5px;background-color:#fff;"><img src= "https://github.com/jumpserver/jumpserver/assets/32935519/393d2c27-a2d0-4dea-882d-00ed509e00c9" alt="JumpServer Workbench" /></td>
</tr>
<tr>
<td style="padding: 5px;background-color:#fff;"><img src= "https://github.com/user-attachments/assets/eaa41f66-8cc8-4f01-a001-0d258501f1c9" alt="JumpServer RBAC" /></td>     
<td style="padding: 5px;background-color:#fff;"><img src= "https://github.com/jumpserver/jumpserver/assets/32935519/3a2611cd-8902-49b8-b82b-2a6dac851f3e" alt="JumpServer Settings" /></td>
</tr>
<tr>
<td style="padding: 5px;background-color:#fff;"><img src= "https://github.com/jumpserver/jumpserver/assets/32935519/1e236093-31f7-4563-8eb1-e36d865f1568" alt="JumpServer SSH" /></td>
<td style="padding: 5px;background-color:#fff;"><img src= "https://github.com/jumpserver/jumpserver/assets/32935519/69373a82-f7ab-41e8-b763-bbad2ba52167" alt="JumpServer RDP" /></td>
</tr>
<tr>
<td style="padding: 5px;background-color:#fff;"><img src= "https://github.com/jumpserver/jumpserver/assets/32935519/5bed98c6-cbe8-4073-9597-d53c69dc3957" alt="JumpServer K8s" /></td>
<td style="padding: 5px;background-color:#fff;"><img src= "https://github.com/jumpserver/jumpserver/assets/32935519/b80ad654-548f-42bc-ba3d-c1cfdf1b46d6" alt="JumpServer DB" /></td>
</tr>
</table>
## 在线体验
## Components
- 环境地址:<https://demo.jumpserver.org/>
JumpServer consists of multiple key components, which collectively form the functional framework of JumpServer, providing users with comprehensive capabilities for operations management and security control.
| :warning: 注意 |
|:-----------------------------|
| 该环境仅作体验目的使用,我们会定时清理、重置数据! |
| 请勿修改体验环境用户的密码! |
| 请勿在环境中添加业务生产环境地址、用户名密码等敏感信息! |
| Project | Status | Description |
|--------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------|
| [Lina](https://github.com/jumpserver/lina) | <a href="https://github.com/jumpserver/lina/releases"><img alt="Lina release" src="https://img.shields.io/github/release/jumpserver/lina.svg" /></a> | JumpServer Web UI |
| [Luna](https://github.com/jumpserver/luna) | <a href="https://github.com/jumpserver/luna/releases"><img alt="Luna release" src="https://img.shields.io/github/release/jumpserver/luna.svg" /></a> | JumpServer Web Terminal |
| [KoKo](https://github.com/jumpserver/koko) | <a href="https://github.com/jumpserver/koko/releases"><img alt="Koko release" src="https://img.shields.io/github/release/jumpserver/koko.svg" /></a> | JumpServer Character Protocol Connector |
| [Lion](https://github.com/jumpserver/lion) | <a href="https://github.com/jumpserver/lion/releases"><img alt="Lion release" src="https://img.shields.io/github/release/jumpserver/lion.svg" /></a> | JumpServer Graphical Protocol Connector |
| [Chen](https://github.com/jumpserver/chen) | <a href="https://github.com/jumpserver/chen/releases"><img alt="Chen release" src="https://img.shields.io/github/release/jumpserver/chen.svg" /> | JumpServer Web DB |
| [Tinker](https://github.com/jumpserver/tinker) | <img alt="Tinker" src="https://img.shields.io/badge/release-private-red" /> | JumpServer Remote Application Connector (Windows) |
| [Panda](https://github.com/jumpserver/Panda) | <img alt="Panda" src="https://img.shields.io/badge/release-private-red" /> | JumpServer EE Remote Application Connector (Linux) |
| [Razor](https://github.com/jumpserver/razor) | <img alt="Chen" src="https://img.shields.io/badge/release-private-red" /> | JumpServer EE RDP Proxy Connector |
| [Magnus](https://github.com/jumpserver/magnus) | <img alt="Magnus" src="https://img.shields.io/badge/release-private-red" /> | JumpServer EE Database Proxy Connector |
| [Nec](https://github.com/jumpserver/nec) | <img alt="Nec" src="https://img.shields.io/badge/release-private-red" /> | JumpServer EE VNC Proxy Connector |
| [Facelive](https://github.com/jumpserver/facelive) | <img alt="Facelive" src="https://img.shields.io/badge/release-private-red" /> | JumpServer EE Facial Recognition |
## 快速开始
- [快速入门](https://docs.jumpserver.org/zh/v3/quick_start/)
- [产品文档](https://docs.jumpserver.org)
- [在线学习](https://edu.fit2cloud.com/page/2635362)
- [知识库](https://kb.fit2cloud.com/categories/jumpserver)
## Contributing
## 案例研究
Welcome to submit PR to contribute. Please refer to [CONTRIBUTING.md][contributing-link] for guidelines.
- [腾讯音乐娱乐集团基于JumpServer的安全运维审计解决方案](https://blog.fit2cloud.com/?p=a04cdf0d-6704-4d18-9b40-9180baecd0e2)
- [腾讯海外游戏基于JumpServer构建游戏安全运营能力](https://blog.fit2cloud.com/?p=3704)
- [万华化学通过JumpServer管理全球化分布式IT资产并且实现与云管平台的联动](https://blog.fit2cloud.com/?p=3504)
- [雪花啤酒JumpServer堡垒机使用体会](https://blog.fit2cloud.com/?p=3412)
- [顺丰科技JumpServer 堡垒机护航顺丰科技超大规模资产安全运维](https://blog.fit2cloud.com/?p=1147)
- [沐瞳游戏通过JumpServer管控多项目分布式资产](https://blog.fit2cloud.com/?p=3213)
- [携程JumpServer 堡垒机部署与运营实战](https://blog.fit2cloud.com/?p=851)
- [大智慧JumpServer 堡垒机让“大智慧”的混合 IT 运维更智慧](https://blog.fit2cloud.com/?p=882)
- [小红书JumpServer 堡垒机大规模资产跨版本迁移之路](https://blog.fit2cloud.com/?p=516)
- [中手游JumpServer堡垒机助力中手游提升多云环境下安全运维能力](https://blog.fit2cloud.com/?p=732)
- [中通快递JumpServer主机安全运维实践](https://blog.fit2cloud.com/?p=708)
- [东方明珠JumpServer高效管控异构化、分布式云端资产](https://blog.fit2cloud.com/?p=687)
- [江苏农信JumpServer堡垒机助力行业云安全运维](https://blog.fit2cloud.com/?p=666)
## License
## 社区交流
Copyright (c) 2014-2025 FIT2CLOUD, All rights reserved.
如果您在使用过程中有任何疑问或对建议,欢迎提交 [GitHub Issue](https://github.com/jumpserver/jumpserver/issues/new/choose)。
您也可以到我们的 [社区论坛](https://bbs.fit2cloud.com/c/js/5) 当中进行交流沟通。
### 参与贡献
欢迎提交 PR 参与贡献。 参考 [CONTRIBUTING.md](https://github.com/jumpserver/jumpserver/blob/dev/CONTRIBUTING.md)
## 组件项目
| 项目 | 状态 | 描述 |
|--------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------|-----------------------------------------------------------------------------------|
| [Lina](https://github.com/jumpserver/lina) | <a href="https://github.com/jumpserver/lina/releases"><img alt="Lina release" src="https://img.shields.io/github/release/jumpserver/lina.svg" /></a> | JumpServer Web UI 项目 |
| [Luna](https://github.com/jumpserver/luna) | <a href="https://github.com/jumpserver/luna/releases"><img alt="Luna release" src="https://img.shields.io/github/release/jumpserver/luna.svg" /></a> | JumpServer Web Terminal 项目 |
| [KoKo](https://github.com/jumpserver/koko) | <a href="https://github.com/jumpserver/koko/releases"><img alt="Koko release" src="https://img.shields.io/github/release/jumpserver/koko.svg" /></a> | JumpServer 字符协议 Connector 项目 |
| [Lion](https://github.com/jumpserver/lion-release) | <a href="https://github.com/jumpserver/lion-release/releases"><img alt="Lion release" src="https://img.shields.io/github/release/jumpserver/lion-release.svg" /></a> | JumpServer 图形协议 Connector 项目,依赖 [Apache Guacamole](https://guacamole.apache.org/) |
| [Razor](https://github.com/jumpserver/razor) | <img alt="Chen" src="https://img.shields.io/badge/release-私有发布-red" /> | JumpServer RDP 代理 Connector 项目 |
| [Tinker](https://github.com/jumpserver/tinker) | <img alt="Tinker" src="https://img.shields.io/badge/release-私有发布-red" /> | JumpServer 远程应用 Connector 项目 (Windows) |
| [Panda](https://github.com/jumpserver/Panda) | <img alt="Panda" src="https://img.shields.io/badge/release-私有发布-red" /> | JumpServer 远程应用 Connector 项目 (Linux) |
| [Magnus](https://github.com/jumpserver/magnus-release) | <a href="https://github.com/jumpserver/magnus-release/releases"><img alt="Magnus release" src="https://img.shields.io/github/release/jumpserver/magnus-release.svg" /> | JumpServer 数据库代理 Connector 项目 |
| [Chen](https://github.com/jumpserver/chen-release) | <a href="https://github.com/jumpserver/chen-release/releases"><img alt="Chen release" src="https://img.shields.io/github/release/jumpserver/chen-release.svg" /> | JumpServer Web DB 项目,替代原来的 OmniDB |
| [Kael](https://github.com/jumpserver/kael) | <a href="https://github.com/jumpserver/kael/releases"><img alt="Kael release" src="https://img.shields.io/github/release/jumpserver/kael.svg" /> | JumpServer 连接 GPT 资产的组件项目 |
| [Wisp](https://github.com/jumpserver/wisp) | <a href="https://github.com/jumpserver/wisp/releases"><img alt="Magnus release" src="https://img.shields.io/github/release/jumpserver/wisp.svg" /> | JumpServer 各系统终端组件和 Core API 通信的组件项目 |
| [Clients](https://github.com/jumpserver/clients) | <a href="https://github.com/jumpserver/clients/releases"><img alt="Clients release" src="https://img.shields.io/github/release/jumpserver/clients.svg" /> | JumpServer 客户端 项目 |
| [Installer](https://github.com/jumpserver/installer) | <a href="https://github.com/jumpserver/installer/releases"><img alt="Installer release" src="https://img.shields.io/github/release/jumpserver/installer.svg" /> | JumpServer 安装包 项目 |
## 安全说明
JumpServer是一款安全产品请参考 [基本安全建议](https://docs.jumpserver.org/zh/master/install/install_security/)
进行安装部署。如果您发现安全相关问题,请直接联系我们:
- 邮箱support@fit2cloud.com
- 电话400-052-0755
## License & Copyright
Copyright (c) 2014-2024 飞致云 FIT2CLOUD, All rights reserved.
Licensed under The GNU General Public License version 3 (GPLv3) (the "License"); you may not use this file except in
compliance with the License. You may obtain a copy of the License at
Licensed under The GNU General Public License version 3 (GPLv3) (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
https://www.gnu.org/licenses/gpl-3.0.html
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "
AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific
language governing permissions and limitations under the License.
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an " AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
<!-- JumpServer official link -->
[docs-link]: https://jumpserver.com/docs
[discord-link]: https://discord.com/invite/W6vYXmAQG2
[contributing-link]: https://github.com/jumpserver/jumpserver/blob/dev/CONTRIBUTING.md
<!-- JumpServer Other link-->
[license-link]: https://www.gnu.org/licenses/gpl-3.0.html
[docker-link]: https://hub.docker.com/u/jumpserver
[github-release-link]: https://github.com/jumpserver/jumpserver/releases/latest
[github-stars-link]: https://github.com/jumpserver/jumpserver
[github-issues-link]: https://github.com/jumpserver/jumpserver/issues
<!-- Shield link-->
[github-release-shield]: https://img.shields.io/github/v/release/jumpserver/jumpserver
[github-stars-shield]: https://img.shields.io/github/stars/jumpserver/jumpserver?color=%231890FF&style=flat-square
[docker-shield]: https://img.shields.io/docker/pulls/jumpserver/jms_all.svg
[license-shield]: https://img.shields.io/github/license/jumpserver/jumpserver
[discord-shield]: https://img.shields.io/discord/1194233267294052363?style=flat&logo=discord&logoColor=%23f5f5f5&labelColor=%235462eb&color=%235462eb

View File

@ -1,94 +0,0 @@
<p align="center"><a href="https://jumpserver.org"><img src="https://download.jumpserver.org/images/jumpserver-logo.svg" alt="JumpServer" width="300" /></a></p>
<h3 align="center">Open Source Bastion Host</h3>
<p align="center">
<a href="https://www.gnu.org/licenses/gpl-3.0.html"><img src="https://img.shields.io/github/license/jumpserver/jumpserver" alt="License: GPLv3"></a>
<a href="https://shields.io/github/downloads/jumpserver/jumpserver/total"><img src="https://shields.io/github/downloads/jumpserver/jumpserver/total" alt=" release"></a>
<a href="https://hub.docker.com/u/jumpserver"><img src="https://img.shields.io/docker/pulls/jumpserver/jms_all.svg" alt="Codacy"></a>
<a href="https://github.com/jumpserver/jumpserver"><img src="https://img.shields.io/github/stars/jumpserver/jumpserver?color=%231890FF&style=flat-square" alt="Stars"></a>
</p>
JumpServer is the world's first open-source Bastion Host and is licensed under the GPLv3. It is a 4A-compliant professional operation and maintenance security audit system.
JumpServer uses Python / Django for development, follows Web 2.0 specifications, and is equipped with an industry-leading Web Terminal solution that provides a beautiful user interface and great user experience
JumpServer adopts a distributed architecture to support multi-branch deployment across multiple cross-regional areas. The central node provides APIs, and login nodes are deployed in each branch. It can be scaled horizontally without concurrency restrictions.
Change the world by taking every little step
----
### Advantages
- Open Source: huge transparency and free to access with quick installation process.
- Distributed: support large-scale concurrent access with ease.
- No Plugin required: all you need is a browser, the ultimate Web Terminal experience.
- Multi-Cloud supported: a unified system to manage assets on different clouds at the same time
- Cloud storage: audit records are stored in the cloud. Data lost no more!
- Multi-Tenant system: multiple subsidiary companies or departments access the same system simultaneously.
- Many applications supported: link to databases, windows remote applications, and Kubernetes cluster, etc.
### JumpServer Component Projects
- [Lina](https://github.com/jumpserver/lina) JumpServer Web UI
- [Luna](https://github.com/jumpserver/luna) JumpServer Web Terminal
- [KoKo](https://github.com/jumpserver/koko) JumpServer Character protocaol Connector, replace original Python Version [Coco](https://github.com/jumpserver/coco)
- [Lion](https://github.com/jumpserver/lion-release) JumpServer Graphics protocol Connectorrely on [Apache Guacamole](https://guacamole.apache.org/)
### Contribution
If you have any good ideas or helping us to fix bugs, please submit a Pull Request and accept our thanks :)
Thanks to the following contributors for making JumpServer better everyday!
<a href="https://github.com/jumpserver/jumpserver/graphs/contributors">
<img src="https://contrib.rocks/image?repo=jumpserver/jumpserver" />
</a>
<a href="https://github.com/jumpserver/koko/graphs/contributors">
<img src="https://contrib.rocks/image?repo=jumpserver/koko" />
</a>
<a href="https://github.com/jumpserver/lina/graphs/contributors">
<img src="https://contrib.rocks/image?repo=jumpserver/lina" />
</a>
<a href="https://github.com/jumpserver/luna/graphs/contributors">
<img src="https://contrib.rocks/image?repo=jumpserver/luna" />
</a>
### Thanks to
- [Apache Guacamole](https://guacamole.apache.org/) Web page connection RDP, SSH, VNC protocol equipment. JumpServer graphical connection dependent.
- [OmniDB](https://omnidb.org/) Web page connection to databases. JumpServer Web database dependent.
### JumpServer Enterprise Version
- [Apply for it](https://jinshuju.net/f/kyOYpi)
### Case Study
- [JumpServer 堡垒机护航顺丰科技超大规模资产安全运维](https://blog.fit2cloud.com/?p=1147)
- [JumpServer 堡垒机让“大智慧”的混合 IT 运维更智慧](https://blog.fit2cloud.com/?p=882)
- [携程 JumpServer 堡垒机部署与运营实战](https://blog.fit2cloud.com/?p=851)
- [小红书的JumpServer堡垒机大规模资产跨版本迁移之路](https://blog.fit2cloud.com/?p=516)
- [JumpServer堡垒机助力中手游提升多云环境下安全运维能力](https://blog.fit2cloud.com/?p=732)
- [中通快递JumpServer主机安全运维实践](https://blog.fit2cloud.com/?p=708)
- [东方明珠JumpServer高效管控异构化、分布式云端资产](https://blog.fit2cloud.com/?p=687)
- [江苏农信JumpServer堡垒机助力行业云安全运维](https://blog.fit2cloud.com/?p=666)。
### For safety instructions
JumpServer is a security product. Please refer to [Basic Security Recommendations](https://docs.jumpserver.org/zh/master/install/install_security/) for deployment and installation.
If you find a security problem, please contact us directly
- ibuler@fit2cloud.com
- support@fit2cloud.com
- 400-052-0755
### License & Copyright
Copyright (c) 2014-2024 FIT2CLOUD Tech, Inc., All rights reserved.
Licensed under The GNU General Public License version 3 (GPLv3) (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at
https://www.gnu.org/licenses/gpl-3.0.htmll
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.

View File

@ -5,8 +5,7 @@ JumpServer 是一款正在成长的安全产品, 请参考 [基本安全建议
如果你发现安全问题,请直接联系我们,我们携手让世界更好:
- ibuler@fit2cloud.com
- support@fit2cloud.com
- 400-052-0755
- support@lxware.hk
# Security Policy
@ -16,6 +15,5 @@ JumpServer is a security product, The installation and development should follow
All security bugs should be reported to the contact as below:
- ibuler@fit2cloud.com
- support@fit2cloud.com
- 400-052-0755
- support@lxware.hk

View File

@ -1,4 +1,6 @@
from .account import *
from .application import *
from .pam_dashboard import *
from .task import *
from .template import *
from .virtual import *

View File

@ -1,20 +1,27 @@
from django.db import transaction
from django.shortcuts import get_object_or_404
from django.utils.translation import gettext_lazy as _
from rest_framework.decorators import action
from rest_framework.generics import ListAPIView, CreateAPIView
from rest_framework.response import Response
from rest_framework.status import HTTP_200_OK
from accounts import serializers
from accounts.filters import AccountFilterSet
from accounts.const import ChangeSecretRecordStatusChoice
from accounts.filters import AccountFilterSet, NodeFilterBackend
from accounts.mixins import AccountRecordViewLogMixin
from accounts.models import Account
from accounts.models import Account, ChangeSecretRecord
from assets.models import Asset, Node
from authentication.permissions import UserConfirmation, ConfirmType
from common.api.mixin import ExtraFilterFieldsMixin
from common.drf.filters import AttrRulesFilterBackend
from common.permissions import IsValidUser
from common.utils import lazyproperty, get_logger
from orgs.mixins.api import OrgBulkModelViewSet
from rbac.permissions import RBACPermission
logger = get_logger(__file__)
__all__ = [
'AccountViewSet', 'AccountSecretsViewSet',
'AccountHistoriesSecretAPI', 'AssetAccountBulkCreateApi',
@ -24,6 +31,7 @@ __all__ = [
class AccountViewSet(OrgBulkModelViewSet):
model = Account
search_fields = ('username', 'name', 'asset__name', 'asset__address', 'comment')
extra_filter_backends = [AttrRulesFilterBackend, NodeFilterBackend]
filterset_class = AccountFilterSet
serializer_classes = {
'default': serializers.AccountSerializer,
@ -33,9 +41,21 @@ class AccountViewSet(OrgBulkModelViewSet):
'partial_update': ['accounts.change_account'],
'su_from_accounts': 'accounts.view_account',
'clear_secret': 'accounts.change_account',
'move_to_assets': 'accounts.create_account',
'copy_to_assets': 'accounts.create_account',
}
export_as_zip = True
def get_queryset(self):
queryset = super().get_queryset()
asset_id = self.request.query_params.get('asset') or self.request.query_params.get('asset_id')
if not asset_id:
return queryset
asset = get_object_or_404(Asset, pk=asset_id)
queryset = asset.all_accounts.all()
return queryset
@action(methods=['get'], detail=False, url_path='su-from-accounts')
def su_from_accounts(self, request, *args, **kwargs):
account_id = request.query_params.get('account')
@ -86,6 +106,45 @@ class AccountViewSet(OrgBulkModelViewSet):
self.model.objects.filter(id__in=account_ids).update(secret=None)
return Response(status=HTTP_200_OK)
def _copy_or_move_to_assets(self, request, move=False):
account = self.get_object()
asset_ids = request.data.get('assets', [])
assets = Asset.objects.filter(id__in=asset_ids)
field_names = [
'name', 'username', 'secret_type', 'secret',
'privileged', 'is_active', 'source', 'source_id', 'comment'
]
account_data = {field: getattr(account, field) for field in field_names}
creation_results = {}
success_count = 0
for asset in assets:
account_data['asset'] = asset
creation_results[asset] = {'state': 'created'}
try:
with transaction.atomic():
self.model.objects.create(**account_data)
success_count += 1
except Exception as e:
logger.debug(f'{"Move" if move else "Copy"} to assets error: {e}')
creation_results[asset] = {'error': _('Account already exists'), 'state': 'error'}
results = [{'asset': str(asset), **res} for asset, res in creation_results.items()]
if move and success_count > 0:
account.delete()
return Response(results, status=HTTP_200_OK)
@action(methods=['post'], detail=True, url_path='move-to-assets')
def move_to_assets(self, request, *args, **kwargs):
return self._copy_or_move_to_assets(request, move=True)
@action(methods=['post'], detail=True, url_path='copy-to-assets')
def copy_to_assets(self, request, *args, **kwargs):
return self._copy_or_move_to_assets(request, move=False)
class AccountSecretsViewSet(AccountRecordViewLogMixin, AccountViewSet):
"""
@ -125,17 +184,31 @@ class AccountHistoriesSecretAPI(ExtraFilterFieldsMixin, AccountRecordViewLogMixi
'GET': 'accounts.view_accountsecret',
}
def get_object(self):
@lazyproperty
def account(self) -> Account:
return get_object_or_404(Account, pk=self.kwargs.get('pk'))
def get_object(self):
return self.account
@lazyproperty
def latest_history(self):
return self.account.history.first()
@property
def latest_change_secret_record(self) -> ChangeSecretRecord:
return self.account.changesecretrecords.filter(
status=ChangeSecretRecordStatusChoice.pending
).order_by('-date_created').first()
@staticmethod
def filter_spm_queryset(resource_ids, queryset):
return queryset.filter(history_id__in=resource_ids)
def get_queryset(self):
account = self.get_object()
account = self.account
histories = account.history.all()
latest_history = account.history.first()
latest_history = self.latest_history
if not latest_history:
return histories
if account.secret != latest_history.secret:
@ -144,3 +217,25 @@ class AccountHistoriesSecretAPI(ExtraFilterFieldsMixin, AccountRecordViewLogMixi
return histories
histories = histories.exclude(history_id=latest_history.history_id)
return histories
def filter_queryset(self, queryset):
queryset = super().filter_queryset(queryset)
queryset = list(queryset)
latest_history = self.latest_history
if not latest_history:
return queryset
latest_change_secret_record = self.latest_change_secret_record
if not latest_change_secret_record:
return queryset
if latest_change_secret_record.date_created > latest_history.history_date:
temp_history = self.model(
secret=latest_change_secret_record.new_secret,
secret_type=self.account.secret_type,
version=latest_history.version,
history_date=latest_change_secret_record.date_created,
)
queryset = [temp_history] + queryset
return queryset

View File

@ -0,0 +1,84 @@
import os
from django.conf import settings
from django.utils.translation import gettext_lazy as _, get_language
from rest_framework.decorators import action
from rest_framework.response import Response
from accounts import serializers
from accounts.models import IntegrationApplication
from audits.models import IntegrationApplicationLog
from authentication.permissions import UserConfirmation, ConfirmType
from common.exceptions import JMSException
from common.permissions import IsValidUser
from common.utils import get_request_ip
from orgs.mixins.api import OrgBulkModelViewSet
from rbac.permissions import RBACPermission
class IntegrationApplicationViewSet(OrgBulkModelViewSet):
model = IntegrationApplication
search_fields = ('name', 'comment')
serializer_classes = {
'default': serializers.IntegrationApplicationSerializer,
'get_account_secret': serializers.IntegrationAccountSecretSerializer
}
rbac_perms = {
'get_once_secret': 'accounts.change_integrationapplication',
'get_account_secret': 'accounts.view_integrationapplication'
}
def read_file(self, path):
if os.path.exists(path):
with open(path, 'r', encoding='utf-8') as file:
return file.read()
return ''
@action(
['GET'], detail=False, url_path='sdks',
permission_classes=[IsValidUser]
)
def get_sdks_info(self, request, *args, **kwargs):
code_suffix_mapper = {
'python': 'py',
'java': 'java',
'go': 'go',
'node': 'js',
'curl': 'sh',
}
sdk_language = request.query_params.get('language', 'python')
sdk_path = os.path.join(settings.APPS_DIR, 'accounts', 'demos', sdk_language)
readme_path = os.path.join(sdk_path, f'README.{get_language()}.md')
demo_path = os.path.join(sdk_path, f'demo.{code_suffix_mapper[sdk_language]}')
readme_content = self.read_file(readme_path)
demo_content = self.read_file(demo_path)
return Response(data={'readme': readme_content, 'code': demo_content})
@action(
['GET'], detail=True, url_path='secret',
permission_classes=[RBACPermission, UserConfirmation.require(ConfirmType.MFA)]
)
def get_once_secret(self, request, *args, **kwargs):
instance = self.get_object()
return Response(data={'id': instance.id, 'secret': instance.secret})
@action(['GET'], detail=False, url_path='account-secret',
permission_classes=[RBACPermission])
def get_account_secret(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.query_params)
if not serializer.is_valid():
return Response({'error': serializer.errors}, status=400)
service = request.user
account = service.get_account(**serializer.data)
if not account:
msg = _('Account not found')
raise JMSException(code='Not found', detail='%s' % msg)
asset = account.asset
IntegrationApplicationLog.objects.create(
remote_addr=get_request_ip(request), service=service.name, service_id=service.id,
account=f'{account.name}({account.username})', asset=f'{asset.name}({asset.address})',
)
return Response(data={'id': request.user.id, 'secret': account.secret})

View File

@ -0,0 +1,130 @@
# -*- coding: utf-8 -*-
#
from collections import defaultdict
from django.db.models import Count, F, Q
from django.http.response import JsonResponse
from rest_framework.views import APIView
from accounts.models import (
Account, GatherAccountsAutomation,
PushAccountAutomation, BackupAccountAutomation,
AccountRisk, IntegrationApplication, ChangeSecretAutomation
)
from assets.const import AllTypes
from common.utils.timezone import local_monday
__all__ = ['PamDashboardApi']
class PamDashboardApi(APIView):
http_method_names = ['get']
rbac_perms = {
'GET': 'accounts.view_account',
}
@staticmethod
def get_type_to_accounts():
result = Account.objects.annotate(type=F('asset__platform__type')) \
.values('type').order_by('type').annotate(total=Count(1))
all_types_dict = dict(AllTypes.choices())
return [
{**i, 'label': all_types_dict.get(i['type'], i['type'])}
for i in result
]
@staticmethod
def get_account_risk_data(_all, query_params):
agg_map = {
'total_long_time_no_login_accounts': ('long_time_no_login_count', Q(risk='long_time_no_login')),
'total_new_found_accounts': ('new_found_count', Q(risk='new_found')),
'total_groups_changed_accounts': ('groups_changed_count', Q(risk='groups_changed')),
'total_sudoers_changed_accounts': ('sudoers_changed_count', Q(risk='sudoers_changed')),
'total_authorized_keys_changed_accounts': (
'authorized_keys_changed_count', Q(risk='authorized_keys_changed')),
'total_account_deleted_accounts': ('account_deleted_count', Q(risk='account_deleted')),
'total_password_expired_accounts': ('password_expired_count', Q(risk='password_expired')),
'total_long_time_password_accounts': ('long_time_password_count', Q(risk='long_time_password')),
'total_weak_password_accounts': ('weak_password_count', Q(risk='weak_password')),
'total_leaked_password_accounts': ('leaked_password_count', Q(risk='leaked_password')),
'total_repeated_password_accounts': ('repeated_password_count', Q(risk='repeated_password')),
}
aggregations = {
agg_key: Count('id', distinct=True, filter=agg_filter)
for param_key, (agg_key, agg_filter) in agg_map.items()
if _all or query_params.get(param_key)
}
data = {}
if aggregations:
account_stats = AccountRisk.objects.aggregate(**aggregations)
data = {param_key: account_stats.get(agg_key) for param_key, (agg_key, _) in agg_map.items() if
agg_key in account_stats}
return data
@staticmethod
def get_account_data(_all, query_params):
agg_map = {
'total_accounts': ('total_count', Count('id')),
'total_privileged_accounts': ('privileged_count', Count('id', filter=Q(privileged=True))),
'total_connectivity_ok_accounts': ('connectivity_ok_count', Count('id', filter=Q(connectivity='ok'))),
'total_secret_reset_accounts': ('secret_reset_count', Count('id', filter=Q(secret_reset=True))),
'total_valid_accounts': ('valid_count', Count('id', filter=Q(is_active=True))),
'total_week_add_accounts': ('week_add_count', Count('id', filter=Q(date_created__gte=local_monday()))),
}
aggregations = {
agg_key: agg_expr
for param_key, (agg_key, agg_expr) in agg_map.items()
if _all or query_params.get(param_key)
}
data = {}
account_stats = Account.objects.aggregate(**aggregations)
for param_key, (agg_key, __) in agg_map.items():
if agg_key in account_stats:
data[param_key] = account_stats[agg_key]
if _all or query_params.get('total_ordinary_accounts'):
if 'total_count' in account_stats and 'privileged_count' in account_stats:
data['total_ordinary_accounts'] = \
account_stats['total_count'] - account_stats['privileged_count']
return data
@staticmethod
def get_automation_counts(_all, query_params):
automation_counts = defaultdict(int)
automation_models = {
'total_count_change_secret_automation': ChangeSecretAutomation,
'total_count_gathered_account_automation': GatherAccountsAutomation,
'total_count_push_account_automation': PushAccountAutomation,
'total_count_backup_account_automation': BackupAccountAutomation,
'total_count_integration_application': IntegrationApplication,
}
for param_key, model in automation_models.items():
if _all or query_params.get(param_key):
automation_counts[param_key] = model.objects.count()
return automation_counts
def get(self, request, *args, **kwargs):
query_params = self.request.query_params
_all = query_params.get('all')
data = {}
data.update(self.get_account_data(_all, query_params))
data.update(self.get_account_risk_data(_all, query_params))
data.update(self.get_automation_counts(_all, query_params))
if _all or query_params.get('total_count_type_to_accounts'):
data.update({
'total_count_type_to_accounts': self.get_type_to_accounts(),
})
return JsonResponse(data, status=200)

View File

@ -1,5 +1,7 @@
from .backup import *
from .base import *
from .change_secret import *
from .gather_accounts import *
from .change_secret_dashboard import *
from .check_account import *
from .gather_account import *
from .push_account import *

View File

@ -1,41 +1,36 @@
# -*- coding: utf-8 -*-
#
from rest_framework import status, viewsets
from rest_framework.response import Response
from accounts import serializers
from accounts.const import AutomationTypes
from accounts.models import (
AccountBackupAutomation, AccountBackupExecution
BackupAccountAutomation
)
from accounts.tasks import execute_account_backup_task
from common.const.choices import Trigger
from orgs.mixins.api import OrgBulkModelViewSet
from .base import AutomationExecutionViewSet
__all__ = [
'AccountBackupPlanViewSet', 'AccountBackupPlanExecutionViewSet'
'BackupAccountViewSet', 'BackupAccountExecutionViewSet'
]
class AccountBackupPlanViewSet(OrgBulkModelViewSet):
model = AccountBackupAutomation
class BackupAccountViewSet(OrgBulkModelViewSet):
model = BackupAccountAutomation
filterset_fields = ('name',)
search_fields = filterset_fields
serializer_class = serializers.AccountBackupSerializer
serializer_class = serializers.BackupAccountSerializer
class AccountBackupPlanExecutionViewSet(viewsets.ModelViewSet):
serializer_class = serializers.AccountBackupPlanExecutionSerializer
search_fields = ('trigger', 'plan__name')
filterset_fields = ('trigger', 'plan_id', 'plan__name')
http_method_names = ['get', 'post', 'options']
class BackupAccountExecutionViewSet(AutomationExecutionViewSet):
rbac_perms = (
("list", "accounts.view_backupaccountexecution"),
("retrieve", "accounts.view_backupaccountexecution"),
("create", "accounts.add_backupaccountexecution"),
("report", "accounts.view_backupaccountexecution"),
)
tp = AutomationTypes.backup_account
def get_queryset(self):
queryset = AccountBackupExecution.objects.all()
queryset = super().get_queryset()
queryset = queryset.filter(type=self.tp)
return queryset
def create(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
pid = serializer.data.get('plan')
task = execute_account_backup_task.delay(pid=str(pid), trigger=Trigger.manual)
return Response({'task': task.id}, status=status.HTTP_201_CREATED)

View File

@ -1,8 +1,12 @@
from django.http import HttpResponse
from django.shortcuts import get_object_or_404
from django.utils.translation import gettext_lazy as _
from django.views.decorators.clickjacking import xframe_options_sameorigin
from rest_framework import status, mixins, viewsets
from rest_framework.decorators import action
from rest_framework.response import Response
from accounts.filters import AutomationExecutionFilterSet
from accounts.models import AutomationExecution
from accounts.tasks import execute_account_automation_task
from assets import serializers
@ -13,7 +17,7 @@ from orgs.mixins import generics
__all__ = [
'AutomationAssetsListApi', 'AutomationRemoveAssetApi',
'AutomationAddAssetApi', 'AutomationNodeAddRemoveApi',
'AutomationExecutionViewSet',
'AutomationExecutionViewSet'
]
@ -35,9 +39,10 @@ class AutomationAssetsListApi(generics.ListAPIView):
return assets
class AutomationRemoveAssetApi(generics.RetrieveUpdateAPIView):
class AutomationRemoveAssetApi(generics.UpdateAPIView):
model = BaseAutomation
serializer_class = serializers.UpdateAssetSerializer
http_method_names = ['patch']
def update(self, request, *args, **kwargs):
instance = self.get_object()
@ -52,9 +57,10 @@ class AutomationRemoveAssetApi(generics.RetrieveUpdateAPIView):
return Response({'msg': 'ok'})
class AutomationAddAssetApi(generics.RetrieveUpdateAPIView):
class AutomationAddAssetApi(generics.UpdateAPIView):
model = BaseAutomation
serializer_class = serializers.UpdateAssetSerializer
http_method_names = ['patch']
def update(self, request, *args, **kwargs):
instance = self.get_object()
@ -68,9 +74,10 @@ class AutomationAddAssetApi(generics.RetrieveUpdateAPIView):
return Response({"error": serializer.errors})
class AutomationNodeAddRemoveApi(generics.RetrieveUpdateAPIView):
class AutomationNodeAddRemoveApi(generics.UpdateAPIView):
model = BaseAutomation
serializer_class = serializers.UpdateNodeSerializer
http_method_names = ['patch']
def update(self, request, *args, **kwargs):
action_params = ['add', 'remove']
@ -97,8 +104,8 @@ class AutomationExecutionViewSet(
):
search_fields = ('trigger', 'automation__name')
filterset_fields = ('trigger', 'automation_id', 'automation__name')
filterset_class = AutomationExecutionFilterSet
serializer_class = serializers.AutomationExecutionSerializer
tp: str
def get_queryset(self):
@ -113,3 +120,10 @@ class AutomationExecutionViewSet(
pid=str(automation.pk), trigger=Trigger.manual, tp=self.tp
)
return Response({'task': task.id}, status=status.HTTP_201_CREATED)
@xframe_options_sameorigin
@action(methods=['get'], detail=True, url_path='report')
def report(self, request, *args, **kwargs):
execution = self.get_object()
report = execution.manager.gen_report()
return HttpResponse(report)

View File

@ -1,15 +1,17 @@
# -*- coding: utf-8 -*-
#
from django.db.models import Max, Q, Subquery, OuterRef
from rest_framework import status, mixins
from rest_framework.decorators import action
from rest_framework.response import Response
from accounts import serializers
from accounts.const import AutomationTypes
from accounts.const import AutomationTypes, ChangeSecretRecordStatusChoice
from accounts.filters import ChangeSecretRecordFilterSet
from accounts.models import ChangeSecretAutomation, ChangeSecretRecord
from accounts.tasks import execute_automation_record_task
from authentication.permissions import UserConfirmation, ConfirmType
from common.permissions import IsValidLicense
from orgs.mixins.api import OrgBulkModelViewSet, OrgGenericViewSet
from rbac.permissions import RBACPermission
from .base import (
@ -27,6 +29,7 @@ __all__ = [
class ChangeSecretAutomationViewSet(OrgBulkModelViewSet):
model = ChangeSecretAutomation
permission_classes = [RBACPermission, IsValidLicense]
filterset_fields = ('name', 'secret_type', 'secret_strategy')
search_fields = filterset_fields
serializer_class = serializers.ChangeSecretAutomationSerializer
@ -34,7 +37,9 @@ class ChangeSecretAutomationViewSet(OrgBulkModelViewSet):
class ChangeSecretRecordViewSet(mixins.ListModelMixin, OrgGenericViewSet):
filterset_class = ChangeSecretRecordFilterSet
search_fields = ('asset__address',)
permission_classes = [RBACPermission, IsValidLicense]
search_fields = ('asset__address', 'account__username')
ordering_fields = ('date_finished',)
tp = AutomationTypes.change_secret
serializer_classes = {
'default': serializers.ChangeSecretRecordSerializer,
@ -43,6 +48,8 @@ class ChangeSecretRecordViewSet(mixins.ListModelMixin, OrgGenericViewSet):
rbac_perms = {
'execute': 'accounts.add_changesecretexecution',
'secret': 'accounts.view_changesecretrecord',
'dashboard': 'accounts.view_changesecretrecord',
'ignore_fail': 'accounts.view_changesecretrecord',
}
def get_permissions(self):
@ -53,8 +60,35 @@ class ChangeSecretRecordViewSet(mixins.ListModelMixin, OrgGenericViewSet):
]
return super().get_permissions()
def filter_queryset(self, queryset):
queryset = super().filter_queryset(queryset)
if self.action == 'dashboard':
return self.get_dashboard_queryset(queryset)
return queryset
@staticmethod
def get_dashboard_queryset(queryset):
recent_dates = queryset.values('account').annotate(
max_date_finished=Max('date_finished')
)
recent_success_accounts = queryset.filter(
account=OuterRef('account'),
date_finished=Subquery(
recent_dates.filter(account=OuterRef('account')).values('max_date_finished')[:1]
)
).filter(Q(status=ChangeSecretRecordStatusChoice.success))
failed_records = queryset.filter(
~Q(account__in=Subquery(recent_success_accounts.values('account'))),
status=ChangeSecretRecordStatusChoice.failed,
ignore_fail=False
)
return failed_records
def get_queryset(self):
return ChangeSecretRecord.objects.all()
return ChangeSecretRecord.get_valid_records()
@action(methods=['post'], detail=False, url_path='execute')
def execute(self, request, *args, **kwargs):
@ -75,19 +109,31 @@ class ChangeSecretRecordViewSet(mixins.ListModelMixin, OrgGenericViewSet):
serializer = self.get_serializer(instance)
return Response(serializer.data)
@action(methods=['get'], detail=False, url_path='dashboard')
def dashboard(self, request, *args, **kwargs):
return super().list(request, *args, **kwargs)
@action(methods=['patch'], detail=True, url_path='ignore-fail')
def ignore_fail(self, request, *args, **kwargs):
instance = self.get_object()
instance.ignore_fail = True
instance.save(update_fields=['ignore_fail'])
return Response(status=status.HTTP_200_OK)
class ChangSecretExecutionViewSet(AutomationExecutionViewSet):
rbac_perms = (
("list", "accounts.view_changesecretexecution"),
("retrieve", "accounts.view_changesecretexecution"),
("create", "accounts.add_changesecretexecution"),
("report", "accounts.view_changesecretexecution"),
)
permission_classes = [RBACPermission, IsValidLicense]
tp = AutomationTypes.change_secret
def get_queryset(self):
queryset = super().get_queryset()
queryset = queryset.filter(automation__type=self.tp)
queryset = queryset.filter(type=self.tp)
return queryset

View File

@ -0,0 +1,185 @@
# -*- coding: utf-8 -*-
#
from collections import defaultdict
from django.core.cache import cache
from django.http.response import JsonResponse
from django.utils import timezone
from rest_framework.views import APIView
from accounts.const import AutomationTypes, ChangeSecretRecordStatusChoice
from accounts.models import ChangeSecretAutomation, AutomationExecution, ChangeSecretRecord
from assets.models import Node, Asset
from common.const import Status
from common.permissions import IsValidLicense
from common.utils import lazyproperty
from common.utils.timezone import local_zero_hour, local_now
from ops.celery import app
from rbac.permissions import RBACPermission
__all__ = ['ChangeSecretDashboardApi']
class ChangeSecretDashboardApi(APIView):
http_method_names = ['get']
rbac_perms = {
'GET': 'accounts.view_changesecretautomation',
}
permission_classes = [RBACPermission, IsValidLicense]
tp = AutomationTypes.change_secret
task_name = 'accounts.tasks.automation.execute_account_automation_task'
ongoing_change_secret_cache_key = "ongoing_change_secret_cache_key"
@lazyproperty
def days(self):
count = self.request.query_params.get('days', 1)
return int(count)
@property
def days_to_datetime(self):
if self.days == 1:
return local_zero_hour()
return local_now() - timezone.timedelta(days=self.days)
def get_queryset_date_filter(self, qs, query_field='date_updated'):
return qs.filter(**{f'{query_field}__gte': self.days_to_datetime})
@lazyproperty
def date_range_list(self):
return [
(local_now() - timezone.timedelta(days=i)).date()
for i in range(self.days - 1, -1, -1)
]
def filter_by_date_range(self, queryset, field_name):
date_range_bounds = self.days_to_datetime.date(), (local_now() + timezone.timedelta(days=1)).date()
return queryset.filter(**{f'{field_name}__range': date_range_bounds})
def calculate_daily_metrics(self, queryset, date_field):
filtered_queryset = self.filter_by_date_range(queryset, date_field)
results = filtered_queryset.values_list(date_field, 'status')
status_counts = defaultdict(lambda: defaultdict(int))
for date_finished, status in results:
date_str = str(date_finished.date())
if status == ChangeSecretRecordStatusChoice.failed:
status_counts[date_str]['failed'] += 1
elif status == ChangeSecretRecordStatusChoice.success:
status_counts[date_str]['success'] += 1
metrics = defaultdict(list)
for date in self.date_range_list:
date_str = str(date)
for status in ['success', 'failed']:
metrics[status].append(status_counts[date_str].get(status, 0))
return metrics
def get_daily_success_and_failure_metrics(self):
metrics = self.calculate_daily_metrics(self.change_secret_records_queryset, 'date_finished')
return metrics.get('success', []), metrics.get('failed', [])
@lazyproperty
def change_secrets_queryset(self):
return ChangeSecretAutomation.objects.all()
@lazyproperty
def change_secret_records_queryset(self):
return ChangeSecretRecord.get_valid_records()
def get_change_secret_asset_queryset(self):
qs = self.change_secrets_queryset
node_ids = qs.filter(nodes__isnull=False).values_list('nodes', flat=True).distinct()
nodes = Node.objects.filter(id__in=node_ids)
node_asset_ids = Node.get_nodes_all_assets(*nodes).values_list('id', flat=True)
direct_asset_ids = qs.filter(assets__isnull=False).values_list('assets', flat=True).distinct()
asset_ids = set(list(direct_asset_ids) + list(node_asset_ids))
return Asset.objects.filter(id__in=asset_ids)
def get_filtered_counts(self, qs, field=None):
if field is None:
return qs.count()
return self.get_queryset_date_filter(qs, field).count()
def get_status_counts(self, executions):
executions = executions.filter(type=self.tp)
total, failed, success = 0, 0, 0
for status in executions.values_list('status', flat=True):
total += 1
if status in [Status.failed, Status.error]:
failed += 1
elif status == Status.success:
success += 1
return {
'total_count_change_secret_executions': total,
'total_count_success_change_secret_executions': success,
'total_count_failed_change_secret_executions': failed,
}
def get(self, request, *args, **kwargs):
query_params = self.request.query_params
data = {}
_all = query_params.get('all')
if _all or query_params.get('total_count_change_secrets'):
data['total_count_change_secrets'] = self.get_filtered_counts(
self.change_secrets_queryset
)
if _all or query_params.get('total_count_periodic_change_secrets'):
data['total_count_periodic_change_secrets'] = self.get_filtered_counts(
self.change_secrets_queryset.filter(is_periodic=True)
)
if _all or query_params.get('total_count_change_secret_assets'):
data['total_count_change_secret_assets'] = self.get_change_secret_asset_queryset().count()
if _all or query_params.get('total_count_change_secret_status'):
executions = self.get_queryset_date_filter(AutomationExecution.objects.all(), 'date_start')
data.update(self.get_status_counts(executions))
if _all or query_params.get('daily_success_and_failure_metrics'):
success, failed = self.get_daily_success_and_failure_metrics()
data.update({
'dates_metrics_date': [date.strftime('%m-%d') for date in self.date_range_list] or ['0'],
'dates_metrics_total_count_success': success,
'dates_metrics_total_count_failed': failed,
})
if _all or query_params.get('total_count_ongoing_change_secret'):
ongoing_counts = cache.get(self.ongoing_change_secret_cache_key)
if ongoing_counts is None:
execution_ids = []
inspect = app.control.inspect()
try:
active_tasks = inspect.active()
except Exception:
active_tasks = None
if active_tasks:
for tasks in active_tasks.values():
for task in tasks:
_id = task.get('id')
name = task.get('name')
tp = task.get('kwargs', {}).get('tp')
if name == self.task_name and tp == self.tp:
execution_ids.append(_id)
snapshots = AutomationExecution.objects.filter(id__in=execution_ids).values_list('snapshot', flat=True)
asset_ids = {asset for i in snapshots for asset in i.get('assets', [])}
account_ids = {account for i in snapshots for account in i.get('accounts', [])}
ongoing_counts = (len(execution_ids), len(asset_ids), len(account_ids))
data['total_count_ongoing_change_secret'] = ongoing_counts[0]
data['total_count_ongoing_change_secret_assets'] = ongoing_counts[1]
data['total_count_ongoing_change_secret_accounts'] = ongoing_counts[2]
cache.set(self.ongoing_change_secret_cache_key, ongoing_counts, 60)
else:
data['total_count_ongoing_change_secret'] = ongoing_counts[0]
data['total_count_ongoing_change_secret_assets'] = ongoing_counts[1]
data['total_count_ongoing_change_secret_accounts'] = ongoing_counts[2]
return JsonResponse(data, status=200)

View File

@ -0,0 +1,162 @@
# -*- coding: utf-8 -*-
#
from django.db.models import Q, Count
from django.http import HttpResponse
from django.shortcuts import get_object_or_404
from rest_framework.decorators import action
from rest_framework.exceptions import MethodNotAllowed
from rest_framework.response import Response
from accounts import serializers
from accounts.const import AutomationTypes
from accounts.models import (
CheckAccountAutomation,
AccountRisk,
RiskChoice,
CheckAccountEngine,
AutomationExecution,
)
from assets.models import Asset
from common.api import JMSModelViewSet
from common.permissions import IsValidLicense
from common.utils import many_get
from orgs.mixins.api import OrgBulkModelViewSet
from rbac.permissions import RBACPermission
from .base import AutomationExecutionViewSet
from ...filters import NodeFilterBackend
from ...risk_handlers import RiskHandler
__all__ = [
"CheckAccountAutomationViewSet",
"CheckAccountExecutionViewSet",
"AccountRiskViewSet",
"CheckAccountEngineViewSet",
]
class CheckAccountAutomationViewSet(OrgBulkModelViewSet):
model = CheckAccountAutomation
filterset_fields = ("name",)
search_fields = filterset_fields
permission_classes = [RBACPermission, IsValidLicense]
serializer_class = serializers.CheckAccountAutomationSerializer
class CheckAccountExecutionViewSet(AutomationExecutionViewSet):
rbac_perms = (
("list", "accounts.view_checkaccountexecution"),
("retrieve", "accounts.view_checkaccountsexecution"),
("create", "accounts.add_checkaccountexecution"),
("adhoc", "accounts.add_checkaccountexecution"),
("report", "accounts.view_checkaccountsexecution"),
)
ordering = ("-date_created",)
tp = AutomationTypes.check_account
permission_classes = [RBACPermission, IsValidLicense]
def get_queryset(self):
queryset = super().get_queryset()
queryset = queryset.filter(type=self.tp)
return queryset
@action(methods=["get"], detail=False, url_path="adhoc")
def adhoc(self, request, *args, **kwargs):
asset_id = request.query_params.get("asset_id")
if not asset_id:
return Response(status=400, data={"asset_id": "This field is required."})
asset = get_object_or_404(Asset, pk=asset_id)
name = "Check asset risk: {}".format(asset.name)
execution = AutomationExecution()
execution.snapshot = {
"assets": [asset_id],
"nodes": [],
"type": AutomationTypes.check_account,
"engines": "__all__",
"name": name,
}
execution.save()
execution.start()
report = execution.manager.gen_report()
return HttpResponse(report)
class AccountRiskViewSet(OrgBulkModelViewSet):
model = AccountRisk
search_fields = ["username", "asset__name"]
filterset_fields = ("risk", "status", "asset_id")
extra_filter_backends = [NodeFilterBackend]
permission_classes = [RBACPermission, IsValidLicense]
serializer_classes = {
"default": serializers.AccountRiskSerializer,
"assets": serializers.AssetRiskSerializer,
"handle": serializers.HandleRiskSerializer,
}
ordering_fields = ("asset", "risk", "status", "username", "date_created")
ordering = ("status", "asset", "date_created")
rbac_perms = {
"sync_accounts": "assets.add_accountrisk",
"assets": "accounts.view_accountrisk",
"handle": "accounts.change_accountrisk",
}
def update(self, request, *args, **kwargs):
raise MethodNotAllowed("PUT")
def create(self, request, *args, **kwargs):
raise MethodNotAllowed("POST")
@action(methods=["get"], detail=False, url_path="assets")
def assets(self, request, *args, **kwargs):
annotations = {
f"{risk[0]}_count": Count("id", filter=Q(risk=risk[0]))
for risk in RiskChoice.choices
}
queryset = (
AccountRisk.objects.select_related(
"asset", "asset__platform"
) # 使用 select_related 来优化 asset 和 asset__platform 的查询
.values(
"asset__id", "asset__name", "asset__address", "asset__platform__name"
) # 添加需要的字段
.annotate(risk_total=Count("id")) # 计算风险总数
.annotate(**annotations) # 使用上面定义的 annotations 进行计数
)
return self.get_paginated_response_from_queryset(queryset)
@action(methods=["post"], detail=False, url_path="handle")
def handle(self, request, *args, **kwargs):
s = self.get_serializer(data=request.data)
s.is_valid(raise_exception=True)
asset, username, act, risk = many_get(
s.validated_data, ("asset", "username", "action", "risk")
)
handler = RiskHandler(asset=asset, username=username, request=self.request)
try:
risk = handler.handle(act, risk)
s = serializers.AccountRiskSerializer(instance=risk)
return Response(data=s.data)
except Exception as e:
return Response(status=400, data=str(e))
class CheckAccountEngineViewSet(JMSModelViewSet):
search_fields = ("name",)
serializer_class = serializers.CheckAccountEngineSerializer
permission_classes = [RBACPermission, IsValidLicense]
perm_model = CheckAccountEngine
http_method_names = ['get', 'options']
def get_queryset(self):
return CheckAccountEngine.get_default_engines()
def filter_queryset(self, queryset: list):
search = self.request.GET.get('search')
if search is not None:
queryset = [
item for item in queryset
if search in item['name']
]
return queryset

View File

@ -0,0 +1,131 @@
# -*- coding: utf-8 -*-
#
from django.http import HttpResponse
from django.shortcuts import get_object_or_404
from django.views.decorators.clickjacking import xframe_options_sameorigin
from rest_framework import status
from rest_framework.decorators import action
from rest_framework.response import Response
from accounts import serializers
from accounts.const import AutomationTypes
from accounts.filters import GatheredAccountFilterSet, NodeFilterBackend
from accounts.models import GatherAccountsAutomation, AutomationExecution, Account
from accounts.models import GatheredAccount
from assets.models import Asset
from common.const import ConfirmOrIgnore
from common.utils.http import is_true
from orgs.mixins.api import OrgBulkModelViewSet
from .base import AutomationExecutionViewSet
__all__ = [
"DiscoverAccountsAutomationViewSet",
"DiscoverAccountsExecutionViewSet",
"GatheredAccountViewSet",
]
from ...risk_handlers import RiskHandler
class DiscoverAccountsAutomationViewSet(OrgBulkModelViewSet):
model = GatherAccountsAutomation
filterset_fields = ("name",)
search_fields = filterset_fields
serializer_class = serializers.DiscoverAccountAutomationSerializer
class DiscoverAccountsExecutionViewSet(AutomationExecutionViewSet):
rbac_perms = (
("list", "accounts.view_gatheraccountsexecution"),
("retrieve", "accounts.view_gatheraccountsexecution"),
("create", "accounts.add_gatheraccountsexecution"),
("adhoc", "accounts.add_gatheraccountsexecution"),
("report", "accounts.view_gatheraccountsexecution"),
)
tp = AutomationTypes.gather_accounts
def get_queryset(self):
queryset = super().get_queryset()
queryset = queryset.filter(type=self.tp)
return queryset
@xframe_options_sameorigin
@action(methods=["get"], detail=False, url_path="adhoc")
def adhoc(self, request, *args, **kwargs):
asset_id = request.query_params.get("asset_id")
if not asset_id:
return Response(status=400, data={"asset_id": "This field is required."})
asset = get_object_or_404(Asset, pk=asset_id)
execution = AutomationExecution()
execution.snapshot = {
"assets": [asset_id],
"nodes": [],
"type": "gather_accounts",
"is_sync_account": False,
"check_risk": True,
"name": "Adhoc gather accounts: {}".format(asset.name),
}
execution.save()
execution.start()
report = execution.manager.gen_report()
return HttpResponse(report)
class GatheredAccountViewSet(OrgBulkModelViewSet):
model = GatheredAccount
search_fields = ("username",)
filterset_class = GatheredAccountFilterSet
extra_filter_backends = [NodeFilterBackend]
ordering = ("status",)
serializer_classes = {
"default": serializers.DiscoverAccountSerializer,
"status": serializers.DiscoverAccountActionSerializer,
"details": serializers.DiscoverAccountDetailsSerializer
}
rbac_perms = {
"status": "assets.change_gatheredaccount",
"details": "assets.view_gatheredaccount"
}
@action(methods=["put"], detail=False, url_path="status")
def status(self, request, *args, **kwargs):
serializer = self.get_serializer(data=request.data)
if not serializer.is_valid():
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
validated_data = serializer.validated_data
ids = validated_data.get('ids', [])
new_status = validated_data.get('status')
updated_instances = GatheredAccount.objects.filter(id__in=ids).select_related('asset')
if new_status == ConfirmOrIgnore.confirmed:
GatheredAccount.sync_accounts(updated_instances)
updated_instances.update(present=True)
updated_instances.update(status=new_status)
return Response(status=status.HTTP_200_OK)
def perform_destroy(self, instance):
request = self.request
params = request.query_params
is_delete_remote = params.get("is_delete_remote")
is_delete_account = params.get("is_delete_account")
asset_id = params.get("asset")
username = params.get("username")
if is_true(is_delete_remote):
self._delete_remote(asset_id, username)
if is_true(is_delete_account):
account = get_object_or_404(Account, username=username, asset_id=asset_id)
account.delete()
super().perform_destroy(instance)
def _delete_remote(self, asset_id, username):
asset = get_object_or_404(Asset, pk=asset_id)
handler = RiskHandler(asset, username, request=self.request)
handler.handle_delete_remote()
@action(methods=["get"], detail=True, url_path="details")
def details(self, request, *args, **kwargs):
pk = kwargs.get('pk')
account = get_object_or_404(GatheredAccount, pk=pk)
serializer = self.get_serializer(account.detail)
return Response(data=serializer.data)

View File

@ -1,59 +0,0 @@
# -*- coding: utf-8 -*-
#
from rest_framework import status
from rest_framework.decorators import action
from rest_framework.response import Response
from accounts import serializers
from accounts.const import AutomationTypes
from accounts.filters import GatheredAccountFilterSet
from accounts.models import GatherAccountsAutomation
from accounts.models import GatheredAccount
from orgs.mixins.api import OrgBulkModelViewSet
from .base import AutomationExecutionViewSet
__all__ = [
'GatherAccountsAutomationViewSet', 'GatherAccountsExecutionViewSet',
'GatheredAccountViewSet'
]
class GatherAccountsAutomationViewSet(OrgBulkModelViewSet):
model = GatherAccountsAutomation
filterset_fields = ('name',)
search_fields = filterset_fields
serializer_class = serializers.GatherAccountAutomationSerializer
class GatherAccountsExecutionViewSet(AutomationExecutionViewSet):
rbac_perms = (
("list", "accounts.view_gatheraccountsexecution"),
("retrieve", "accounts.view_gatheraccountsexecution"),
("create", "accounts.add_gatheraccountsexecution"),
)
tp = AutomationTypes.gather_accounts
def get_queryset(self):
queryset = super().get_queryset()
queryset = queryset.filter(automation__type=self.tp)
return queryset
class GatheredAccountViewSet(OrgBulkModelViewSet):
model = GatheredAccount
search_fields = ('username',)
filterset_class = GatheredAccountFilterSet
serializer_classes = {
'default': serializers.GatheredAccountSerializer,
}
rbac_perms = {
'sync_accounts': 'assets.add_gatheredaccount',
}
@action(methods=['post'], detail=False, url_path='sync-accounts')
def sync_accounts(self, request, *args, **kwargs):
gathered_account_ids = request.data.get('gathered_account_ids')
gathered_accounts = self.model.objects.filter(id__in=gathered_account_ids)
self.model.sync_accounts(gathered_accounts)
return Response(status=status.HTTP_201_CREATED)

View File

@ -1,15 +1,16 @@
# -*- coding: utf-8 -*-
#
from rest_framework import mixins
from accounts import serializers
from accounts.const import AutomationTypes
from accounts.models import PushAccountAutomation, ChangeSecretRecord
from orgs.mixins.api import OrgBulkModelViewSet
from accounts.filters import PushAccountRecordFilterSet
from accounts.models import PushAccountAutomation, PushSecretRecord
from orgs.mixins.api import OrgBulkModelViewSet, OrgGenericViewSet
from .base import (
AutomationAssetsListApi, AutomationRemoveAssetApi, AutomationAddAssetApi,
AutomationNodeAddRemoveApi, AutomationExecutionViewSet
)
from .change_secret import ChangeSecretRecordViewSet
__all__ = [
'PushAccountAutomationViewSet', 'PushAccountAssetsListApi', 'PushAccountRemoveAssetApi',
@ -30,24 +31,28 @@ class PushAccountExecutionViewSet(AutomationExecutionViewSet):
("list", "accounts.view_pushaccountexecution"),
("retrieve", "accounts.view_pushaccountexecution"),
("create", "accounts.add_pushaccountexecution"),
("report", "accounts.view_pushaccountexecution"),
)
tp = AutomationTypes.push_account
def get_queryset(self):
queryset = super().get_queryset()
queryset = queryset.filter(automation__type=self.tp)
queryset = queryset.filter(type=self.tp)
return queryset
class PushAccountRecordViewSet(ChangeSecretRecordViewSet):
serializer_class = serializers.ChangeSecretRecordSerializer
class PushAccountRecordViewSet(mixins.ListModelMixin, OrgGenericViewSet):
filterset_class = PushAccountRecordFilterSet
search_fields = ('asset__address', 'account__username')
ordering_fields = ('date_finished',)
tp = AutomationTypes.push_account
serializer_classes = {
'default': serializers.PushSecretRecordSerializer,
}
def get_queryset(self):
return ChangeSecretRecord.objects.filter(
execution__automation__type=AutomationTypes.push_account
)
return PushSecretRecord.get_valid_records()
class PushAccountAssetsListApi(AutomationAssetsListApi):

View File

@ -4,6 +4,7 @@ from django.apps import AppConfig
class AccountsConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'accounts'
verbose_name = 'App Accounts'
def ready(self):
from . import signal_handlers # noqa

View File

@ -3,20 +3,24 @@ import time
from collections import defaultdict, OrderedDict
from django.conf import settings
from django.db.models import F
from django.utils.translation import gettext_lazy as _
from rest_framework import serializers
from xlsxwriter import Workbook
from accounts.const import AccountBackupType
from accounts.models.automations.backup_account import AccountBackupAutomation
from accounts.models import BackupAccountAutomation, Account
from accounts.notifications import AccountBackupExecutionTaskMsg, AccountBackupByObjStorageExecutionTaskMsg
from accounts.serializers import AccountSecretSerializer
from assets.const import AllTypes
from common.const import Status
from common.utils.file import encrypt_and_compress_zip_file, zip_files
from common.utils.timezone import local_now_filename, local_now_display
from terminal.models.component.storage import ReplayStorage
from users.models import User
PATH = os.path.join(os.path.dirname(settings.BASE_DIR), 'tmp')
split_help_text = _('The account key will be split into two parts and sent')
class RecipientsNotFound(Exception):
@ -32,17 +36,26 @@ class BaseAccountHandler:
if isinstance(v, OrderedDict):
cls.unpack_data(v, data)
else:
if isinstance(v, dict):
v = v.get('label')
elif v is None:
v = ''
data[k] = v
return data
@classmethod
def get_header_fields(cls, serializer: serializers.Serializer):
try:
backup_fields = getattr(serializer, 'Meta').fields_backup
exclude_backup_fields = getattr(serializer, 'Meta').exclude_backup_fields
except AttributeError:
backup_fields = serializer.fields.keys()
exclude_backup_fields = []
backup_fields = serializer.fields.keys()
header_fields = {}
for field in backup_fields:
if field in exclude_backup_fields:
continue
v = serializer.fields[field]
if isinstance(v, serializers.Serializer):
_fields = cls.get_header_fields(v)
@ -72,9 +85,9 @@ class BaseAccountHandler:
class AssetAccountHandler(BaseAccountHandler):
@staticmethod
def get_filename(plan_name):
def get_filename(name):
filename = os.path.join(
PATH, f'{plan_name}-{local_now_filename()}-{time.time()}.xlsx'
PATH, f'{name}-{local_now_filename()}-{time.time()}.xlsx'
)
return filename
@ -115,32 +128,42 @@ class AssetAccountHandler(BaseAccountHandler):
data = AccountSecretSerializer(_accounts, many=True).data
cls.handler_secret(data, section)
data_map.update(cls.add_rows(data, header_fields, sheet_name))
print('\n\033[33m- 共备份 {} 条账号\033[0m'.format(accounts.count()))
number_of_backup_accounts = _('Number of backup accounts')
print('\033[33m- {}: {}\033[0m'.format(number_of_backup_accounts, accounts.count()))
return data_map
class AccountBackupHandler:
def __init__(self, execution):
def __init__(self, manager, execution):
self.manager = manager
self.execution = execution
self.plan_name = self.execution.plan.name
self.is_frozen = False # 任务状态冻结标志
self.name = self.execution.snapshot.get('name', '-')
def get_accounts(self):
# TODO 可以优化一下查询 在账号上做 category 的缓存 避免数据量大时连表操作
types = self.execution.snapshot.get('types', [])
self.manager.summary['total_types'] = len(types)
qs = Account.objects.filter(
asset__platform__type__in=types
).annotate(type=F('asset__platform__type'))
return qs
def create_excel(self, section='complete'):
hint = _('Generating asset related backup information files')
print(
'\n'
'\033[32m>>> 正在生成资产或应用相关备份信息文件\033[0m'
f'\033[32m>>> {hint}\033[0m'
''
)
# Print task start date
time_start = time.time()
files = []
accounts = self.execution.backup_accounts
accounts = self.get_accounts()
self.manager.summary['total_accounts'] = accounts.count()
data_map = AssetAccountHandler.create_data_map(accounts, section)
if not data_map:
return files
filename = AssetAccountHandler.get_filename(self.plan_name)
filename = AssetAccountHandler.get_filename(self.name)
wb = Workbook(filename)
for sheet, data in data_map.items():
@ -151,28 +174,30 @@ class AccountBackupHandler:
wb.close()
files.append(filename)
timedelta = round((time.time() - time_start), 2)
print('创建备份文件完成: 用时 {}s'.format(timedelta))
time_cost = _('Duration')
file_created = _('Backup file creation completed')
print('{}: {} {}s'.format(file_created, time_cost, timedelta))
return files
def send_backup_mail(self, files, recipients):
if not files:
return
recipients = User.objects.filter(id__in=list(recipients))
msg = _("Start sending backup emails")
print(
'\n'
'\033[32m>>> 开始发送备份邮件\033[0m'
f'\033[32m>>> {msg}\033[0m'
''
)
plan_name = self.plan_name
name = self.name
for user in recipients:
if not user.secret_key:
attachment_list = []
else:
attachment = os.path.join(PATH, f'{plan_name}-{local_now_filename()}-{time.time()}.zip')
attachment = os.path.join(PATH, f'{name}-{local_now_filename()}-{time.time()}.zip')
encrypt_and_compress_zip_file(attachment, user.secret_key, files)
attachment_list = [attachment, ]
AccountBackupExecutionTaskMsg(plan_name, user).publish(attachment_list)
print('邮件已发送至{}({})'.format(user, user.email))
attachment_list = [attachment]
AccountBackupExecutionTaskMsg(name, user).publish(attachment_list)
for file in files:
os.remove(file)
@ -181,73 +206,53 @@ class AccountBackupHandler:
return
recipients = ReplayStorage.objects.filter(id__in=list(recipients))
print(
'\n'
'\033[32m>>> 开始发送备份文件到sftp服务器\033[0m'
'\033[32m>>> 📃 ---> sftp \033[0m'
''
)
plan_name = self.plan_name
name = self.name
encrypt_file = _('Encrypting files using encryption password')
for rec in recipients:
attachment = os.path.join(PATH, f'{plan_name}-{local_now_filename()}-{time.time()}.zip')
attachment = os.path.join(PATH, f'{name}-{local_now_filename()}-{time.time()}.zip')
if password:
print('\033[32m>>> 使用加密密码对文件进行加密中\033[0m')
print(f'\033[32m>>> {encrypt_file}\033[0m')
encrypt_and_compress_zip_file(attachment, password, files)
else:
zip_files(attachment, files)
attachment_list = attachment
AccountBackupByObjStorageExecutionTaskMsg(plan_name, rec).publish(attachment_list)
print('备份文件将发送至{}({})'.format(rec.name, rec.id))
AccountBackupByObjStorageExecutionTaskMsg(name, rec).publish(attachment_list)
file_sent_to = _('The backup file will be sent to')
print('{}: {}({})'.format(file_sent_to, rec.name, rec.id))
for file in files:
os.remove(file)
def step_perform_task_update(self, is_success, reason):
self.execution.reason = reason[:1024]
self.execution.is_success = is_success
self.execution.save()
print('\n已完成对任务状态的更新\n')
@staticmethod
def step_finished(is_success):
if is_success:
print('任务执行成功')
else:
print('任务执行失败')
def _run(self):
is_success = False
error = '-'
try:
backup_type = self.execution.snapshot.get('backup_type', AccountBackupType.email.value)
if backup_type == AccountBackupType.email.value:
backup_type = self.execution.snapshot.get('backup_type', AccountBackupType.email)
if backup_type == AccountBackupType.email:
self.backup_by_email()
elif backup_type == AccountBackupType.object_storage.value:
elif backup_type == AccountBackupType.object_storage:
self.backup_by_obj_storage()
except Exception as e:
self.is_frozen = True
print('任务执行被异常中断')
print('下面打印发生异常的 Traceback 信息 : ')
print(e)
error = str(e)
else:
is_success = True
finally:
reason = error
self.step_perform_task_update(is_success, reason)
self.step_finished(is_success)
print(f'\033[31m>>> {error}\033[0m')
self.execution.status = Status.error
self.execution.summary['error'] = error
def backup_by_obj_storage(self):
object_id = self.execution.snapshot.get('id')
zip_encrypt_password = AccountBackupAutomation.objects.get(id=object_id).zip_encrypt_password
zip_encrypt_password = BackupAccountAutomation.objects.get(id=object_id).zip_encrypt_password
obj_recipients_part_one = self.execution.snapshot.get('obj_recipients_part_one', [])
obj_recipients_part_two = self.execution.snapshot.get('obj_recipients_part_two', [])
no_assigned_sftp_server = _('The backup task has no assigned sftp server')
if not obj_recipients_part_one and not obj_recipients_part_two:
print(
'\n'
'\033[31m>>> 该备份任务未分配sftp服务器\033[0m'
f'\033[31m>>> {no_assigned_sftp_server}\033[0m'
''
)
raise RecipientsNotFound('Not Found Recipients')
if obj_recipients_part_one and obj_recipients_part_two:
print('\033[32m>>> 账号的密钥将被拆分成前后两部分发送\033[0m')
print(f'\033[32m>>> {split_help_text}\033[0m')
files = self.create_excel(section='front')
self.send_backup_obj_storage(files, obj_recipients_part_one, zip_encrypt_password)
@ -259,17 +264,18 @@ class AccountBackupHandler:
self.send_backup_obj_storage(files, recipients, zip_encrypt_password)
def backup_by_email(self):
warn_text = _('The backup task has no assigned recipient')
recipients_part_one = self.execution.snapshot.get('recipients_part_one', [])
recipients_part_two = self.execution.snapshot.get('recipients_part_two', [])
if not recipients_part_one and not recipients_part_two:
print(
'\n'
'\033[31m>>> 该备份任务未分配收件人\033[0m'
f'\033[31m>>> {warn_text}\033[0m'
''
)
raise RecipientsNotFound('Not Found Recipients')
return
if recipients_part_one and recipients_part_two:
print('\033[32m>>> 账号的密钥将被拆分成前后两部分发送\033[0m')
print(f'\033[32m>>> {split_help_text}\033[0m')
files = self.create_excel(section='front')
self.send_backup_mail(files, recipients_part_one)
@ -281,15 +287,5 @@ class AccountBackupHandler:
self.send_backup_mail(files, recipients)
def run(self):
print('任务开始: {}'.format(local_now_display()))
time_start = time.time()
try:
self._run()
except Exception as e:
print('任务运行出现异常')
print('下面显示异常 Traceback 信息: ')
print(e)
finally:
print('\n任务结束: {}'.format(local_now_display()))
timedelta = round((time.time() - time_start), 2)
print('用时: {}s'.format(timedelta))
print('{}: {}'.format(_('Plan start'), local_now_display()))
self._run()

View File

@ -1,44 +1,30 @@
# -*- coding: utf-8 -*-
#
import time
from django.utils import timezone
from django.utils.translation import gettext_lazy as _
from assets.automations.base.manager import BaseManager
from common.utils.timezone import local_now_display
from .handlers import AccountBackupHandler
class AccountBackupManager:
def __init__(self, execution):
self.execution = execution
self.date_start = timezone.now()
self.time_start = time.time()
self.date_end = None
self.time_end = None
self.timedelta = 0
class AccountBackupManager(BaseManager):
def do_run(self):
execution = self.execution
print('\n\033[33m# 账号备份计划正在执行\033[0m')
handler = AccountBackupHandler(execution)
account_backup_execution_being_executed = _('The account backup plan is being executed')
print(f'\033[33m# {account_backup_execution_being_executed}\033[0m')
handler = AccountBackupHandler(self, execution)
handler.run()
def pre_run(self):
self.execution.date_start = self.date_start
self.execution.save()
def post_run(self):
self.time_end = time.time()
self.date_end = timezone.now()
def send_report_if_need(self):
pass
def print_summary(self):
print('\n\n' + '-' * 80)
print('计划执行结束 {}\n'.format(local_now_display()))
self.timedelta = self.time_end - self.time_start
print('用时: {}s'.format(self.timedelta))
self.execution.timedelta = self.timedelta
self.execution.save()
plan_execution_end = _('Plan execution end')
print('{} {}\n'.format(plan_execution_end, local_now_display()))
time_cost = _('Duration')
print('{}: {}s'.format(time_cost, self.duration))
def run(self):
self.pre_run()
self.do_run()
self.post_run()
def get_report_template(self):
return "accounts/backup_account_report.html"

View File

@ -1,12 +1,203 @@
from copy import deepcopy
from django.conf import settings
from django.utils import timezone
from django.utils.translation import gettext_lazy as _
from accounts.automations.methods import platform_automation_methods
from accounts.const import SSHKeyStrategy, SecretStrategy, SecretType, ChangeSecretRecordStatusChoice
from accounts.models import BaseAccountQuerySet
from accounts.utils import SecretGenerator
from assets.automations.base.manager import BasePlaybookManager
from assets.const import HostTypes
from common.db.utils import safe_db_connection
from common.utils import get_logger
logger = get_logger(__name__)
class AccountBasePlaybookManager(BasePlaybookManager):
template_path = ''
@property
def platform_automation_methods(self):
return platform_automation_methods
class BaseChangeSecretPushManager(AccountBasePlaybookManager):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.secret_type = self.execution.snapshot.get('secret_type')
self.secret_strategy = self.execution.snapshot.get(
'secret_strategy', SecretStrategy.custom
)
self.ssh_key_change_strategy = self.execution.snapshot.get(
'ssh_key_change_strategy', SSHKeyStrategy.set_jms
)
self.account_ids = self.execution.snapshot['accounts']
self.record_map = self.execution.snapshot.get('record_map', {}) # 这个是某个失败的记录重试
self.name_recorder_mapper = {} # 做个映射,方便后面处理
def gen_account_inventory(self, account, asset, h, path_dir):
raise NotImplementedError
def get_ssh_params(self, secret, secret_type):
kwargs = {}
if secret_type != SecretType.SSH_KEY:
return kwargs
kwargs['strategy'] = self.ssh_key_change_strategy
kwargs['exclusive'] = 'yes' if kwargs['strategy'] == SSHKeyStrategy.set else 'no'
if kwargs['strategy'] == SSHKeyStrategy.set_jms:
kwargs['regexp'] = '.*{}$'.format(secret.split()[2].strip())
return kwargs
def get_secret(self, account):
if self.secret_strategy == SecretStrategy.custom:
new_secret = self.execution.snapshot['secret']
else:
generator = SecretGenerator(
self.secret_strategy, self.secret_type,
self.execution.snapshot.get('password_rules')
)
new_secret = generator.get_secret()
return new_secret
def get_accounts(self, privilege_account) -> BaseAccountQuerySet | None:
if not privilege_account:
print('Not privilege account')
return
asset = privilege_account.asset
accounts = asset.all_accounts.all()
accounts = accounts.filter(id__in=self.account_ids, secret_reset=True)
if self.secret_type:
accounts = accounts.filter(secret_type=self.secret_type)
if settings.CHANGE_AUTH_PLAN_SECURE_MODE_ENABLED:
accounts = accounts.filter(privileged=False).exclude(
username__in=['root', 'administrator', privilege_account.username]
)
return accounts
def handle_ssh_secret(self, secret_type, new_secret, path_dir):
private_key_path = None
if secret_type == SecretType.SSH_KEY:
private_key_path = self.generate_private_key_path(new_secret, path_dir)
new_secret = self.generate_public_key(new_secret)
return new_secret, private_key_path
def gen_inventory(self, h, account, new_secret, private_key_path, asset):
secret_type = account.secret_type
h['ssh_params'].update(self.get_ssh_params(new_secret, secret_type))
h['account'] = {
'name': account.name,
'username': account.username,
'full_username': account.full_username,
'secret_type': secret_type,
'secret': account.escape_jinja2_syntax(new_secret),
'private_key_path': private_key_path,
'become': account.get_ansible_become_auth(),
}
if asset.platform.type == 'oracle':
h['account']['mode'] = 'sysdba' if account.privileged else None
return h
def host_callback(self, host, asset=None, account=None, automation=None, path_dir=None, **kwargs):
host = super().host_callback(
host, asset=asset, account=account, automation=automation,
path_dir=path_dir, **kwargs
)
if host.get('error'):
return host
host['check_conn_after_change'] = self.execution.snapshot.get('check_conn_after_change', True)
host['ssh_params'] = {}
accounts = self.get_accounts(account)
error_msg = _("No pending accounts found")
if not accounts:
print(f'{asset}: {error_msg}')
return []
if asset.type == HostTypes.WINDOWS:
accounts = accounts.filter(secret_type=SecretType.PASSWORD)
inventory_hosts = []
if asset.type == HostTypes.WINDOWS and self.secret_type == SecretType.SSH_KEY:
print(f'Windows {asset} does not support ssh key push')
return inventory_hosts
for account in accounts:
h = deepcopy(host)
h['name'] += '(' + account.username + ')' # To distinguish different accounts
try:
h = self.gen_account_inventory(account, asset, h, path_dir)
except Exception as e:
h['error'] = str(e)
inventory_hosts.append(h)
return inventory_hosts
@staticmethod
def save_record(recorder):
recorder.save(update_fields=['error', 'status', 'date_finished'])
def on_host_success(self, host, result):
recorder = self.name_recorder_mapper.get(host)
if not recorder:
return
recorder.status = ChangeSecretRecordStatusChoice.success.value
recorder.date_finished = timezone.now()
account = recorder.account
if not account:
print("Account not found, deleted ?")
return
account.secret = getattr(recorder, 'new_secret', account.secret)
account.date_updated = timezone.now()
account.date_change_secret = timezone.now()
account.change_secret_status = ChangeSecretRecordStatusChoice.success
self.summary['ok_accounts'] += 1
self.result['ok_accounts'].append(
{
"asset": str(account.asset),
"username": account.username,
}
)
super().on_host_success(host, result)
with safe_db_connection():
account.save(update_fields=['secret', 'date_updated', 'date_change_secret', 'change_secret_status'])
self.save_record(recorder)
def on_host_error(self, host, error, result):
recorder = self.name_recorder_mapper.get(host)
if not recorder:
return
recorder.status = ChangeSecretRecordStatusChoice.failed.value
recorder.date_finished = timezone.now()
recorder.error = error
account = recorder.account
if not account:
print("Account not found, deleted ?")
return
account.date_updated = timezone.now()
account.date_change_secret = timezone.now()
account.change_secret_status = ChangeSecretRecordStatusChoice.failed
self.summary['fail_accounts'] += 1
self.result['fail_accounts'].append(
{
"asset": str(recorder.asset),
"username": recorder.account.username,
}
)
super().on_host_error(host, error, result)
with safe_db_connection():
account.save(update_fields=['change_secret_status', 'date_change_secret', 'date_updated'])
self.save_record(recorder)

View File

@ -20,6 +20,7 @@
become_private_key_path: "{{ jms_custom_become_private_key_path | default(None) }}"
old_ssh_version: "{{ jms_asset.old_ssh_version | default(False) }}"
gateway_args: "{{ jms_asset.ansible_ssh_common_args | default(None) }}"
recv_timeout: "{{ params.recv_timeout | default(30) }}"
register: ping_info
delegate_to: localhost
@ -39,9 +40,12 @@
name: "{{ account.username }}"
password: "{{ account.secret }}"
commands: "{{ params.commands }}"
first_conn_delay_time: "{{ first_conn_delay_time | default(0.5) }}"
answers: "{{ params.answers }}"
recv_timeout: "{{ params.recv_timeout | default(30) }}"
delay_time: "{{ params.delay_time | default(2) }}"
prompt: "{{ params.prompt | default('.*') }}"
ignore_errors: true
when: ping_info is succeeded
when: ping_info is succeeded and check_conn_after_change
register: change_info
delegate_to: localhost
@ -58,4 +62,6 @@
become_private_key_path: "{{ account.become.ansible_ssh_private_key_file | default(None) }}"
old_ssh_version: "{{ jms_asset.old_ssh_version | default(False) }}"
gateway_args: "{{ jms_asset.ansible_ssh_common_args | default(None) }}"
recv_timeout: "{{ params.recv_timeout | default(30) }}"
delegate_to: localhost
when: check_conn_after_change

View File

@ -10,10 +10,30 @@ protocol: ssh
priority: 50
params:
- name: commands
type: list
type: text
label: "{{ 'Params commands label' | trans }}"
default: [ '' ]
default: ''
help_text: "{{ 'Params commands help text' | trans }}"
- name: recv_timeout
type: int
label: "{{ 'Params recv_timeout label' | trans }}"
default: 30
help_text: "{{ 'Params recv_timeout help text' | trans }}"
- name: delay_time
type: int
label: "{{ 'Params delay_time label' | trans }}"
default: 2
help_text: "{{ 'Params delay_time help text' | trans }}"
- name: prompt
type: str
label: "{{ 'Params prompt label' | trans }}"
default: '.*'
help_text: "{{ 'Params prompt help text' | trans }}"
- name: answers
type: text
label: "{{ 'Params answer label' | trans }}"
default: '.*'
help_text: "{{ 'Params answer help text' | trans }}"
i18n:
SSH account change secret:
@ -22,11 +42,91 @@ i18n:
en: 'Custom password change by SSH command line'
Params commands help text:
zh: '自定义命令中如需包含账号的 账号、密码、SSH 连接的用户密码 字段,<br />请使用 &#123;username&#125;、&#123;password&#125;、&#123;login_password&#125;格式,执行任务时会进行替换 。<br />比如针对 Cisco 主机进行改密,一般需要配置五条命令:<br />1. enable<br />2. &#123;login_password&#125;<br />3. configure terminal<br />4. username &#123;username&#125; privilege 0 password &#123;password&#125; <br />5. end'
ja: 'カスタム コマンドに SSH 接続用のアカウント番号、パスワード、ユーザー パスワード フィールドを含める必要がある場合は、<br />&#123;ユーザー名&#125;、&#123;パスワード&#125;、&#123;login_password& を使用してください。 # 125; 形式。タスクの実行時に置き換えられます。 <br />たとえば、Cisco ホストのパスワードを変更するには、通常、次の 5 つのコマンドを設定する必要があります:<br />1.enable<br />2.&#123;login_password&#125;<br />3 .ターミナルの設定<br / >4. ユーザー名 &#123;ユーザー名&#125; 権限 0 パスワード &#123;パスワード&#125; <br />5. 終了'
en: 'If the custom command needs to include the account number, password, and user password field for SSH connection,<br />Please use &#123;username&#125;, &#123;password&#125;, &#123;login_password&# 125; format, which will be replaced when executing the task. <br />For example, to change the password of a Cisco host, you generally need to configure five commands:<br />1. enable<br />2. &#123;login_password&#125;<br />3. configure terminal<br / >4. username &#123;username&#125; privilege 0 password &#123;password&#125; <br />5. end'
zh: |
请将命令中的指定位置改成特殊符号 <br />
1. 改密账号 -> {username} <br />
2. 改密密码 -> {password} <br />
3. 登录用户密码 -> {login_password} <br />
<strong>多条命令使用换行分割,</strong>执行任务时系统会根据特殊符号替换真实数据。<br />
比如针对 Cisco 主机进行改密,一般需要配置五条命令:<br />
enable <br />
{login_password} <br />
configure terminal <br />
username {username} privilege 0 password {password} <br />
end <br />
ja: |
コマンド内の指定された位置を特殊記号に変更してください。<br />
新しいパスワード(アカウント変更) -> {username} <br />
新しいパスワード(パスワード変更) -> {password} <br />
ログインユーザーパスワード -> {login_password} <br />
<strong>複数のコマンドは改行で区切り、</strong>タスクを実行するときにシステムは特殊記号を使用して実際のデータを置き換えます。<br />
例えば、Cisco機器のパスワードを変更する場合、一般的には5つのコマンドを設定する必要があります<br />
enable <br />
{login_password} <br />
configure terminal <br />
username {username} privilege 0 password {password} <br />
end <br />
en: |
Please change the specified positions in the command to special symbols. <br />
Change password account -> {username} <br />
Change password -> {password} <br />
Login user password -> {login_password} <br />
<strong>Multiple commands are separated by new lines,</strong> and when executing tasks, <br />
the system will replace the special symbols with real data. <br />
For example, to change the password for a Cisco device, you generally need to configure five commands: <br />
enable <br />
{login_password} <br />
configure terminal <br />
username {username} privilege 0 password {password} <br />
end <br />
Params commands label:
zh: '自定义命令'
ja: 'カスタムコマンド'
en: 'Custom command'
Params recv_timeout label:
zh: '超时时间'
ja: 'タイムアウト'
en: 'Timeout'
Params recv_timeout help text:
zh: '等待命令结果返回的超时时间(秒)'
ja: 'コマンドの結果を待つタイムアウト時間(秒)'
en: 'The timeout for waiting for the command result to return (Seconds)'
Params delay_time label:
zh: '延迟发送时间'
ja: '遅延送信時間'
en: 'Delayed send time'
Params delay_time help text:
zh: '每条命令延迟发送的时间间隔(秒)'
ja: '各コマンド送信の遅延間隔(秒)'
en: 'Time interval for each command delay in sending (Seconds)'
Params prompt label:
zh: '提示符'
ja: 'ヒント'
en: 'Prompt'
Params prompt help text:
zh: '终端连接后显示的提示符信息(正则表达式)'
ja: 'ターミナル接続後に表示されるプロンプト情報(正規表現)'
en: 'Prompt information displayed after terminal connection (Regular expression)'
Params answer label:
zh: '命令结果'
ja: 'コマンド結果'
en: 'Command result'
Params answer help text:
zh: |
根据结果匹配度决定是否执行下一条命令,输入框的内容和上方 “自定义命令” 内容按行一一对应(正则表达式)
ja: |
結果の一致度に基づいて次のコマンドを実行するかどうかを決定します。
入力欄の内容は、上の「カスタムコマンド」の内容と行ごとに対応しています(せいきひょうげん)
en: |
Decide whether to execute the next command based on the result match.
The input content corresponds line by line with the content
of the `Custom command` above. (Regular expression)

View File

@ -1,7 +1,7 @@
- hosts: mongodb
gather_facts: no
vars:
ansible_python_interpreter: /opt/py3/bin/python
ansible_python_interpreter: "{{ local_python_interpreter }}"
tasks:
- name: Test MongoDB connection
@ -53,3 +53,4 @@
ssl_certfile: "{{ jms_asset.secret_info.client_key | default('') }}"
connection_options:
- tlsAllowInvalidHostnames: "{{ jms_asset.spec_info.allow_invalid_cert}}"
when: check_conn_after_change

View File

@ -1,9 +1,12 @@
- hosts: mysql
gather_facts: no
vars:
ansible_python_interpreter: /opt/py3/bin/python
ansible_python_interpreter: "{{ local_python_interpreter }}"
db_name: "{{ jms_asset.spec_info.db_name }}"
check_ssl: "{{ jms_asset.spec_info.use_ssl and not jms_asset.spec_info.allow_invalid_cert }}"
ca_cert: "{{ jms_asset.secret_info.ca_cert | default('') }}"
ssl_cert: "{{ jms_asset.secret_info.client_cert | default('') }}"
ssl_key: "{{ jms_asset.secret_info.client_key | default('') }}"
tasks:
- name: Test MySQL connection
@ -13,9 +16,9 @@
login_host: "{{ jms_asset.address }}"
login_port: "{{ jms_asset.port }}"
check_hostname: "{{ check_ssl if check_ssl else omit }}"
ca_cert: "{{ jms_asset.secret_info.ca_cert | default(omit) if check_ssl else omit }}"
client_cert: "{{ jms_asset.secret_info.client_cert | default(omit) if check_ssl else omit }}"
client_key: "{{ jms_asset.secret_info.client_key | default(omit) if check_ssl else omit }}"
ca_cert: "{{ ca_cert if check_ssl and ca_cert | length > 0 else omit }}"
client_cert: "{{ ssl_cert if check_ssl and ssl_cert | length > 0 else omit }}"
client_key: "{{ ssl_key if check_ssl and ssl_key | length > 0 else omit }}"
filter: version
register: db_info
@ -30,9 +33,9 @@
login_host: "{{ jms_asset.address }}"
login_port: "{{ jms_asset.port }}"
check_hostname: "{{ check_ssl if check_ssl else omit }}"
ca_cert: "{{ jms_asset.secret_info.ca_cert | default(omit) if check_ssl else omit }}"
client_cert: "{{ jms_asset.secret_info.client_cert | default(omit) if check_ssl else omit }}"
client_key: "{{ jms_asset.secret_info.client_key | default(omit) if check_ssl else omit }}"
ca_cert: "{{ ca_cert if check_ssl and ca_cert | length > 0 else omit }}"
client_cert: "{{ ssl_cert if check_ssl and ssl_cert | length > 0 else omit }}"
client_key: "{{ ssl_key if check_ssl and ssl_key | length > 0 else omit }}"
name: "{{ account.username }}"
password: "{{ account.secret }}"
host: "%"
@ -47,7 +50,8 @@
login_host: "{{ jms_asset.address }}"
login_port: "{{ jms_asset.port }}"
check_hostname: "{{ check_ssl if check_ssl else omit }}"
ca_cert: "{{ jms_asset.secret_info.ca_cert | default(omit) if check_ssl else omit }}"
client_cert: "{{ jms_asset.secret_info.client_cert | default(omit) if check_ssl else omit }}"
client_key: "{{ jms_asset.secret_info.client_key | default(omit) if check_ssl else omit }}"
ca_cert: "{{ ca_cert if check_ssl and ca_cert | length > 0 else omit }}"
client_cert: "{{ ssl_cert if check_ssl and ssl_cert | length > 0 else omit }}"
client_key: "{{ ssl_key if check_ssl and ssl_key | length > 0 else omit }}"
filter: version
when: check_conn_after_change

View File

@ -1,7 +1,7 @@
- hosts: oracle
gather_facts: no
vars:
ansible_python_interpreter: /opt/py3/bin/python
ansible_python_interpreter: "{{ local_python_interpreter }}"
tasks:
- name: Test Oracle connection
@ -40,3 +40,4 @@
login_port: "{{ jms_asset.port }}"
login_database: "{{ jms_asset.spec_info.db_name }}"
mode: "{{ account.mode }}"
when: check_conn_after_change

View File

@ -1,7 +1,11 @@
- hosts: postgre
gather_facts: no
vars:
ansible_python_interpreter: /opt/py3/bin/python
ansible_python_interpreter: "{{ local_python_interpreter }}"
check_ssl: "{{ jms_asset.spec_info.use_ssl }}"
ca_cert: "{{ jms_asset.secret_info.ca_cert | default('') }}"
ssl_cert: "{{ jms_asset.secret_info.client_cert | default('') }}"
ssl_key: "{{ jms_asset.secret_info.client_key | default('') }}"
tasks:
- name: Test PostgreSQL connection
@ -11,6 +15,10 @@
login_host: "{{ jms_asset.address }}"
login_port: "{{ jms_asset.port }}"
login_db: "{{ jms_asset.spec_info.db_name }}"
ca_cert: "{{ ca_cert if check_ssl and ca_cert | length > 0 else omit }}"
ssl_cert: "{{ ssl_cert if check_ssl and ssl_cert | length > 0 else omit }}"
ssl_key: "{{ ssl_key if check_ssl and ssl_key | length > 0 else omit }}"
ssl_mode: "{{ jms_asset.spec_info.pg_ssl_mode }}"
register: result
failed_when: not result.is_available
@ -28,6 +36,10 @@
db: "{{ jms_asset.spec_info.db_name }}"
name: "{{ account.username }}"
password: "{{ account.secret }}"
ca_cert: "{{ ca_cert if check_ssl and ca_cert | length > 0 else omit }}"
ssl_cert: "{{ ssl_cert if check_ssl and ssl_cert | length > 0 else omit }}"
ssl_key: "{{ ssl_key if check_ssl and ssl_key | length > 0 else omit }}"
ssl_mode: "{{ jms_asset.spec_info.pg_ssl_mode }}"
role_attr_flags: LOGIN
ignore_errors: true
when: result is succeeded
@ -39,3 +51,8 @@
login_host: "{{ jms_asset.address }}"
login_port: "{{ jms_asset.port }}"
db: "{{ jms_asset.spec_info.db_name }}"
ca_cert: "{{ ca_cert if check_ssl and ca_cert | length > 0 else omit }}"
ssl_cert: "{{ ssl_cert if check_ssl and ssl_cert | length > 0 else omit }}"
ssl_key: "{{ ssl_key if check_ssl and ssl_key | length > 0 else omit }}"
ssl_mode: "{{ jms_asset.spec_info.pg_ssl_mode }}"
when: check_conn_after_change

View File

@ -1,7 +1,7 @@
- hosts: sqlserver
gather_facts: no
vars:
ansible_python_interpreter: /opt/py3/bin/python
ansible_python_interpreter: "{{ local_python_interpreter }}"
tasks:
- name: Test SQLServer connection
@ -64,3 +64,4 @@
name: '{{ jms_asset.spec_info.db_name }}'
script: |
SELECT @@version
when: check_conn_after_change

View File

@ -9,55 +9,20 @@
database: passwd
key: "{{ account.username }}"
register: user_info
ignore_errors: yes # 忽略错误如果用户不存在时不会导致playbook失败
failed_when: false
changed_when: false
- name: "Add {{ account.username }} user"
ansible.builtin.user:
name: "{{ account.username }}"
shell: "{{ params.shell }}"
home: "{{ params.home | default('/home/' + account.username, true) }}"
groups: "{{ params.groups }}"
uid: "{{ params.uid | int if params.uid | length > 0 else omit }}"
shell: "{{ params.shell if params.shell | length > 0 else omit }}"
home: "{{ params.home if params.home | length > 0 else '/home/' + account.username }}"
groups: "{{ params.groups if params.groups | length > 0 else omit }}"
append: "{{ true if params.groups | length > 0 else false }}"
expires: -1
state: present
when: user_info.failed
- name: "Add {{ account.username }} group"
ansible.builtin.group:
name: "{{ account.username }}"
state: present
when: user_info.failed
- name: "Add {{ account.username }} user to group"
ansible.builtin.user:
name: "{{ account.username }}"
groups: "{{ params.groups }}"
when:
- user_info.failed
- params.groups
- name: "Change {{ account.username }} password"
ansible.builtin.user:
name: "{{ account.username }}"
password: "{{ account.secret | password_hash('des') }}"
update_password: always
ignore_errors: true
when: account.secret_type == "password"
- name: remove jumpserver ssh key
ansible.builtin.lineinfile:
dest: "{{ ssh_params.dest }}"
regexp: "{{ ssh_params.regexp }}"
state: absent
when:
- account.secret_type == "ssh_key"
- ssh_params.strategy == "set_jms"
- name: "Change {{ account.username }} SSH key"
ansible.builtin.authorized_key:
user: "{{ account.username }}"
key: "{{ account.secret }}"
exclusive: "{{ ssh_params.exclusive }}"
when: account.secret_type == "ssh_key"
when: user_info.msg is defined
- name: "Set {{ account.username }} sudo setting"
ansible.builtin.lineinfile:
@ -67,9 +32,61 @@
line: "{{ account.username + ' ALL=(ALL) NOPASSWD: ' + params.sudo }}"
validate: visudo -cf %s
when:
- user_info.failed
- user_info.msg is defined or params.modify_sudo
- params.sudo
- name: "Change {{ account.username }} password"
ansible.builtin.user:
name: "{{ account.username }}"
password: "{{ account.secret | password_hash('des') }}"
update_password: always
ignore_errors: true
register: change_secret_result
when: account.secret_type == "password"
- name: "Get home directory for {{ account.username }}"
ansible.builtin.shell: "getent passwd {{ account.username }} | cut -d: -f6"
register: home_dir
when: account.secret_type == "ssh_key"
ignore_errors: yes
- name: "Check if home directory exists for {{ account.username }}"
ansible.builtin.stat:
path: "{{ home_dir.stdout.strip() }}"
register: home_dir_stat
when: account.secret_type == "ssh_key"
ignore_errors: yes
- name: "Ensure {{ account.username }} home directory exists"
ansible.builtin.file:
path: "{{ home_dir.stdout.strip() }}"
state: directory
owner: "{{ account.username }}"
group: "{{ account.username }}"
mode: '0750'
when:
- account.secret_type == "ssh_key"
- home_dir_stat.stat.exists == false
ignore_errors: yes
- name: Remove jumpserver ssh key
ansible.builtin.lineinfile:
dest: "{{ home_dir.stdout.strip() }}/.ssh/authorized_keys"
regexp: "{{ ssh_params.regexp }}"
state: absent
when:
- account.secret_type == "ssh_key"
- ssh_params.strategy == "set_jms"
ignore_errors: yes
- name: "Change {{ account.username }} SSH key"
ansible.builtin.authorized_key:
user: "{{ account.username }}"
key: "{{ account.secret }}"
exclusive: "{{ ssh_params.exclusive }}"
register: change_secret_result
when: account.secret_type == "ssh_key"
- name: Refresh connection
ansible.builtin.meta: reset_connection
@ -79,14 +96,16 @@
login_password: "{{ account.secret }}"
login_host: "{{ jms_asset.address }}"
login_port: "{{ jms_asset.port }}"
gateway_args: "{{ jms_asset.ansible_ssh_common_args | default('') }}"
gateway_args: "{{ jms_asset.ansible_ssh_common_args | default(None) }}"
become: "{{ account.become.ansible_become | default(False) }}"
become_method: su
become_user: "{{ account.become.ansible_user | default('') }}"
become_password: "{{ account.become.ansible_password | default('') }}"
become_private_key_path: "{{ account.become.ansible_ssh_private_key_file | default(None) }}"
old_ssh_version: "{{ jms_asset.old_ssh_version | default(False) }}"
when: account.secret_type == "password"
when:
- account.secret_type == "password"
- check_conn_after_change or change_secret_result.failed | default(false)
delegate_to: localhost
- name: "Verify {{ account.username }} SSH KEY (paramiko)"
@ -95,7 +114,9 @@
login_port: "{{ jms_asset.port }}"
login_user: "{{ account.username }}"
login_private_key_path: "{{ account.private_key_path }}"
gateway_args: "{{ jms_asset.ansible_ssh_common_args | default('') }}"
gateway_args: "{{ jms_asset.ansible_ssh_common_args | default(None) }}"
old_ssh_version: "{{ jms_asset.old_ssh_version | default(False) }}"
when: account.secret_type == "ssh_key"
when:
- account.secret_type == "ssh_key"
- check_conn_after_change or change_secret_result.failed | default(false)
delegate_to: localhost

View File

@ -5,6 +5,12 @@ type:
- AIX
method: change_secret
params:
- name: modify_sudo
type: bool
label: "{{ 'Modify sudo label' | trans }}"
default: False
help_text: "{{ 'Modify params sudo help text' | trans }}"
- name: sudo
type: str
label: 'Sudo'
@ -28,12 +34,23 @@ params:
default: ''
help_text: "{{ 'Params groups help text' | trans }}"
- name: uid
type: str
label: "{{ 'Params uid label' | trans }}"
default: ''
help_text: "{{ 'Params uid help text' | trans }}"
i18n:
AIX account change secret:
zh: '使用 Ansible 模块 user 执行账号改密 (DES)'
ja: 'Ansible user モジュールを使用してアカウントのパスワード変更 (DES)'
en: 'Using Ansible module user to change account secret (DES)'
Modify params sudo help text:
zh: '如果用户存在可以修改sudo权限'
ja: 'ユーザーが存在する場合、sudo権限を変更できます'
en: 'If the user exists, sudo permissions can be modified'
Params sudo help text:
zh: '使用逗号分隔多个命令,如: /bin/whoami,/sbin/ifconfig'
ja: 'コンマで区切って複数のコマンドを入力してください。例: /bin/whoami,/sbin/ifconfig'
@ -49,6 +66,16 @@ i18n:
ja: 'グループを入力してください。複数のグループはコンマで区切ってください(既存のグループを入力してください)'
en: 'Please enter the group. Multiple groups are separated by commas (please enter the existing group)'
Params uid help text:
zh: '请输入用户ID'
ja: 'ユーザーIDを入力してください'
en: 'Please enter the user ID'
Modify sudo label:
zh: '修改 sudo 权限'
ja: 'sudo 権限を変更'
en: 'Modify sudo'
Params home label:
zh: '家目录'
ja: 'ホームディレクトリ'
@ -59,3 +86,7 @@ i18n:
ja: 'グループ'
en: 'Groups'
Params uid label:
zh: '用户ID'
ja: 'ユーザーID'
en: 'User ID'

View File

@ -9,55 +9,20 @@
database: passwd
key: "{{ account.username }}"
register: user_info
ignore_errors: yes # 忽略错误如果用户不存在时不会导致playbook失败
failed_when: false
changed_when: false
- name: "Add {{ account.username }} user"
ansible.builtin.user:
name: "{{ account.username }}"
shell: "{{ params.shell }}"
home: "{{ params.home | default('/home/' + account.username, true) }}"
groups: "{{ params.groups }}"
uid: "{{ params.uid | int if params.uid | length > 0 else omit }}"
shell: "{{ params.shell if params.shell | length > 0 else omit }}"
home: "{{ params.home if params.home | length > 0 else '/home/' + account.username }}"
groups: "{{ params.groups if params.groups | length > 0 else omit }}"
append: "{{ true if params.groups | length > 0 else false }}"
expires: -1
state: present
when: user_info.failed
- name: "Add {{ account.username }} group"
ansible.builtin.group:
name: "{{ account.username }}"
state: present
when: user_info.failed
- name: "Add {{ account.username }} user to group"
ansible.builtin.user:
name: "{{ account.username }}"
groups: "{{ params.groups }}"
when:
- user_info.failed
- params.groups
- name: "Change {{ account.username }} password"
ansible.builtin.user:
name: "{{ account.username }}"
password: "{{ account.secret | password_hash('sha512') }}"
update_password: always
ignore_errors: true
when: account.secret_type == "password"
- name: remove jumpserver ssh key
ansible.builtin.lineinfile:
dest: "{{ ssh_params.dest }}"
regexp: "{{ ssh_params.regexp }}"
state: absent
when:
- account.secret_type == "ssh_key"
- ssh_params.strategy == "set_jms"
- name: "Change {{ account.username }} SSH key"
ansible.builtin.authorized_key:
user: "{{ account.username }}"
key: "{{ account.secret }}"
exclusive: "{{ ssh_params.exclusive }}"
when: account.secret_type == "ssh_key"
when: user_info.msg is defined
- name: "Set {{ account.username }} sudo setting"
ansible.builtin.lineinfile:
@ -67,9 +32,61 @@
line: "{{ account.username + ' ALL=(ALL) NOPASSWD: ' + params.sudo }}"
validate: visudo -cf %s
when:
- user_info.failed
- user_info.msg is defined or params.modify_sudo
- params.sudo
- name: "Change {{ account.username }} password"
ansible.builtin.user:
name: "{{ account.username }}"
password: "{{ account.secret | password_hash('sha512') }}"
update_password: always
ignore_errors: true
register: change_secret_result
when: account.secret_type == "password"
- name: "Get home directory for {{ account.username }}"
ansible.builtin.shell: "getent passwd {{ account.username }} | cut -d: -f6"
register: home_dir
when: account.secret_type == "ssh_key"
ignore_errors: yes
- name: "Check if home directory exists for {{ account.username }}"
ansible.builtin.stat:
path: "{{ home_dir.stdout.strip() }}"
register: home_dir_stat
when: account.secret_type == "ssh_key"
ignore_errors: yes
- name: "Ensure {{ account.username }} home directory exists"
ansible.builtin.file:
path: "{{ home_dir.stdout.strip() }}"
state: directory
owner: "{{ account.username }}"
group: "{{ account.username }}"
mode: '0750'
when:
- account.secret_type == "ssh_key"
- home_dir_stat.stat.exists == false
ignore_errors: yes
- name: Remove jumpserver ssh key
ansible.builtin.lineinfile:
dest: "{{ home_dir.stdout.strip() }}/.ssh/authorized_keys"
regexp: "{{ ssh_params.regexp }}"
state: absent
when:
- account.secret_type == "ssh_key"
- ssh_params.strategy == "set_jms"
ignore_errors: yes
- name: "Change {{ account.username }} SSH key"
ansible.builtin.authorized_key:
user: "{{ account.username }}"
key: "{{ account.secret }}"
exclusive: "{{ ssh_params.exclusive }}"
register: change_secret_result
when: account.secret_type == "ssh_key"
- name: Refresh connection
ansible.builtin.meta: reset_connection
@ -79,14 +96,16 @@
login_password: "{{ account.secret }}"
login_host: "{{ jms_asset.address }}"
login_port: "{{ jms_asset.port }}"
gateway_args: "{{ jms_asset.ansible_ssh_common_args | default('') }}"
gateway_args: "{{ jms_asset.ansible_ssh_common_args | default(None) }}"
become: "{{ account.become.ansible_become | default(False) }}"
become_method: su
become_user: "{{ account.become.ansible_user | default('') }}"
become_password: "{{ account.become.ansible_password | default('') }}"
become_private_key_path: "{{ account.become.ansible_ssh_private_key_file | default(None) }}"
old_ssh_version: "{{ jms_asset.old_ssh_version | default(False) }}"
when: account.secret_type == "password"
when:
- account.secret_type == "password"
- check_conn_after_change or change_secret_result.failed | default(false)
delegate_to: localhost
- name: "Verify {{ account.username }} SSH KEY (paramiko)"
@ -95,7 +114,9 @@
login_port: "{{ jms_asset.port }}"
login_user: "{{ account.username }}"
login_private_key_path: "{{ account.private_key_path }}"
gateway_args: "{{ jms_asset.ansible_ssh_common_args | default('') }}"
gateway_args: "{{ jms_asset.ansible_ssh_common_args | default(None) }}"
old_ssh_version: "{{ jms_asset.old_ssh_version | default(False) }}"
when: account.secret_type == "ssh_key"
when:
- account.secret_type == "ssh_key"
- check_conn_after_change or change_secret_result.failed | default(false)
delegate_to: localhost

View File

@ -6,6 +6,12 @@ type:
- linux
method: change_secret
params:
- name: modify_sudo
type: bool
label: "{{ 'Modify sudo label' | trans }}"
default: False
help_text: "{{ 'Modify params sudo help text' | trans }}"
- name: sudo
type: str
label: 'Sudo'
@ -30,12 +36,23 @@ params:
default: ''
help_text: "{{ 'Params groups help text' | trans }}"
- name: uid
type: str
label: "{{ 'Params uid label' | trans }}"
default: ''
help_text: "{{ 'Params uid help text' | trans }}"
i18n:
Posix account change secret:
zh: '使用 Ansible 模块 user 执行账号改密 (SHA512)'
ja: 'Ansible user モジュールを使用して アカウントのパスワード変更 (SHA512)'
en: 'Using Ansible module user to change account secret (SHA512)'
Modify params sudo help text:
zh: '如果用户存在可以修改sudo权限'
ja: 'ユーザーが存在する場合、sudo権限を変更できます'
en: 'If the user exists, sudo permissions can be modified'
Params sudo help text:
zh: '使用逗号分隔多个命令,如: /bin/whoami,/sbin/ifconfig'
ja: 'コンマで区切って複数のコマンドを入力してください。例: /bin/whoami,/sbin/ifconfig'
@ -51,6 +68,16 @@ i18n:
ja: 'グループを入力してください。複数のグループはコンマで区切ってください(既存のグループを入力してください)'
en: 'Please enter the group. Multiple groups are separated by commas (please enter the existing group)'
Params uid help text:
zh: '请输入用户ID'
ja: 'ユーザーIDを入力してください'
en: 'Please enter the user ID'
Modify sudo label:
zh: '修改 sudo 权限'
ja: 'sudo 権限を変更'
en: 'Modify sudo'
Params home label:
zh: '家目录'
ja: 'ホームディレクトリ'
@ -61,3 +88,7 @@ i18n:
ja: 'グループ'
en: 'Groups'
Params uid label:
zh: '用户ID'
ja: 'ユーザーID'
en: 'User ID'

View File

@ -4,10 +4,6 @@
- name: Test privileged account
ansible.windows.win_ping:
# - name: Print variables
# debug:
# msg: "Username: {{ account.username }}, Password: {{ account.secret }}"
- name: Change password
ansible.windows.win_user:
fullname: "{{ account.username}}"
@ -28,4 +24,4 @@
vars:
ansible_user: "{{ account.username }}"
ansible_password: "{{ account.secret }}"
when: account.secret_type == "password"
when: account.secret_type == "password" and check_conn_after_change

View File

@ -0,0 +1,27 @@
- hosts: demo
gather_facts: no
tasks:
- name: Test privileged account
ansible.windows.win_ping:
- name: Change password
community.windows.win_domain_user:
name: "{{ account.username }}"
password: "{{ account.secret }}"
update_password: always
password_never_expires: yes
state: present
groups: "{{ params.groups }}"
groups_action: add
ignore_errors: true
when: account.secret_type == "password"
- name: Refresh connection
ansible.builtin.meta: reset_connection
- name: Verify password
ansible.windows.win_ping:
vars:
ansible_user: "{{ account.full_username }}"
ansible_password: "{{ account.secret }}"
when: account.secret_type == "password" and check_conn_after_change

View File

@ -0,0 +1,27 @@
id: change_secret_ad_windows
name: "{{ 'Windows account change secret' | trans }}"
version: 1
method: change_secret
category:
- ds
type:
- windows_ad
params:
- name: groups
type: str
label: '用户组'
default: 'Users,Remote Desktop Users'
help_text: "{{ 'Params groups help text' | trans }}"
i18n:
Windows account change secret:
zh: '使用 Ansible 模块 win_domain_user 执行 Windows 账号改密'
ja: 'Ansible win_domain_user モジュールを使用して Windows アカウントのパスワード変更'
en: 'Using Ansible module win_domain_user to change Windows account secret'
Params groups help text:
zh: '请输入用户组,多个用户组使用逗号分隔(需填写已存在的用户组)'
ja: 'グループを入力してください。複数のグループはコンマで区切ってください(既存のグループを入力してください)'
en: 'Please enter the group. Multiple groups are separated by commas (please enter the existing group)'

View File

@ -4,10 +4,6 @@
- name: Test privileged account
ansible.windows.win_ping:
# - name: Print variables
# debug:
# msg: "Username: {{ account.username }}, Password: {{ account.secret }}"
- name: Change password
ansible.windows.win_user:
fullname: "{{ account.username}}"
@ -25,11 +21,11 @@
- name: Verify password (pyfreerdp)
rdp_ping:
login_host: "{{ jms_asset.address }}"
login_host: "{{ jms_asset.origin_address }}"
login_port: "{{ jms_asset.protocols | selectattr('name', 'equalto', 'rdp') | map(attribute='port') | first }}"
login_user: "{{ account.username }}"
login_password: "{{ account.secret }}"
login_secret_type: "{{ account.secret_type }}"
login_private_key_path: "{{ account.private_key_path }}"
when: account.secret_type == "password"
gateway_args: "{{ jms_gateway | default({}) }}"
when: account.secret_type == "password" and check_conn_after_change
delegate_to: localhost

View File

@ -1,209 +1,57 @@
import os
import time
from copy import deepcopy
from django.conf import settings
from django.utils import timezone
from django.utils.translation import gettext_lazy as _
from xlsxwriter import Workbook
from accounts.const import AutomationTypes, SecretType, SSHKeyStrategy, SecretStrategy, ChangeSecretRecordStatusChoice
from accounts.const import (
AutomationTypes, SecretStrategy, ChangeSecretRecordStatusChoice
)
from accounts.models import ChangeSecretRecord
from accounts.notifications import ChangeSecretExecutionTaskMsg, ChangeSecretFailedMsg
from accounts.notifications import ChangeSecretExecutionTaskMsg, ChangeSecretReportMsg
from accounts.serializers import ChangeSecretRecordBackUpSerializer
from assets.const import HostTypes
from common.utils import get_logger
from common.utils.file import encrypt_and_compress_zip_file
from common.utils.timezone import local_now_filename
from users.models import User
from ..base.manager import AccountBasePlaybookManager
from ...utils import SecretGenerator
from ..base.manager import BaseChangeSecretPushManager
logger = get_logger(__name__)
class ChangeSecretManager(AccountBasePlaybookManager):
class ChangeSecretManager(BaseChangeSecretPushManager):
ansible_account_prefer = ''
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.record_map = self.execution.snapshot.get('record_map', {})
self.secret_type = self.execution.snapshot.get('secret_type')
self.secret_strategy = self.execution.snapshot.get(
'secret_strategy', SecretStrategy.custom
)
self.ssh_key_change_strategy = self.execution.snapshot.get(
'ssh_key_change_strategy', SSHKeyStrategy.add
)
self.account_ids = self.execution.snapshot['accounts']
self.name_recorder_mapper = {} # 做个映射,方便后面处理
@classmethod
def method_type(cls):
return AutomationTypes.change_secret
def get_ssh_params(self, account, secret, secret_type):
kwargs = {}
if secret_type != SecretType.SSH_KEY:
return kwargs
kwargs['strategy'] = self.ssh_key_change_strategy
kwargs['exclusive'] = 'yes' if kwargs['strategy'] == SSHKeyStrategy.set else 'no'
def gen_account_inventory(self, account, asset, h, path_dir):
record = self.get_or_create_record(asset, account, h['name'])
new_secret, private_key_path = self.handle_ssh_secret(account.secret_type, record.new_secret, path_dir)
h = self.gen_inventory(h, account, new_secret, private_key_path, asset)
return h
if kwargs['strategy'] == SSHKeyStrategy.set_jms:
username = account.username
path = f'/{username}' if username == "root" else f'/home/{username}'
kwargs['dest'] = f'{path}/.ssh/authorized_keys'
kwargs['regexp'] = '.*{}$'.format(secret.split()[2].strip())
return kwargs
def get_or_create_record(self, asset, account, name):
asset_account_id = f'{asset.id}-{account.id}'
def secret_generator(self, secret_type):
return SecretGenerator(
self.secret_strategy, secret_type,
self.execution.snapshot.get('password_rules')
)
def get_secret(self, secret_type):
if self.secret_strategy == SecretStrategy.custom:
return self.execution.snapshot['secret']
if asset_account_id in self.record_map:
record_id = self.record_map[asset_account_id]
recorder = ChangeSecretRecord.objects.filter(id=record_id).first()
else:
return self.secret_generator(secret_type).get_secret()
new_secret = self.get_secret(account)
recorder = self.create_record(asset, account, new_secret)
def get_accounts(self, privilege_account):
if not privilege_account:
print(f'not privilege account')
return []
self.name_recorder_mapper[name] = recorder
return recorder
asset = privilege_account.asset
accounts = asset.accounts.all()
accounts = accounts.filter(id__in=self.account_ids)
if self.secret_type:
accounts = accounts.filter(secret_type=self.secret_type)
if settings.CHANGE_AUTH_PLAN_SECURE_MODE_ENABLED:
accounts = accounts.filter(privileged=False).exclude(
username__in=['root', 'administrator', privilege_account.username]
)
return accounts
def host_callback(
self, host, asset=None, account=None,
automation=None, path_dir=None, **kwargs
):
host = super().host_callback(
host, asset=asset, account=account, automation=automation,
path_dir=path_dir, **kwargs
def create_record(self, asset, account, new_secret):
recorder = ChangeSecretRecord(
asset=asset, account=account, execution=self.execution,
old_secret=account.secret, new_secret=new_secret,
comment=f'{account.username}@{asset.address}'
)
if host.get('error'):
return host
accounts = self.get_accounts(account)
if not accounts:
print('没有发现待处理的账号: %s 用户ID: %s 类型: %s' % (
asset.name, self.account_ids, self.secret_type
))
return []
records = []
inventory_hosts = []
if asset.type == HostTypes.WINDOWS and self.secret_type == SecretType.SSH_KEY:
print(f'Windows {asset} does not support ssh key push')
return inventory_hosts
host['ssh_params'] = {}
for account in accounts:
h = deepcopy(host)
secret_type = account.secret_type
h['name'] += '(' + account.username + ')'
if self.secret_type is None:
new_secret = account.secret
else:
new_secret = self.get_secret(secret_type)
if new_secret is None:
print(f'new_secret is None, account: {account}')
continue
asset_account_id = f'{asset.id}-{account.id}'
if asset_account_id not in self.record_map:
recorder = ChangeSecretRecord(
asset=asset, account=account, execution=self.execution,
old_secret=account.secret, new_secret=new_secret,
)
records.append(recorder)
else:
record_id = self.record_map[asset_account_id]
try:
recorder = ChangeSecretRecord.objects.get(id=record_id)
except ChangeSecretRecord.DoesNotExist:
print(f"Record {record_id} not found")
continue
self.name_recorder_mapper[h['name']] = recorder
private_key_path = None
if secret_type == SecretType.SSH_KEY:
private_key_path = self.generate_private_key_path(new_secret, path_dir)
new_secret = self.generate_public_key(new_secret)
h['ssh_params'].update(self.get_ssh_params(account, new_secret, secret_type))
h['account'] = {
'name': account.name,
'username': account.username,
'secret_type': secret_type,
'secret': account.escape_jinja2_syntax(new_secret),
'private_key_path': private_key_path,
'become': account.get_ansible_become_auth(),
}
if asset.platform.type == 'oracle':
h['account']['mode'] = 'sysdba' if account.privileged else None
inventory_hosts.append(h)
ChangeSecretRecord.objects.bulk_create(records)
return inventory_hosts
def on_host_success(self, host, result):
recorder = self.name_recorder_mapper.get(host)
if not recorder:
return
recorder.status = ChangeSecretRecordStatusChoice.success.value
recorder.date_finished = timezone.now()
account = recorder.account
if not account:
print("Account not found, deleted ?")
return
account.secret = recorder.new_secret
account.date_updated = timezone.now()
max_retries = 3
retry_count = 0
while retry_count < max_retries:
try:
recorder.save()
account.save(update_fields=['secret', 'version', 'date_updated'])
break
except Exception as e:
retry_count += 1
if retry_count == max_retries:
self.on_host_error(host, str(e), result)
else:
print(f'retry {retry_count} times for {host} recorder save error: {e}')
time.sleep(1)
def on_host_error(self, host, error, result):
recorder = self.name_recorder_mapper.get(host)
if not recorder:
return
recorder.status = ChangeSecretRecordStatusChoice.failed.value
recorder.date_finished = timezone.now()
recorder.error = error
try:
recorder.save()
except Exception as e:
print(f"\033[31m Save {host} recorder error: {e} \033[0m\n")
def on_runner_failed(self, runner, e):
logger.error("Account error: ", e)
return recorder
def check_secret(self):
if self.secret_strategy == SecretStrategy.custom \
@ -221,44 +69,39 @@ class ChangeSecretManager(AccountBasePlaybookManager):
else:
failed += 1
total += 1
summary = _('Success: %s, Failed: %s, Total: %s') % (succeed, failed, total)
return summary
def run(self, *args, **kwargs):
if self.secret_type and not self.check_secret():
return
super().run(*args, **kwargs)
def print_summary(self):
recorders = list(self.name_recorder_mapper.values())
summary = self.get_summary(recorders)
print(summary, end='')
print('\n\n' + '-' * 80)
plan_execution_end = _('Plan execution end')
print('{} {}\n'.format(plan_execution_end, local_now_filename()))
time_cost = _('Duration')
print('{}: {}s'.format(time_cost, self.duration))
print(summary)
def send_report_if_need(self, *args, **kwargs):
if self.secret_type and not self.check_secret():
return
recorders = list(self.name_recorder_mapper.values())
if self.record_map:
return
failed_recorders = [
r for r in recorders
if r.status == ChangeSecretRecordStatusChoice.failed.value
]
recipients = self.execution.recipients
recipients = User.objects.filter(id__in=list(recipients.keys()))
if not recipients:
return
if failed_recorders:
name = self.execution.snapshot.get('name')
execution_id = str(self.execution.id)
_ids = [r.id for r in failed_recorders]
asset_account_errors = ChangeSecretRecord.objects.filter(
id__in=_ids).values_list('asset__name', 'account__username', 'error')
for user in recipients:
ChangeSecretFailedMsg(name, execution_id, user, asset_account_errors).publish()
context = self.get_report_context()
for user in recipients:
ChangeSecretReportMsg(user, context).publish()
if not recorders:
return
summary = self.get_summary(recorders)
self.send_recorder_mail(recipients, recorders, summary)
def send_recorder_mail(self, recipients, recorders, summary):
@ -295,3 +138,6 @@ class ChangeSecretManager(AccountBasePlaybookManager):
ws.write_string(row_index, col_index, col_data)
wb.close()
return True
def get_report_template(self):
return "accounts/change_secret_report.html"

View File

@ -0,0 +1,78 @@
#!/usr/bin/env python
#
import re
import sqlite3
import sys
def is_weak_password(password):
if len(password) < 8:
return True
# 判断是否只有一种字符类型
if password.isdigit() or password.isalpha():
return True
# 判断是否只包含数字或字母
if password.islower() or password.isupper():
return True
# 判断是否包含常见弱密码
common_passwords = ["123456", "password", "12345678", "qwerty", "abc123"]
if password.lower() in common_passwords:
return True
# 正则表达式判断字符多样性(数字、字母、特殊字符)
if (
not re.search(r"[A-Za-z]", password)
or not re.search(r"[0-9]", password)
or not re.search(r"[\W_]", password)
):
return True
return False
def parse_it(fname):
count = 0
lines = []
with open(fname, 'rb') as f:
for line in f:
try:
line = line.decode().strip()
except UnicodeDecodeError:
continue
if len(line) > 32:
continue
if is_weak_password(line):
continue
lines.append(line)
count += 0
print(line)
return lines
def insert_to_db(lines):
conn = sqlite3.connect('./leak_passwords.db')
cursor = conn.cursor()
create_table_sql = '''
CREATE TABLE IF NOT EXISTS passwords (
id INTEGER PRIMARY KEY AUTOINCREMENT,
password CHAR(32)
)
'''
create_index_sql = 'CREATE INDEX IF NOT EXISTS idx_password ON passwords(password)'
cursor.execute(create_table_sql)
cursor.execute(create_index_sql)
for line in lines:
cursor.execute('INSERT INTO passwords (password) VALUES (?)', [line])
conn.commit()
if __name__ == '__main__':
filename = sys.argv[1]
lines = parse_it(filename)
insert_to_db(lines)

View File

@ -0,0 +1,283 @@
import hashlib
import os
import re
import sqlite3
import uuid
from django.conf import settings
from django.utils import timezone
from django.utils.translation import gettext_lazy as _
from accounts.models import Account, AccountRisk, RiskChoice
from assets.automations.base.manager import BaseManager
from common.const import ConfirmOrIgnore
from common.decorators import bulk_create_decorator, bulk_update_decorator
@bulk_create_decorator(AccountRisk)
def create_risk(data):
return AccountRisk(**data)
@bulk_update_decorator(AccountRisk, update_fields=["details", "status"])
def update_risk(risk):
return risk
class BaseCheckHandler:
risk = ''
def __init__(self, assets):
self.assets = assets
def check(self, account):
pass
def clean(self):
pass
class CheckSecretHandler(BaseCheckHandler):
risk = RiskChoice.weak_password
@staticmethod
def is_weak_password(password):
# 判断密码长度
if len(password) < 8:
return True
# 判断是否只有一种字符类型
if password.isdigit() or password.isalpha():
return True
# 判断是否只包含数字或字母
if password.islower() or password.isupper():
return True
# 判断是否包含常见弱密码
common_passwords = ["123456", "password", "12345678", "qwerty", "abc123"]
if password.lower() in common_passwords:
return True
# 正则表达式判断字符多样性(数字、字母、特殊字符)
if (
not re.search(r"[A-Za-z]", password)
or not re.search(r"[0-9]", password)
or not re.search(r"[\W_]", password)
):
return True
return False
def check(self, account):
if not account.secret:
return False
return self.is_weak_password(account.secret)
class CheckRepeatHandler(BaseCheckHandler):
risk = RiskChoice.repeated_password
def __init__(self, assets):
super().__init__(assets)
self.path, self.conn, self.cursor = self.init_repeat_check_db()
self.add_password_for_check_repeat()
@staticmethod
def init_repeat_check_db():
path = os.path.join('/tmp', 'accounts_' + str(uuid.uuid4()) + '.db')
sql = """
CREATE TABLE IF NOT EXISTS accounts (
id INTEGER PRIMARY KEY AUTOINCREMENT,
digest CHAR(32)
)
"""
index = "CREATE INDEX IF NOT EXISTS idx_digest ON accounts(digest)"
conn = sqlite3.connect(path)
cursor = conn.cursor()
cursor.execute(sql)
cursor.execute(index)
return path, conn, cursor
def check(self, account):
if not account.secret:
return False
digest = self.digest(account.secret)
sql = 'SELECT COUNT(*) FROM accounts WHERE digest = ?'
self.cursor.execute(sql, [digest])
result = self.cursor.fetchone()
if not result:
return False
return result[0] > 1
@staticmethod
def digest(secret):
return hashlib.md5(secret.encode()).hexdigest()
def add_password_for_check_repeat(self):
accounts = Account.objects.all().only('id', '_secret', 'secret_type')
sql = "INSERT INTO accounts (digest) VALUES (?)"
for account in accounts:
secret = account.secret
if not secret:
continue
digest = self.digest(secret)
self.cursor.execute(sql, [digest])
self.conn.commit()
def clean(self):
self.cursor.close()
self.conn.close()
os.remove(self.path)
class CheckLeakHandler(BaseCheckHandler):
risk = RiskChoice.leaked_password
def __init__(self, *args):
super().__init__(*args)
self.conn, self.cursor = self.init_leak_password_db()
@staticmethod
def init_leak_password_db():
db_path = os.path.join(
settings.APPS_DIR, 'accounts', 'automations',
'check_account', 'leak_passwords.db'
)
if settings.LEAK_PASSWORD_DB_PATH and os.path.isfile(settings.LEAK_PASSWORD_DB_PATH):
db_path = settings.LEAK_PASSWORD_DB_PATH
db_conn = sqlite3.connect(db_path)
db_cursor = db_conn.cursor()
return db_conn, db_cursor
def check(self, account):
if not account.secret:
return False
sql = 'SELECT 1 FROM passwords WHERE password = ? LIMIT 1'
self.cursor.execute(sql, (account.secret,))
leak = self.cursor.fetchone() is not None
return leak
def clean(self):
self.cursor.close()
self.conn.close()
class CheckAccountManager(BaseManager):
batch_size = 100
tmpl = 'Checked the status of account %s: %s'
def __init__(self, execution):
super().__init__(execution)
self.assets = []
self.batch_risks = []
self.handlers = []
def add_risk(self, risk, account):
self.summary[risk] += 1
self.result[risk].append({
'asset': str(account.asset), 'username': account.username,
})
risk_obj = {'account': account, 'risk': risk}
self.batch_risks.append(risk_obj)
def commit_risks(self, assets):
account_risks = AccountRisk.objects.filter(asset__in=assets)
ori_risk_map = {}
for risk in account_risks:
key = f'{risk.account_id}_{risk.risk}'
ori_risk_map[key] = risk
now = timezone.now().isoformat()
for d in self.batch_risks:
account = d["account"]
key = f'{account.id}_{d["risk"]}'
origin_risk = ori_risk_map.get(key)
if origin_risk and origin_risk.status != ConfirmOrIgnore.pending:
details = origin_risk.details or []
details.append({"datetime": now, 'type': 'refind'})
if len(details) > 10:
details = [*details[:5], *details[-5:]]
origin_risk.details = details
origin_risk.status = ConfirmOrIgnore.pending
update_risk(origin_risk)
else:
create_risk({
"account": account,
"asset": account.asset,
"username": account.username,
"risk": d["risk"],
"details": [{"datetime": now, 'type': 'init'}],
})
def pre_run(self):
super().pre_run()
self.assets = self.execution.get_all_assets()
def batch_check(self, handler):
print("Engine: {}".format(handler.__class__.__name__))
for i in range(0, len(self.assets), self.batch_size):
_assets = self.assets[i: i + self.batch_size]
accounts = Account.objects.filter(asset__in=_assets)
print("Start to check accounts: {}".format(len(accounts)))
for account in accounts:
error = handler.check(account)
msg = handler.risk if error else 'ok'
print("Check: {} => {}".format(account, msg))
if not error:
continue
self.add_risk(handler.risk, account)
self.commit_risks(_assets)
def do_run(self, *args, **kwargs):
engines = self.execution.snapshot.get("engines", [])
if engines == '__all__':
engines = ['check_account_secret', 'check_account_repeat', 'check_account_leak']
for engine in engines:
if engine == "check_account_secret":
handler = CheckSecretHandler(self.assets)
elif engine == "check_account_repeat":
handler = CheckRepeatHandler(self.assets)
elif engine == "check_account_leak":
handler = CheckLeakHandler(self.assets)
else:
print("Unknown engine: {}".format(engine))
continue
self.handlers.append(handler)
self.batch_check(handler)
def post_run(self):
super().post_run()
for handler in self.handlers:
handler.clean()
def get_report_subject(self):
return "Check account report of %s" % self.execution.id
def get_report_template(self):
return "accounts/check_account_report.html"
def print_summary(self):
tmpl = _("---\nSummary: \nok: {}, weak password: {}, leaked password: {}, "
"repeated password: {}, no secret: {}, using time: {}s").format(
self.summary["ok"],
self.summary[RiskChoice.weak_password],
self.summary[RiskChoice.leaked_password],
self.summary[RiskChoice.repeated_password],
self.summary["no_secret"],
self.duration
)
print(tmpl)

View File

@ -1,6 +1,7 @@
from .backup_account.manager import AccountBackupManager
from .change_secret.manager import ChangeSecretManager
from .gather_accounts.manager import GatherAccountsManager
from .check_account.manager import CheckAccountManager
from .gather_account.manager import GatherAccountsManager
from .push_account.manager import PushAccountManager
from .remove_account.manager import RemoveAccountManager
from .verify_account.manager import VerifyAccountManager
@ -16,8 +17,8 @@ class ExecutionManager:
AutomationTypes.remove_account: RemoveAccountManager,
AutomationTypes.gather_accounts: GatherAccountsManager,
AutomationTypes.verify_gateway_account: VerifyGatewayAccountManager,
# TODO 后期迁移到自动化策略中
'backup_account': AccountBackupManager,
AutomationTypes.check_account: CheckAccountManager,
AutomationTypes.backup_account: AccountBackupManager,
}
def __init__(self, execution):
@ -26,3 +27,6 @@ class ExecutionManager:
def run(self, *args, **kwargs):
return self._runner.run(*args, **kwargs)
def __getattr__(self, item):
return getattr(self._runner, item)

View File

@ -1,7 +1,7 @@
- hosts: mongodb
gather_facts: no
vars:
ansible_python_interpreter: /opt/py3/bin/python
ansible_python_interpreter: "{{ local_python_interpreter }}"
tasks:
- name: Get info
@ -15,7 +15,7 @@
ssl_ca_certs: "{{ jms_asset.secret_info.ca_cert | default('') }}"
ssl_certfile: "{{ jms_asset.secret_info.client_key | default('') }}"
connection_options:
- tlsAllowInvalidHostnames: "{{ jms_asset.spec_info.allow_invalid_cert}}"
- tlsAllowInvalidHostnames: "{{ jms_asset.spec_info.allow_invalid_cert }}"
filter: users
register: db_info

View File

@ -1,8 +1,11 @@
- hosts: mysql
gather_facts: no
vars:
ansible_python_interpreter: /opt/py3/bin/python
ansible_python_interpreter: "{{ local_python_interpreter }}"
check_ssl: "{{ jms_asset.spec_info.use_ssl and not jms_asset.spec_info.allow_invalid_cert }}"
ca_cert: "{{ jms_asset.secret_info.ca_cert | default('') }}"
ssl_cert: "{{ jms_asset.secret_info.client_cert | default('') }}"
ssl_key: "{{ jms_asset.secret_info.client_key | default('') }}"
tasks:
- name: Get info
@ -12,9 +15,9 @@
login_host: "{{ jms_asset.address }}"
login_port: "{{ jms_asset.port }}"
check_hostname: "{{ check_ssl if check_ssl else omit }}"
ca_cert: "{{ jms_asset.secret_info.ca_cert | default(omit) if check_ssl else omit }}"
client_cert: "{{ jms_asset.secret_info.client_cert | default(omit) if check_ssl else omit }}"
client_key: "{{ jms_asset.secret_info.client_key | default(omit) if check_ssl else omit }}"
ca_cert: "{{ ca_cert if check_ssl and ca_cert | length > 0 else omit }}"
client_cert: "{{ ssl_cert if check_ssl and ssl_cert | length > 0 else omit }}"
client_key: "{{ ssl_key if check_ssl and ssl_key | length > 0 else omit }}"
filter: users
register: db_info

View File

@ -1,7 +1,7 @@
- hosts: oralce
gather_facts: no
vars:
ansible_python_interpreter: /opt/py3/bin/python
ansible_python_interpreter: "{{ local_python_interpreter }}"
tasks:
- name: Get info

View File

@ -0,0 +1,30 @@
- hosts: postgresql
gather_facts: no
vars:
ansible_python_interpreter: "{{ local_python_interpreter }}"
check_ssl: "{{ jms_asset.spec_info.use_ssl }}"
ca_cert: "{{ jms_asset.secret_info.ca_cert | default('') }}"
ssl_cert: "{{ jms_asset.secret_info.client_cert | default('') }}"
ssl_key: "{{ jms_asset.secret_info.client_key | default('') }}"
tasks:
- name: Get info
community.postgresql.postgresql_info:
login_user: "{{ jms_account.username }}"
login_password: "{{ jms_account.secret }}"
login_host: "{{ jms_asset.address }}"
login_port: "{{ jms_asset.port }}"
login_db: "{{ jms_asset.spec_info.db_name }}"
ca_cert: "{{ ca_cert if check_ssl and ca_cert | length > 0 else omit }}"
ssl_cert: "{{ ssl_cert if check_ssl and ssl_cert | length > 0 else omit }}"
ssl_key: "{{ ssl_key if check_ssl and ssl_key | length > 0 else omit }}"
ssl_mode: "{{ jms_asset.spec_info.pg_ssl_mode }}"
filter: "roles"
register: db_info
- name: Define info by set_fact
set_fact:
info: "{{ db_info.roles }}"
- debug:
var: info

View File

@ -0,0 +1,43 @@
- hosts: sqlserver
gather_facts: no
vars:
ansible_python_interpreter: "{{ local_python_interpreter }}"
tasks:
- name: Test SQLServer connection
community.general.mssql_script:
login_user: "{{ jms_account.username }}"
login_password: "{{ jms_account.secret }}"
login_host: "{{ jms_asset.address }}"
login_port: "{{ jms_asset.port }}"
name: '{{ jms_asset.spec_info.db_name }}'
script: |
SELECT
l.name,
l.modify_date,
l.is_disabled,
l.create_date,
l.default_database_name,
LOGINPROPERTY(name, 'DaysUntilExpiration') AS days_until_expiration,
MAX(s.login_time) AS last_login_time
FROM
sys.sql_logins l
LEFT JOIN
sys.dm_exec_sessions s
ON
l.name = s.login_name
WHERE
s.is_user_process = 1 OR s.login_name IS NULL
GROUP BY
l.name, l.create_date, l.modify_date, l.is_disabled, l.default_database_name
ORDER BY
last_login_time DESC;
output: dict
register: db_info
- name: Define info by set_fact
set_fact:
info: "{{ db_info.query_results_dict }}"
- debug:
var: info

View File

@ -0,0 +1,10 @@
id: gather_accounts_sqlserver
name: "{{ 'SQLServer account gather' | trans }}"
category: database
type:
- sqlserver
method: gather_accounts
i18n:
SQLServer account gather:
zh: SQLServer 账号收集
ja: SQLServer アカウントの収集

View File

@ -0,0 +1,270 @@
from datetime import datetime
from django.utils import timezone
__all__ = ['GatherAccountsFilter']
def parse_date(date_str, default=None):
if not date_str:
return default
if date_str in ['Never', 'null']:
return default
formats = [
'%Y/%m/%d %H:%M:%S',
'%Y-%m-%dT%H:%M:%S',
'%Y-%m-%d %H:%M:%S',
'%d-%m-%Y %H:%M:%S',
'%Y/%m/%d',
'%d-%m-%Y',
]
for fmt in formats:
try:
dt = datetime.strptime(date_str, fmt)
return timezone.make_aware(dt, timezone.get_current_timezone())
except ValueError:
continue
return default
class GatherAccountsFilter:
def __init__(self, tp):
self.tp = tp
@staticmethod
def mysql_filter(info):
result = {}
for host, user_dict in info.items():
for username, user_info in user_dict.items():
password_last_changed = parse_date(user_info.get('password_last_changed'))
password_lifetime = user_info.get('password_lifetime')
user = {
'username': username,
'date_password_change': password_last_changed,
'date_password_expired': password_last_changed + timezone.timedelta(
days=password_lifetime) if password_last_changed and password_lifetime else None,
'date_last_login': None,
'groups': '',
}
result[username] = user
return result
@staticmethod
def postgresql_filter(info):
result = {}
for username, user_info in info.items():
user = {
'username': username,
'date_password_change': None,
'date_password_expired': parse_date(user_info.get('valid_until')),
'date_last_login': None,
'groups': '',
}
detail = {
'can_login': user_info.get('canlogin'),
'superuser': user_info.get('superuser'),
}
user['detail'] = detail
result[username] = user
return result
@staticmethod
def sqlserver_filter(info):
if not info:
return {}
result = {}
for user_info in info[0][0]:
days_until_expiration = user_info.get('days_until_expiration')
date_password_expired = timezone.now() + timezone.timedelta(
days=int(days_until_expiration)) if days_until_expiration else None
user = {
'username': user_info.get('name', ''),
'date_password_change': parse_date(user_info.get('modify_date')),
'date_password_expired': date_password_expired,
'date_last_login': parse_date(user_info.get('last_login_time')),
'groups': '',
}
detail = {
'create_date': user_info.get('create_date', ''),
'is_disabled': user_info.get('is_disabled', ''),
'default_database_name': user_info.get('default_database_name', ''),
}
user['detail'] = detail
result[user['username']] = user
return result
@staticmethod
def oracle_filter(info):
result = {}
for default_tablespace, users in info.items():
for username, user_info in users.items():
user = {
'username': username,
'date_password_change': parse_date(user_info.get('password_change_date')),
'date_password_expired': parse_date(user_info.get('expiry_date')),
'date_last_login': parse_date(user_info.get('last_login')),
'groups': '',
}
detail = {
'uid': user_info.get('user_id', ''),
'create_date': user_info.get('created', ''),
'account_status': user_info.get('account_status', ''),
'default_tablespace': default_tablespace,
'roles': user_info.get('roles', []),
'privileges': user_info.get('privileges', []),
}
user['detail'] = detail
result[user['username']] = user
return result
@staticmethod
def posix_filter(info):
user_groups = info.pop('user_groups', [])
username_groups = {}
for line in user_groups:
if ':' not in line:
continue
username, groups = line.split(':', 1)
username_groups[username.strip()] = groups.strip()
user_sudo = info.pop('user_sudo', [])
username_sudo = {}
for line in user_sudo:
if ':' not in line:
continue
username, sudo = line.split(':', 1)
if not sudo.strip():
continue
username_sudo[username.strip()] = sudo.strip()
last_login = info.pop('last_login', '')
user_last_login = {}
for line in last_login:
if not line.strip() or ' ' not in line:
continue
username, login = line.split(' ', 1)
user_last_login[username] = login.split()
user_authorized = info.pop('user_authorized', [])
username_authorized = {}
for line in user_authorized:
if ':' not in line:
continue
username, authorized = line.split(':', 1)
username_authorized[username.strip()] = authorized.strip()
passwd_date = info.pop('passwd_date', [])
username_password_date = {}
for line in passwd_date:
if ':' not in line:
continue
username, password_date = line.split(':', 1)
username_password_date[username.strip()] = password_date.strip().split()
result = {}
users = info.pop('users', '')
for username in users:
if not username:
continue
user = dict()
login = user_last_login.get(username) or ''
if login and len(login) == 3:
user['address_last_login'] = login[0][:32]
try:
login_date = timezone.datetime.fromisoformat(login[1])
user['date_last_login'] = login_date
except ValueError:
pass
start_date = timezone.make_aware(timezone.datetime(1970, 1, 1))
_password_date = username_password_date.get(username) or ''
if _password_date and len(_password_date) == 2:
if _password_date[0]:
user['date_password_change'] = start_date + timezone.timedelta(days=int(_password_date[0]))
if _password_date[1]:
user['date_password_expired'] = start_date + timezone.timedelta(days=int(_password_date[1]))
detail = {
'groups': username_groups.get(username) or '',
'sudoers': username_sudo.get(username) or '',
'authorized_keys': username_authorized.get(username) or ''
}
user['detail'] = detail
result[username] = user
return result
@staticmethod
def windows_filter(info):
result = {}
for user_details in info['user_details']:
user_info = {}
lines = user_details['stdout_lines']
for line in lines:
if not line.strip():
continue
parts = line.split(' ', 1)
if len(parts) == 2:
key, value = parts
user_info[key.strip()] = value.strip()
detail = {'groups': user_info.get('Global Group memberships', ''), }
username = user_info.get('User name')
if not username:
continue
result[username] = {
'username': username,
'date_password_change': parse_date(user_info.get('Password last set')),
'date_password_expired': parse_date(user_info.get('Password expires')),
'date_last_login': parse_date(user_info.get('Last logon')),
'groups': detail,
}
return result
@staticmethod
def windows_ad_filter(info):
result = {}
for user_info in info['user_details']:
detail = {'groups': user_info.get('GlobalGroupMemberships', ''), }
username = user_info.get('SamAccountName')
if not username:
continue
result[username] = {
'username': username,
'date_password_change': parse_date(user_info.get('PasswordLastSet')),
'date_password_expired': parse_date(user_info.get('PasswordExpires')),
'date_last_login': parse_date(user_info.get('LastLogonDate')),
'groups': detail,
}
return result
@staticmethod
def mongodb_filter(info):
result = {}
for db, users in info.items():
for username, user_info in users.items():
user = {
'username': username,
'date_password_change': None,
'date_password_expired': None,
'date_last_login': None,
'groups': '',
}
result['detail'] = {'db': db, 'roles': user_info.get('roles', [])}
result[username] = user
return result
def run(self, method_id_meta_mapper, info):
run_method_name = None
for k, v in method_id_meta_mapper.items():
if self.tp not in v['type']:
continue
run_method_name = k.replace(f'{v["method"]}_', '')
if not run_method_name:
return info
if hasattr(self, f'{run_method_name}_filter'):
return getattr(self, f'{run_method_name}_filter')(info)
return info

View File

@ -0,0 +1,61 @@
- hosts: demo
gather_facts: no
tasks:
- name: Get users
ansible.builtin.shell:
cmd: >
getent passwd | awk -F: '$7 !~ /(false|nologin|true|sync)$/' | grep -v '^$' | awk -F":" '{ print $1 }'
register: users
- name: Gather posix account last login
ansible.builtin.shell: |
for user in {{ users.stdout_lines | join(" ") }}; do
last -wi --time-format iso -n 1 ${user} | awk '{ print $1,$3,$4, $NF }' | head -1 | awk 'NF'
done
register: last_login
- name: Get user password change date and expiry
ansible.builtin.shell: |
for user in {{ users.stdout_lines | join(" ") }}; do
k=$(getent shadow $user | awk -F: '{ print $3, $5 }')
echo "$user:$k"
done
register: passwd_date
- name: Get user groups
ansible.builtin.shell: |
for user in {{ users.stdout_lines | join(" ") }}; do
echo "$(groups $user)" | sed 's@ : @:@g'
done
register: user_groups
- name: Get sudoers
ansible.builtin.shell: |
for user in {{ users.stdout_lines | join(" ") }}; do
echo "$user: $(grep "^$user " /etc/sudoers | tr '\n' ';' || echo '')"
done
register: user_sudo
- name: Get authorized keys
ansible.builtin.shell: |
for user in {{ users.stdout_lines | join(" ") }}; do
home=$(getent passwd $user | cut -d: -f6)
echo -n "$user:"
if [ -f "${home}/.ssh/authorized_keys" ]; then
cat ${home}/.ssh/authorized_keys | tr '\n' ';'
fi
echo
done
register: user_authorized
- set_fact:
info:
users: "{{ users.stdout_lines }}"
last_login: "{{ last_login.stdout_lines }}"
user_groups: "{{ user_groups.stdout_lines }}"
user_sudo: "{{ user_sudo.stdout_lines }}"
user_authorized: "{{ user_authorized.stdout_lines }}"
passwd_date: "{{ passwd_date.stdout_lines }}"
- debug:
var: info

View File

@ -0,0 +1,33 @@
- hosts: demo
gather_facts: no
tasks:
- name: Run net user command to get all users
win_shell: net user
register: user_list_output
failed_when: false
- name: Parse all users from net user command
set_fact:
all_users: >-
{%- set users = [] -%}
{%- for line in user_list_output.stdout_lines -%}
{%- if loop.index > 4 and line.strip() != "" and not line.startswith("The command completed") -%}
{%- for user in line.split() -%}
{%- set _ = users.append(user) -%}
{%- endfor -%}
{%- endif -%}
{%- endfor -%}
{{ users }}
- name: Run net user command for each user to get details
win_shell: net user {{ item }}
loop: "{{ all_users }}"
register: user_details
ignore_errors: yes
- set_fact:
info:
user_details: "{{ user_details.results }}"
- debug:
var: info

View File

@ -2,10 +2,13 @@ id: gather_accounts_windows
name: "{{ 'Windows account gather' | trans }}"
version: 1
method: gather_accounts
category: host
category:
- host
type:
- windows
i18n:
Windows account gather:
zh: 使用命令 net user 收集 Windows 账号

View File

@ -0,0 +1,74 @@
- hosts: demo
gather_facts: no
tasks:
- name: Import ActiveDirectory module
win_shell: Import-Module ActiveDirectory
args:
warn: false
- name: Get the SamAccountName list of all AD users
win_shell: |
Import-Module ActiveDirectory
Get-ADUser -Filter * | Select-Object -ExpandProperty SamAccountName
register: ad_user_list
- name: Set the all_users variable
set_fact:
all_users: "{{ ad_user_list.stdout_lines }}"
- name: Get detailed information for each user
win_shell: |
Import-Module ActiveDirectory
$user = Get-ADUser -Identity {{ item }} -Properties Name, SamAccountName, Enabled, LastLogonDate, PasswordLastSet, msDS-UserPasswordExpiryTimeComputed, MemberOf
$globalGroups = @()
if ($user.MemberOf) {
$globalGroups = $user.MemberOf | ForEach-Object {
try {
$group = Get-ADGroup $_ -ErrorAction Stop
if ($group.GroupScope -eq 'Global') { $group.Name }
} catch {
}
}
}
$passwordExpiry = $null
$expiryRaw = $user.'msDS-UserPasswordExpiryTimeComputed'
if ($expiryRaw) {
try {
$passwordExpiry = [datetime]::FromFileTime($expiryRaw)
} catch {
$passwordExpiry = $null
}
}
$output = [PSCustomObject]@{
Name = $user.Name
SamAccountName = $user.SamAccountName
Enabled = $user.Enabled
LastLogonDate = if ($user.LastLogonDate) { $user.LastLogonDate.ToString("yyyy-MM-dd HH:mm:ss") } else { $null }
PasswordLastSet = if ($user.PasswordLastSet) { $user.PasswordLastSet.ToString("yyyy-MM-dd HH:mm:ss") } else { $null }
PasswordExpires = if ($passwordExpiry) { $passwordExpiry.ToString("yyyy-MM-dd HH:mm:ss") } else { $null }
GlobalGroupMemberships = $globalGroups
}
$output | ConvertTo-Json -Depth 3
loop: "{{ all_users }}"
register: ad_user_details
ignore_errors: yes
- set_fact:
info:
user_details: >-
{{
ad_user_details.results
| selectattr('rc', 'equalto', 0)
| map(attribute='stdout')
| select('truthy')
| map('from_json')
}}
- debug:
var: info

View File

@ -0,0 +1,15 @@
id: gather_accounts_windows_ad
name: "{{ 'Windows account gather' | trans }}"
version: 1
method: gather_accounts
category:
- ds
type:
- windows_ad
i18n:
Windows account gather:
zh: 使用命令 Get-ADUser 收集 Windows 账号
ja: コマンド Get-ADUser を使用して Windows アカウントを収集する
en: Using command Get-ADUser to gather accounts

View File

@ -0,0 +1,409 @@
import time
from collections import defaultdict
from django.utils import timezone
from accounts.const import AutomationTypes
from accounts.models import GatheredAccount, Account, AccountRisk, RiskChoice
from common.const import ConfirmOrIgnore
from common.decorators import bulk_create_decorator, bulk_update_decorator
from common.utils import get_logger
from common.utils.strings import get_text_diff
from orgs.utils import tmp_to_org
from .filter import GatherAccountsFilter
from ..base.manager import AccountBasePlaybookManager
logger = get_logger(__name__)
risk_items = [
"authorized_keys",
"sudoers",
"groups",
]
common_risk_items = [
"address_last_login",
"date_last_login",
"date_password_change",
"date_password_expired",
"detail"
]
diff_items = risk_items + common_risk_items
def format_datetime(value):
if isinstance(value, timezone.datetime):
return value.strftime("%Y-%m-%d %H:%M:%S")
return value
def get_items_diff(ori_account, d):
if hasattr(ori_account, "_diff"):
return ori_account._diff
diff = {}
for item in diff_items:
get_item_diff(item, ori_account, d, diff)
ori_account._diff = diff
return diff
def get_item_diff(item, ori_account, d, diff):
detail = getattr(ori_account, 'detail', {})
new_detail = d.get('detail', {})
ori = getattr(ori_account, item, None) or detail.get(item)
new = d.get(item, "") or new_detail.get(item)
if not ori and not new:
return
ori = format_datetime(ori)
new = format_datetime(new)
if new != ori:
diff[item] = get_text_diff(str(ori), str(new))
class AnalyseAccountRisk:
long_time = timezone.timedelta(days=90)
datetime_check_items = [
{"field": "date_last_login", "risk": "long_time_no_login", "delta": long_time},
{
"field": "date_password_change",
"risk": RiskChoice.long_time_password,
"delta": long_time,
},
{
"field": "date_password_expired",
"risk": "password_expired",
"delta": timezone.timedelta(seconds=1),
},
]
def __init__(self, check_risk=True):
self.check_risk = check_risk
self.now = timezone.now()
self.pending_add_risks = []
def _analyse_item_changed(self, ori_ga, d):
diff = get_items_diff(ori_ga, d)
if not diff:
return
risks = []
for k, v in diff.items():
if k not in risk_items:
continue
risks.append(
dict(
asset_id=str(ori_ga.asset_id),
username=ori_ga.username,
gathered_account=ori_ga,
risk=k + "_changed",
detail={"diff": v},
)
)
self.save_or_update_risks(risks)
def _analyse_datetime_changed(self, ori_account, d, asset, username):
basic = {"asset_id": str(asset.id), "username": username}
risks = []
for item in self.datetime_check_items:
field = item["field"]
risk = item["risk"]
delta = item["delta"]
date = d.get(field)
if not date:
continue
# 服务器收集的时间和数据库时间一致,不进行比较,无法检测风险 不太对,先注释
# pre_date = ori_account and getattr(ori_account, field)
# if pre_date == date:
# continue
if date and date < timezone.now() - delta:
risks.append(
dict(**basic, risk=risk, detail={"date": date.isoformat()})
)
self.save_or_update_risks(risks)
def save_or_update_risks(self, risks):
# 提前取出来,避免每次都查数据库
asset_ids = {r["asset_id"] for r in risks}
assets_risks = AccountRisk.objects.filter(asset_id__in=asset_ids)
assets_risks = {f"{r.asset_id}_{r.username}_{r.risk}": r for r in assets_risks}
for d in risks:
detail = d.pop("detail", {})
detail["datetime"] = self.now.isoformat()
key = f"{d['asset_id']}_{d['username']}_{d['risk']}"
found = assets_risks.get(key)
if not found:
self._create_risk(dict(**d, details=[detail]))
continue
found.details.append(detail)
self._update_risk(found)
@bulk_create_decorator(AccountRisk)
def _create_risk(self, data):
return AccountRisk(**data)
@bulk_update_decorator(AccountRisk, update_fields=["details"])
def _update_risk(self, account):
return account
def lost_accounts(self, asset, lost_users):
if not self.check_risk:
return
for user in lost_users:
self._create_risk(
dict(
asset_id=str(asset.id),
username=user,
risk=RiskChoice.account_deleted,
details=[{"datetime": self.now.isoformat()}],
)
)
def analyse_risk(self, asset, ga, d, sys_found):
if not self.check_risk:
return
if ga:
self._analyse_item_changed(ga, d)
if not sys_found:
basic = {"asset": asset, "username": d["username"], 'gathered_account': ga}
self._create_risk(
dict(
**basic,
risk=RiskChoice.new_found,
details=[{"datetime": self.now.isoformat()}],
)
)
self._analyse_datetime_changed(ga, d, asset, d["username"])
class GatherAccountsManager(AccountBasePlaybookManager):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.host_asset_mapper = {}
self.asset_account_info = {}
self.asset_usernames_mapper = defaultdict(set)
self.ori_asset_usernames = defaultdict(set)
self.ori_gathered_usernames = defaultdict(set)
self.ori_gathered_accounts_mapper = dict()
self.is_sync_account = self.execution.snapshot.get("is_sync_account")
self.check_risk = self.execution.snapshot.get("check_risk", False)
@classmethod
def method_type(cls):
return AutomationTypes.gather_accounts
def host_callback(self, host, asset=None, **kwargs):
super().host_callback(host, asset=asset, **kwargs)
self.host_asset_mapper[host["name"]] = asset
return host
def _filter_success_result(self, tp, result):
result = GatherAccountsFilter(tp).run(self.method_id_meta_mapper, result)
return result
@staticmethod
def _get_nested_info(data, *keys):
for key in keys:
data = data.get(key, {})
if not data:
break
return data
def _collect_asset_account_info(self, asset, info):
result = self._filter_success_result(asset.type, info)
accounts = []
for username, info in result.items():
self.asset_usernames_mapper[str(asset.id)].add(username)
d = {"asset": asset, "username": username, "remote_present": True, **info}
accounts.append(d)
self.asset_account_info[asset] = accounts
def on_host_success(self, host, result):
super().on_host_success(host, result)
info = self._get_nested_info(result, "debug", "res", "info")
asset = self.host_asset_mapper.get(host)
if asset and info:
self._collect_asset_account_info(asset, info)
else:
print(f"\033[31m Not found {host} info \033[0m\n")
def prefetch_origin_account_usernames(self):
"""
提起查出来避免每次 sql 查询
:return:
"""
assets = self.asset_usernames_mapper.keys()
accounts = Account.objects.filter(asset__in=assets).values_list(
"asset", "username"
)
for asset_id, username in accounts:
self.ori_asset_usernames[str(asset_id)].add(username)
ga_accounts = GatheredAccount.objects.filter(asset__in=assets)
for account in ga_accounts:
self.ori_gathered_usernames[str(account.asset_id)].add(account.username)
key = "{}_{}".format(account.asset_id, account.username)
self.ori_gathered_accounts_mapper[key] = account
def update_gather_accounts_status(self, asset):
"""
远端账号收集中的账号vault 中的账号
要根据账号新增见啥标识 收集账号的状态, 让管理员关注
远端账号 -> 收集账号 -> 特权账号
"""
remote_users = self.asset_usernames_mapper[str(asset.id)]
ori_users = self.ori_asset_usernames[str(asset.id)]
ori_ga_users = self.ori_gathered_usernames[str(asset.id)]
queryset = GatheredAccount.objects.filter(asset=asset).exclude(
status=ConfirmOrIgnore.ignored
)
# 远端账号 比 收集账号多的
# 新增创建,不用处理状态
new_found_users = remote_users - ori_ga_users
if new_found_users:
self.summary["new_accounts"] += len(new_found_users)
for username in new_found_users:
self.result["new_accounts"].append(
{
"asset": str(asset),
"username": username,
}
)
# 远端上 比 收集账号少的
# 标识 remote_present=False, 标记为待处理
# 远端资产上不存在的,标识为待处理,需要管理员介入
lost_users = ori_ga_users - remote_users
if lost_users:
queryset.filter(username__in=lost_users).update(
status=ConfirmOrIgnore.pending, remote_present=False
)
self.summary["lost_accounts"] += len(lost_users)
for username in lost_users:
self.result["lost_accounts"].append(
{
"asset": str(asset),
"username": username,
}
)
risk_analyser = AnalyseAccountRisk(self.check_risk)
risk_analyser.lost_accounts(asset, lost_users)
# 收集的账号 比 账号列表多的, 有可能是账号中删掉了, 但这时候状态已经是 confirm 了
# 标识状态为 待处理, 让管理员去确认
ga_added_users = ori_ga_users - ori_users
if ga_added_users:
queryset.filter(username__in=ga_added_users).update(status=ConfirmOrIgnore.pending)
# 收集的账号 比 账号列表少的
# 这个好像不不用对比,原始情况就这样
# 远端账号 比 账号列表少的
# 创建收集账号,标识 remote_present=False, 状态待处理
# 远端账号 比 账号列表多的
# 正常情况, 不用处理,因为远端账号会创建到收集账号,收集账号再去对比
# 不过这个好像也处理一下 status因为已存在这是状态应该是确认
(
queryset.filter(username__in=ori_users)
.exclude(status=ConfirmOrIgnore.confirmed)
.update(status=ConfirmOrIgnore.confirmed)
)
# 远端存在的账号,标识为已存在
(
queryset.filter(username__in=remote_users, remote_present=False).update(
remote_present=True
)
)
# 资产上没有的,标识为为存在
(
queryset.exclude(username__in=ori_users)
.filter(present=True)
.update(present=False)
)
(
queryset.filter(username__in=ori_users)
.filter(present=False)
.update(present=True)
)
@bulk_create_decorator(GatheredAccount)
def create_gathered_account(self, d):
ga = GatheredAccount()
for k, v in d.items():
setattr(ga, k, v)
return ga
@bulk_update_decorator(GatheredAccount, update_fields=common_risk_items)
def update_gathered_account(self, ori_account, d):
diff = get_items_diff(ori_account, d)
if not diff:
return
for k in diff:
if k not in common_risk_items:
continue
v = d.get(k)
setattr(ori_account, k, v)
return ori_account
def do_run(self, *args, **kwargs):
super().do_run(*args, **kwargs)
self.prefetch_origin_account_usernames()
risk_analyser = AnalyseAccountRisk(self.check_risk)
for asset, accounts_data in self.asset_account_info.items():
ori_users = self.ori_asset_usernames[str(asset.id)]
need_analyser_gather_account = []
with tmp_to_org(asset.org_id):
for d in accounts_data:
username = d["username"]
ori_account = self.ori_gathered_accounts_mapper.get(
"{}_{}".format(asset.id, username)
)
if not ori_account:
ga = self.create_gathered_account(d)
else:
ga = ori_account
self.update_gathered_account(ori_account, d)
ori_found = username in ori_users
need_analyser_gather_account.append((asset, ga, d, ori_found))
self.create_gathered_account.finish()
self.update_gathered_account.finish()
for analysis_data in need_analyser_gather_account:
risk_analyser.analyse_risk(*analysis_data)
self.update_gather_accounts_status(asset)
if not self.is_sync_account:
continue
gathered_accounts = GatheredAccount.objects.filter(asset=asset)
GatheredAccount.sync_accounts(gathered_accounts)
GatheredAccount.objects.filter(
asset=asset, username__in=ori_users, present=False
).update(
present=True
)
# 因为有 bulk create, bulk update, 所以这里需要 sleep 一下,等待数据同步
time.sleep(0.5)
def get_report_template(self):
return "accounts/gather_account_report.html"

View File

@ -1,22 +0,0 @@
- hosts: postgresql
gather_facts: no
vars:
ansible_python_interpreter: /opt/py3/bin/python
tasks:
- name: Get info
community.postgresql.postgresql_info:
login_user: "{{ jms_account.username }}"
login_password: "{{ jms_account.secret }}"
login_host: "{{ jms_asset.address }}"
login_port: "{{ jms_asset.port }}"
login_db: "{{ jms_asset.spec_info.db_name }}"
filter: "roles"
register: db_info
- name: Define info by set_fact
set_fact:
info: "{{ db_info.roles }}"
- debug:
var: info

View File

@ -1,74 +0,0 @@
import re
from django.utils import timezone
__all__ = ['GatherAccountsFilter']
# TODO 后期会挪到playbook中
class GatherAccountsFilter:
def __init__(self, tp):
self.tp = tp
@staticmethod
def mysql_filter(info):
result = {}
for _, user_dict in info.items():
for username, _ in user_dict.items():
if len(username.split('.')) == 1:
result[username] = {}
return result
@staticmethod
def postgresql_filter(info):
result = {}
for username in info:
result[username] = {}
return result
@staticmethod
def posix_filter(info):
username_pattern = re.compile(r'^(\S+)')
ip_pattern = re.compile(r'(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})')
login_time_pattern = re.compile(r'\w{3} \d{2} \d{2}:\d{2}:\d{2} \d{4}')
result = {}
for line in info:
usernames = username_pattern.findall(line)
username = ''.join(usernames)
if username:
result[username] = {}
else:
continue
ip_addrs = ip_pattern.findall(line)
ip_addr = ''.join(ip_addrs)
if ip_addr:
result[username].update({'address': ip_addr})
login_times = login_time_pattern.findall(line)
if login_times:
date = timezone.datetime.strptime(f'{login_times[0]} +0800', '%b %d %H:%M:%S %Y %z')
result[username].update({'date': date})
return result
@staticmethod
def windows_filter(info):
info = info[4:-2]
result = {}
for i in info:
for username in i.split():
result[username] = {}
return result
def run(self, method_id_meta_mapper, info):
run_method_name = None
for k, v in method_id_meta_mapper.items():
if self.tp not in v['type']:
continue
run_method_name = k.replace(f'{v["method"]}_', '')
if not run_method_name:
return info
if hasattr(self, f'{run_method_name}_filter'):
return getattr(self, f'{run_method_name}_filter')(info)
return info

View File

@ -1,21 +0,0 @@
- hosts: demo
gather_facts: no
tasks:
- name: Gather posix account
ansible.builtin.shell:
cmd: >
users=$(getent passwd | grep -v nologin | grep -v shutdown | awk -F":" '{ print $1 }');for i in $users;
do k=$(last -w -F $i -1 | head -1 | grep -v ^$ | awk '{ print $0 }')
if [ -n "$k" ]; then
echo $k
else
echo $i
fi;done
register: result
- name: Define info by set_fact
ansible.builtin.set_fact:
info: "{{ result.stdout_lines }}"
- debug:
var: info

View File

@ -1,14 +0,0 @@
- hosts: demo
gather_facts: no
tasks:
- name: Gather windows account
ansible.builtin.win_shell: net user
register: result
ignore_errors: true
- name: Define info by set_fact
ansible.builtin.set_fact:
info: "{{ result.stdout_lines }}"
- debug:
var: info

View File

@ -1,139 +0,0 @@
from collections import defaultdict
from accounts.const import AutomationTypes
from accounts.models import GatheredAccount
from assets.models import Asset
from common.utils import get_logger
from orgs.utils import tmp_to_org
from users.models import User
from .filter import GatherAccountsFilter
from ..base.manager import AccountBasePlaybookManager
from ...notifications import GatherAccountChangeMsg
logger = get_logger(__name__)
class GatherAccountsManager(AccountBasePlaybookManager):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.host_asset_mapper = {}
self.asset_account_info = {}
self.asset_username_mapper = defaultdict(set)
self.is_sync_account = self.execution.snapshot.get('is_sync_account')
@classmethod
def method_type(cls):
return AutomationTypes.gather_accounts
def host_callback(self, host, asset=None, **kwargs):
super().host_callback(host, asset=asset, **kwargs)
self.host_asset_mapper[host['name']] = asset
return host
def filter_success_result(self, tp, result):
result = GatherAccountsFilter(tp).run(self.method_id_meta_mapper, result)
return result
def generate_data(self, asset, result):
data = []
for username, info in result.items():
self.asset_username_mapper[str(asset.id)].add(username)
d = {'asset': asset, 'username': username, 'present': True}
if info.get('date'):
d['date_last_login'] = info['date']
if info.get('address'):
d['address_last_login'] = info['address'][:32]
data.append(d)
return data
def collect_asset_account_info(self, asset, result):
data = self.generate_data(asset, result)
self.asset_account_info[asset] = data
@staticmethod
def get_nested_info(data, *keys):
for key in keys:
data = data.get(key, {})
if not data:
break
return data
def on_host_success(self, host, result):
info = self.get_nested_info(result, 'debug', 'res', 'info')
asset = self.host_asset_mapper.get(host)
if asset and info:
result = self.filter_success_result(asset.type, info)
self.collect_asset_account_info(asset, result)
else:
print(f'\033[31m Not found {host} info \033[0m\n')
def update_or_create_accounts(self):
for asset, data in self.asset_account_info.items():
with tmp_to_org(asset.org_id):
gathered_accounts = []
GatheredAccount.objects.filter(asset=asset, present=True).update(present=False)
for d in data:
username = d['username']
gathered_account, __ = GatheredAccount.objects.update_or_create(
defaults=d, asset=asset, username=username,
)
gathered_accounts.append(gathered_account)
if not self.is_sync_account:
continue
GatheredAccount.sync_accounts(gathered_accounts)
def run(self, *args, **kwargs):
super().run(*args, **kwargs)
users, change_info = self.generate_send_users_and_change_info()
self.update_or_create_accounts()
self.send_email_if_need(users, change_info)
def generate_send_users_and_change_info(self):
recipients = self.execution.recipients
if not self.asset_username_mapper or not recipients:
return None, None
users = User.objects.filter(id__in=recipients)
if not users:
return users, None
asset_ids = self.asset_username_mapper.keys()
assets = Asset.objects.filter(id__in=asset_ids)
gather_accounts = GatheredAccount.objects.filter(asset_id__in=asset_ids, present=True)
asset_id_map = {str(asset.id): asset for asset in assets}
asset_id_username = list(assets.values_list('id', 'accounts__username'))
asset_id_username.extend(list(gather_accounts.values_list('asset_id', 'username')))
system_asset_username_mapper = defaultdict(set)
for asset_id, username in asset_id_username:
system_asset_username_mapper[str(asset_id)].add(username)
change_info = {}
for asset_id, usernames in self.asset_username_mapper.items():
system_usernames = system_asset_username_mapper.get(asset_id)
if not system_usernames:
continue
add_usernames = usernames - system_usernames
remove_usernames = system_usernames - usernames
k = f'{asset_id_map[asset_id]}[{asset_id}]'
if not add_usernames and not remove_usernames:
continue
change_info[k] = {
'add_usernames': ', '.join(add_usernames),
'remove_usernames': ', '.join(remove_usernames),
}
return users, change_info
@staticmethod
def send_email_if_need(users, change_info):
if not users or not change_info:
return
for user in users:
GatherAccountChangeMsg(user, change_info).publish_async()

View File

@ -0,0 +1,62 @@
- hosts: custom
gather_facts: no
vars:
ansible_connection: local
ansible_become: false
tasks:
- name: Test privileged account (paramiko)
ssh_ping:
login_host: "{{ jms_asset.address }}"
login_port: "{{ jms_asset.port }}"
login_user: "{{ jms_account.username }}"
login_password: "{{ jms_account.secret }}"
login_secret_type: "{{ jms_account.secret_type }}"
login_private_key_path: "{{ jms_account.private_key_path }}"
become: "{{ jms_custom_become | default(False) }}"
become_method: "{{ jms_custom_become_method | default('su') }}"
become_user: "{{ jms_custom_become_user | default('') }}"
become_password: "{{ jms_custom_become_password | default('') }}"
become_private_key_path: "{{ jms_custom_become_private_key_path | default(None) }}"
old_ssh_version: "{{ jms_asset.old_ssh_version | default(False) }}"
gateway_args: "{{ jms_asset.ansible_ssh_common_args | default(None) }}"
register: ping_info
delegate_to: localhost
- name: Change asset password (paramiko)
custom_command:
login_user: "{{ jms_account.username }}"
login_password: "{{ jms_account.secret }}"
login_host: "{{ jms_asset.address }}"
login_port: "{{ jms_asset.port }}"
login_secret_type: "{{ jms_account.secret_type }}"
login_private_key_path: "{{ jms_account.private_key_path }}"
become: "{{ jms_custom_become | default(False) }}"
become_method: "{{ jms_custom_become_method | default('su') }}"
become_user: "{{ jms_custom_become_user | default('') }}"
become_password: "{{ jms_custom_become_password | default('') }}"
become_private_key_path: "{{ jms_custom_become_private_key_path | default(None) }}"
name: "{{ account.username }}"
password: "{{ account.secret }}"
commands: "{{ params.commands }}"
first_conn_delay_time: "{{ first_conn_delay_time | default(0.5) }}"
ignore_errors: true
when: ping_info is succeeded and check_conn_after_change
register: change_info
delegate_to: localhost
- name: Verify password (paramiko)
ssh_ping:
login_user: "{{ account.username }}"
login_password: "{{ account.secret }}"
login_host: "{{ jms_asset.address }}"
login_port: "{{ jms_asset.port }}"
become: "{{ account.become.ansible_become | default(False) }}"
become_method: su
become_user: "{{ account.become.ansible_user | default('') }}"
become_password: "{{ account.become.ansible_password | default('') }}"
become_private_key_path: "{{ account.become.ansible_ssh_private_key_file | default(None) }}"
old_ssh_version: "{{ jms_asset.old_ssh_version | default(False) }}"
gateway_args: "{{ jms_asset.ansible_ssh_common_args | default(None) }}"
delegate_to: localhost
when: check_conn_after_change

View File

@ -0,0 +1,32 @@
id: push_account_by_ssh
name: "{{ 'SSH account push' | trans }}"
category:
- device
- host
type:
- all
method: push_account
protocol: ssh
priority: 50
params:
- name: commands
type: list
label: "{{ 'Params commands label' | trans }}"
default: [ '' ]
help_text: "{{ 'Params commands help text' | trans }}"
i18n:
SSH account push:
zh: '使用 SSH 命令行自定义推送'
ja: 'SSHコマンドラインを使用してプッシュをカスタマイズする'
en: 'Custom push using SSH command line'
Params commands help text:
zh: '自定义命令中如需包含账号的 账号、密码、SSH 连接的用户密码 字段,<br />请使用 &#123;username&#125;、&#123;password&#125;、&#123;login_password&#125;格式,执行任务时会进行替换 。<br />比如针对 Cisco 主机进行改密,一般需要配置五条命令:<br />1. enable<br />2. &#123;login_password&#125;<br />3. configure terminal<br />4. username &#123;username&#125; privilege 0 password &#123;password&#125; <br />5. end'
ja: 'カスタム コマンドに SSH 接続用のアカウント番号、パスワード、ユーザー パスワード フィールドを含める必要がある場合は、<br />&#123;ユーザー名&#125;、&#123;パスワード&#125;、&#123;login_password& を使用してください。 # 125; 形式。タスクの実行時に置き換えられます。 <br />たとえば、Cisco ホストのパスワードを変更するには、通常、次の 5 つのコマンドを設定する必要があります:<br />1.enable<br />2.&#123;login_password&#125;<br />3 .ターミナルの設定<br / >4. ユーザー名 &#123;ユーザー名&#125; 権限 0 パスワード &#123;パスワード&#125; <br />5. 終了'
en: 'If the custom command needs to include the account number, password, and user password field for SSH connection,<br />Please use &#123;username&#125;, &#123;password&#125;, &#123;login_password&# 125; format, which will be replaced when executing the task. <br />For example, to change the password of a Cisco host, you generally need to configure five commands:<br />1. enable<br />2. &#123;login_password&#125;<br />3. configure terminal<br / >4. username &#123;username&#125; privilege 0 password &#123;password&#125; <br />5. end'
Params commands label:
zh: '自定义命令'
ja: 'カスタムコマンド'
en: 'Custom command'

View File

@ -1,7 +1,7 @@
- hosts: mongodb
gather_facts: no
vars:
ansible_python_interpreter: /opt/py3/bin/python
ansible_python_interpreter: "{{ local_python_interpreter }}"
tasks:
- name: Test MongoDB connection
@ -53,3 +53,4 @@
ssl_certfile: "{{ jms_asset.secret_info.client_key | default('') }}"
connection_options:
- tlsAllowInvalidHostnames: "{{ jms_asset.spec_info.allow_invalid_cert}}"
when: check_conn_after_change

View File

@ -1,9 +1,12 @@
- hosts: mysql
gather_facts: no
vars:
ansible_python_interpreter: /opt/py3/bin/python
ansible_python_interpreter: "{{ local_python_interpreter }}"
db_name: "{{ jms_asset.spec_info.db_name }}"
check_ssl: "{{ jms_asset.spec_info.use_ssl and not jms_asset.spec_info.allow_invalid_cert }}"
ca_cert: "{{ jms_asset.secret_info.ca_cert | default('') }}"
ssl_cert: "{{ jms_asset.secret_info.client_cert | default('') }}"
ssl_key: "{{ jms_asset.secret_info.client_key | default('') }}"
tasks:
- name: Test MySQL connection
@ -13,9 +16,9 @@
login_host: "{{ jms_asset.address }}"
login_port: "{{ jms_asset.port }}"
check_hostname: "{{ check_ssl if check_ssl else omit }}"
ca_cert: "{{ jms_asset.secret_info.ca_cert | default(omit) if check_ssl else omit }}"
client_cert: "{{ jms_asset.secret_info.client_cert | default(omit) if check_ssl else omit }}"
client_key: "{{ jms_asset.secret_info.client_key | default(omit) if check_ssl else omit }}"
ca_cert: "{{ ca_cert if check_ssl and ca_cert | length > 0 else omit }}"
client_cert: "{{ ssl_cert if check_ssl and ssl_cert | length > 0 else omit }}"
client_key: "{{ ssl_key if check_ssl and ssl_key | length > 0 else omit }}"
filter: version
register: db_info
@ -30,9 +33,9 @@
login_host: "{{ jms_asset.address }}"
login_port: "{{ jms_asset.port }}"
check_hostname: "{{ check_ssl if check_ssl else omit }}"
ca_cert: "{{ jms_asset.secret_info.ca_cert | default(omit) if check_ssl else omit }}"
client_cert: "{{ jms_asset.secret_info.client_cert | default(omit) if check_ssl else omit }}"
client_key: "{{ jms_asset.secret_info.client_key | default(omit) if check_ssl else omit }}"
ca_cert: "{{ ca_cert if check_ssl and ca_cert | length > 0 else omit }}"
client_cert: "{{ ssl_cert if check_ssl and ssl_cert | length > 0 else omit }}"
client_key: "{{ ssl_key if check_ssl and ssl_key | length > 0 else omit }}"
name: "{{ account.username }}"
password: "{{ account.secret }}"
host: "%"
@ -47,7 +50,8 @@
login_host: "{{ jms_asset.address }}"
login_port: "{{ jms_asset.port }}"
check_hostname: "{{ check_ssl if check_ssl else omit }}"
ca_cert: "{{ jms_asset.secret_info.ca_cert | default(omit) if check_ssl else omit }}"
client_cert: "{{ jms_asset.secret_info.client_cert | default(omit) if check_ssl else omit }}"
client_key: "{{ jms_asset.secret_info.client_key | default(omit) if check_ssl else omit }}"
ca_cert: "{{ ca_cert if check_ssl and ca_cert | length > 0 else omit }}"
client_cert: "{{ ssl_cert if check_ssl and ssl_cert | length > 0 else omit }}"
client_key: "{{ ssl_key if check_ssl and ssl_key | length > 0 else omit }}"
filter: version
when: check_conn_after_change

View File

@ -1,7 +1,7 @@
- hosts: oracle
gather_facts: no
vars:
ansible_python_interpreter: /opt/py3/bin/python
ansible_python_interpreter: "{{ local_python_interpreter }}"
tasks:
- name: Test Oracle connection
@ -40,3 +40,4 @@
login_port: "{{ jms_asset.port }}"
login_database: "{{ jms_asset.spec_info.db_name }}"
mode: "{{ account.mode }}"
when: check_conn_after_change

View File

@ -1,7 +1,11 @@
- hosts: postgre
gather_facts: no
vars:
ansible_python_interpreter: /opt/py3/bin/python
ansible_python_interpreter: "{{ local_python_interpreter }}"
check_ssl: "{{ jms_asset.spec_info.use_ssl }}"
ca_cert: "{{ jms_asset.secret_info.ca_cert | default('') }}"
ssl_cert: "{{ jms_asset.secret_info.client_cert | default('') }}"
ssl_key: "{{ jms_asset.secret_info.client_key | default('') }}"
tasks:
- name: Test PostgreSQL connection
@ -11,6 +15,10 @@
login_host: "{{ jms_asset.address }}"
login_port: "{{ jms_asset.port }}"
login_db: "{{ jms_asset.spec_info.db_name }}"
ca_cert: "{{ ca_cert if check_ssl and ca_cert | length > 0 else omit }}"
ssl_cert: "{{ ssl_cert if check_ssl and ssl_cert | length > 0 else omit }}"
ssl_key: "{{ ssl_key if check_ssl and ssl_key | length > 0 else omit }}"
ssl_mode: "{{ jms_asset.spec_info.pg_ssl_mode }}"
register: result
failed_when: not result.is_available
@ -28,6 +36,10 @@
db: "{{ jms_asset.spec_info.db_name }}"
name: "{{ account.username }}"
password: "{{ account.secret }}"
ca_cert: "{{ ca_cert if check_ssl and ca_cert | length > 0 else omit }}"
ssl_cert: "{{ ssl_cert if check_ssl and ssl_cert | length > 0 else omit }}"
ssl_key: "{{ ssl_key if check_ssl and ssl_key | length > 0 else omit }}"
ssl_mode: "{{ jms_asset.spec_info.pg_ssl_mode }}"
role_attr_flags: LOGIN
ignore_errors: true
when: result is succeeded
@ -40,8 +52,13 @@
login_host: "{{ jms_asset.address }}"
login_port: "{{ jms_asset.port }}"
db: "{{ jms_asset.spec_info.db_name }}"
ca_cert: "{{ ca_cert if check_ssl and ca_cert | length > 0 else omit }}"
ssl_cert: "{{ ssl_cert if check_ssl and ssl_cert | length > 0 else omit }}"
ssl_key: "{{ ssl_key if check_ssl and ssl_key | length > 0 else omit }}"
ssl_mode: "{{ jms_asset.spec_info.pg_ssl_mode }}"
when:
- result is succeeded
- change_info is succeeded
- check_conn_after_change
register: result
failed_when: not result.is_available

View File

@ -1,7 +1,7 @@
- hosts: sqlserver
gather_facts: no
vars:
ansible_python_interpreter: /opt/py3/bin/python
ansible_python_interpreter: "{{ local_python_interpreter }}"
tasks:
- name: Test SQLServer connection
@ -66,3 +66,4 @@
name: '{{ jms_asset.spec_info.db_name }}'
script: |
SELECT @@version
when: check_conn_after_change

View File

@ -9,55 +9,20 @@
database: passwd
key: "{{ account.username }}"
register: user_info
ignore_errors: yes # 忽略错误如果用户不存在时不会导致playbook失败
failed_when: false
changed_when: false
- name: "Add {{ account.username }} user"
ansible.builtin.user:
name: "{{ account.username }}"
shell: "{{ params.shell }}"
home: "{{ params.home | default('/home/' + account.username, true) }}"
groups: "{{ params.groups }}"
uid: "{{ params.uid | int if params.uid | length > 0 else omit }}"
shell: "{{ params.shell if params.shell | length > 0 else omit }}"
home: "{{ params.home if params.home | length > 0 else '/home/' + account.username }}"
groups: "{{ params.groups if params.groups | length > 0 else omit }}"
append: "{{ true if params.groups | length > 0 else false }}"
expires: -1
state: present
when: user_info.failed
- name: "Add {{ account.username }} group"
ansible.builtin.group:
name: "{{ account.username }}"
state: present
when: user_info.failed
- name: "Add {{ account.username }} user to group"
ansible.builtin.user:
name: "{{ account.username }}"
groups: "{{ params.groups }}"
when:
- user_info.failed
- params.groups
- name: "Change {{ account.username }} password"
ansible.builtin.user:
name: "{{ account.username }}"
password: "{{ account.secret | password_hash('des') }}"
update_password: always
ignore_errors: true
when: account.secret_type == "password"
- name: remove jumpserver ssh key
ansible.builtin.lineinfile:
dest: "{{ ssh_params.dest }}"
regexp: "{{ ssh_params.regexp }}"
state: absent
when:
- account.secret_type == "ssh_key"
- ssh_params.strategy == "set_jms"
- name: "Change {{ account.username }} SSH key"
ansible.builtin.authorized_key:
user: "{{ account.username }}"
key: "{{ account.secret }}"
exclusive: "{{ ssh_params.exclusive }}"
when: account.secret_type == "ssh_key"
when: user_info.msg is defined
- name: "Set {{ account.username }} sudo setting"
ansible.builtin.lineinfile:
@ -67,9 +32,61 @@
line: "{{ account.username + ' ALL=(ALL) NOPASSWD: ' + params.sudo }}"
validate: visudo -cf %s
when:
- user_info.failed
- user_info.msg is defined or params.modify_sudo
- params.sudo
- name: "Change {{ account.username }} password"
ansible.builtin.user:
name: "{{ account.username }}"
password: "{{ account.secret | password_hash('des') }}"
update_password: always
ignore_errors: true
register: change_secret_result
when: account.secret_type == "password"
- name: "Get home directory for {{ account.username }}"
ansible.builtin.shell: "getent passwd {{ account.username }} | cut -d: -f6"
register: home_dir
when: account.secret_type == "ssh_key"
ignore_errors: yes
- name: "Check if home directory exists for {{ account.username }}"
ansible.builtin.stat:
path: "{{ home_dir.stdout.strip() }}"
register: home_dir_stat
when: account.secret_type == "ssh_key"
ignore_errors: yes
- name: "Ensure {{ account.username }} home directory exists"
ansible.builtin.file:
path: "{{ home_dir.stdout.strip() }}"
state: directory
owner: "{{ account.username }}"
group: "{{ account.username }}"
mode: '0750'
when:
- account.secret_type == "ssh_key"
- home_dir_stat.stat.exists == false
ignore_errors: yes
- name: Remove jumpserver ssh key
ansible.builtin.lineinfile:
dest: "{{ home_dir.stdout.strip() }}/.ssh/authorized_keys"
regexp: "{{ ssh_params.regexp }}"
state: absent
when:
- account.secret_type == "ssh_key"
- ssh_params.strategy == "set_jms"
ignore_errors: yes
- name: "Change {{ account.username }} SSH key"
ansible.builtin.authorized_key:
user: "{{ account.username }}"
key: "{{ account.secret }}"
exclusive: "{{ ssh_params.exclusive }}"
register: change_secret_result
when: account.secret_type == "ssh_key"
- name: Refresh connection
ansible.builtin.meta: reset_connection
@ -79,14 +96,16 @@
login_password: "{{ account.secret }}"
login_host: "{{ jms_asset.address }}"
login_port: "{{ jms_asset.port }}"
gateway_args: "{{ jms_asset.ansible_ssh_common_args | default('') }}"
gateway_args: "{{ jms_asset.ansible_ssh_common_args | default(None) }}"
become: "{{ account.become.ansible_become | default(False) }}"
become_method: su
become_user: "{{ account.become.ansible_user | default('') }}"
become_password: "{{ account.become.ansible_password | default('') }}"
become_private_key_path: "{{ account.become.ansible_ssh_private_key_file | default(None) }}"
old_ssh_version: "{{ jms_asset.old_ssh_version | default(False) }}"
when: account.secret_type == "password"
when:
- account.secret_type == "password"
- check_conn_after_change or change_secret_result.failed | default(false)
delegate_to: localhost
- name: "Verify {{ account.username }} SSH KEY (paramiko)"
@ -95,8 +114,10 @@
login_port: "{{ jms_asset.port }}"
login_user: "{{ account.username }}"
login_private_key_path: "{{ account.private_key_path }}"
gateway_args: "{{ jms_asset.ansible_ssh_common_args | default('') }}"
gateway_args: "{{ jms_asset.ansible_ssh_common_args | default(None) }}"
old_ssh_version: "{{ jms_asset.old_ssh_version | default(False) }}"
when: account.secret_type == "ssh_key"
when:
- account.secret_type == "ssh_key"
- check_conn_after_change or change_secret_result.failed | default(false)
delegate_to: localhost

View File

@ -5,6 +5,12 @@ type:
- AIX
method: push_account
params:
- name: modify_sudo
type: bool
label: "{{ 'Modify sudo label' | trans }}"
default: False
help_text: "{{ 'Modify params sudo help text' | trans }}"
- name: sudo
type: str
label: 'Sudo'
@ -28,12 +34,23 @@ params:
default: ''
help_text: "{{ 'Params groups help text' | trans }}"
- name: uid
type: str
label: "{{ 'Params uid label' | trans }}"
default: ''
help_text: "{{ 'Params uid help text' | trans }}"
i18n:
Aix account push:
zh: '使用 Ansible 模块 user 执行 Aix 账号推送 (DES)'
ja: 'Ansible user モジュールを使用して Aix アカウントをプッシュする (DES)'
en: 'Using Ansible module user to push account (DES)'
Modify params sudo help text:
zh: '如果用户存在可以修改sudo权限'
ja: 'ユーザーが存在する場合、sudo権限を変更できます'
en: 'If the user exists, sudo permissions can be modified'
Params sudo help text:
zh: '使用逗号分隔多个命令,如: /bin/whoami,/sbin/ifconfig'
ja: 'コンマで区切って複数のコマンドを入力してください。例: /bin/whoami,/sbin/ifconfig'
@ -49,6 +66,16 @@ i18n:
ja: 'グループを入力してください。複数のグループはコンマで区切ってください(既存のグループを入力してください)'
en: 'Please enter the group. Multiple groups are separated by commas (please enter the existing group)'
Params uid help text:
zh: '请输入用户ID'
ja: 'ユーザーIDを入力してください'
en: 'Please enter the user ID'
Modify sudo label:
zh: '修改 sudo 权限'
ja: 'sudo 権限を変更'
en: 'Modify sudo'
Params home label:
zh: '家目录'
ja: 'ホームディレクトリ'
@ -59,3 +86,7 @@ i18n:
ja: 'グループ'
en: 'Groups'
Params uid label:
zh: '用户ID'
ja: 'ユーザーID'
en: 'User ID'

View File

@ -9,55 +9,20 @@
database: passwd
key: "{{ account.username }}"
register: user_info
ignore_errors: yes # 忽略错误如果用户不存在时不会导致playbook失败
failed_when: false
changed_when: false
- name: "Add {{ account.username }} user"
ansible.builtin.user:
name: "{{ account.username }}"
shell: "{{ params.shell }}"
home: "{{ params.home | default('/home/' + account.username, true) }}"
groups: "{{ params.groups }}"
uid: "{{ params.uid | int if params.uid | length > 0 else omit }}"
shell: "{{ params.shell if params.shell | length > 0 else omit }}"
home: "{{ params.home if params.home | length > 0 else '/home/' + account.username }}"
groups: "{{ params.groups if params.groups | length > 0 else omit }}"
append: "{{ true if params.groups | length > 0 else false }}"
expires: -1
state: present
when: user_info.failed
- name: "Add {{ account.username }} group"
ansible.builtin.group:
name: "{{ account.username }}"
state: present
when: user_info.failed
- name: "Add {{ account.username }} user to group"
ansible.builtin.user:
name: "{{ account.username }}"
groups: "{{ params.groups }}"
when:
- user_info.failed
- params.groups
- name: "Change {{ account.username }} password"
ansible.builtin.user:
name: "{{ account.username }}"
password: "{{ account.secret | password_hash('sha512') }}"
update_password: always
ignore_errors: true
when: account.secret_type == "password"
- name: remove jumpserver ssh key
ansible.builtin.lineinfile:
dest: "{{ ssh_params.dest }}"
regexp: "{{ ssh_params.regexp }}"
state: absent
when:
- account.secret_type == "ssh_key"
- ssh_params.strategy == "set_jms"
- name: "Change {{ account.username }} SSH key"
ansible.builtin.authorized_key:
user: "{{ account.username }}"
key: "{{ account.secret }}"
exclusive: "{{ ssh_params.exclusive }}"
when: account.secret_type == "ssh_key"
when: user_info.msg is defined
- name: "Set {{ account.username }} sudo setting"
ansible.builtin.lineinfile:
@ -67,9 +32,61 @@
line: "{{ account.username + ' ALL=(ALL) NOPASSWD: ' + params.sudo }}"
validate: visudo -cf %s
when:
- user_info.failed
- user_info.msg is defined or params.modify_sudo
- params.sudo
- name: "Change {{ account.username }} password"
ansible.builtin.user:
name: "{{ account.username }}"
password: "{{ account.secret | password_hash('sha512') }}"
update_password: always
ignore_errors: true
register: change_secret_result
when: account.secret_type == "password"
- name: "Get home directory for {{ account.username }}"
ansible.builtin.shell: "getent passwd {{ account.username }} | cut -d: -f6"
register: home_dir
when: account.secret_type == "ssh_key"
ignore_errors: yes
- name: "Check if home directory exists for {{ account.username }}"
ansible.builtin.stat:
path: "{{ home_dir.stdout.strip() }}"
register: home_dir_stat
when: account.secret_type == "ssh_key"
ignore_errors: yes
- name: "Ensure {{ account.username }} home directory exists"
ansible.builtin.file:
path: "{{ home_dir.stdout.strip() }}"
state: directory
owner: "{{ account.username }}"
group: "{{ account.username }}"
mode: '0750'
when:
- account.secret_type == "ssh_key"
- home_dir_stat.stat.exists == false
ignore_errors: yes
- name: Remove jumpserver ssh key
ansible.builtin.lineinfile:
dest: "{{ home_dir.stdout.strip() }}/.ssh/authorized_keys"
regexp: "{{ ssh_params.regexp }}"
state: absent
when:
- account.secret_type == "ssh_key"
- ssh_params.strategy == "set_jms"
ignore_errors: yes
- name: "Change {{ account.username }} SSH key"
ansible.builtin.authorized_key:
user: "{{ account.username }}"
key: "{{ account.secret }}"
exclusive: "{{ ssh_params.exclusive }}"
register: change_secret_result
when: account.secret_type == "ssh_key"
- name: Refresh connection
ansible.builtin.meta: reset_connection
@ -79,14 +96,16 @@
login_password: "{{ account.secret }}"
login_host: "{{ jms_asset.address }}"
login_port: "{{ jms_asset.port }}"
gateway_args: "{{ jms_asset.ansible_ssh_common_args | default('') }}"
gateway_args: "{{ jms_asset.ansible_ssh_common_args | default(None) }}"
become: "{{ account.become.ansible_become | default(False) }}"
become_method: su
become_user: "{{ account.become.ansible_user | default('') }}"
become_password: "{{ account.become.ansible_password | default('') }}"
become_private_key_path: "{{ account.become.ansible_ssh_private_key_file | default(None) }}"
old_ssh_version: "{{ jms_asset.old_ssh_version | default(False) }}"
when: account.secret_type == "password"
when:
- account.secret_type == "password"
- check_conn_after_change or change_secret_result.failed | default(false)
delegate_to: localhost
- name: "Verify {{ account.username }} SSH KEY (paramiko)"
@ -95,8 +114,10 @@
login_port: "{{ jms_asset.port }}"
login_user: "{{ account.username }}"
login_private_key_path: "{{ account.private_key_path }}"
gateway_args: "{{ jms_asset.ansible_ssh_common_args | default('') }}"
gateway_args: "{{ jms_asset.ansible_ssh_common_args | default(None) }}"
old_ssh_version: "{{ jms_asset.old_ssh_version | default(False) }}"
when: account.secret_type == "ssh_key"
when:
- account.secret_type == "ssh_key"
- check_conn_after_change or change_secret_result.failed | default(false)
delegate_to: localhost

View File

@ -6,6 +6,12 @@ type:
- linux
method: push_account
params:
- name: modify_sudo
type: bool
label: "{{ 'Modify sudo label' | trans }}"
default: False
help_text: "{{ 'Modify params sudo help text' | trans }}"
- name: sudo
type: str
label: 'Sudo'
@ -30,12 +36,23 @@ params:
default: ''
help_text: "{{ 'Params groups help text' | trans }}"
- name: uid
type: str
label: "{{ 'Params uid label' | trans }}"
default: ''
help_text: "{{ 'Params uid help text' | trans }}"
i18n:
Posix account push:
zh: '使用 Ansible 模块 user 执行账号推送 (sha512)'
ja: 'Ansible user モジュールを使用してアカウントをプッシュする (sha512)'
en: 'Using Ansible module user to push account (sha512)'
Modify params sudo help text:
zh: '如果用户存在可以修改sudo权限'
ja: 'ユーザーが存在する場合、sudo権限を変更できます'
en: 'If the user exists, sudo permissions can be modified'
Params sudo help text:
zh: '使用逗号分隔多个命令,如: /bin/whoami,/sbin/ifconfig'
ja: 'コンマで区切って複数のコマンドを入力してください。例: /bin/whoami,/sbin/ifconfig'
@ -51,6 +68,16 @@ i18n:
ja: 'グループを入力してください。複数のグループはコンマで区切ってください(既存のグループを入力してください)'
en: 'Please enter the group. Multiple groups are separated by commas (please enter the existing group)'
Params uid help text:
zh: '请输入用户ID'
ja: 'ユーザーIDを入力してください'
en: 'Please enter the user ID'
Modify sudo label:
zh: '修改 sudo 权限'
ja: 'sudo 権限を変更'
en: 'Modify sudo'
Params home label:
zh: '家目录'
ja: 'ホームディレクトリ'
@ -59,4 +86,9 @@ i18n:
Params groups label:
zh: '用户组'
ja: 'グループ'
en: 'Groups'
en: 'Groups'
Params uid label:
zh: '用户ID'
ja: 'ユーザーID'
en: 'User ID'

View File

@ -4,10 +4,6 @@
- name: Test privileged account
ansible.windows.win_ping:
# - name: Print variables
# debug:
# msg: "Username: {{ account.username }}, Password: {{ account.secret }}"
- name: Push user password
ansible.windows.win_user:
fullname: "{{ account.username}}"
@ -28,4 +24,4 @@
vars:
ansible_user: "{{ account.username }}"
ansible_password: "{{ account.secret }}"
when: account.secret_type == "password"
when: account.secret_type == "password" and check_conn_after_change

Some files were not shown because too many files have changed in this diff Show More