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

pull/14517/head
ibuler 2024-11-12 16:01:31 +08:00
commit a750fbb785
31 changed files with 245 additions and 46 deletions

View File

@ -41,7 +41,7 @@
commands: "{{ params.commands }}" commands: "{{ params.commands }}"
first_conn_delay_time: "{{ first_conn_delay_time | default(0.5) }}" first_conn_delay_time: "{{ first_conn_delay_time | default(0.5) }}"
ignore_errors: true ignore_errors: true
when: ping_info is succeeded when: ping_info is succeeded and check_conn_after_change
register: change_info register: change_info
delegate_to: localhost delegate_to: localhost
@ -59,3 +59,4 @@
old_ssh_version: "{{ jms_asset.old_ssh_version | default(False) }}" old_ssh_version: "{{ jms_asset.old_ssh_version | default(False) }}"
gateway_args: "{{ jms_asset.ansible_ssh_common_args | default(None) }}" gateway_args: "{{ jms_asset.ansible_ssh_common_args | default(None) }}"
delegate_to: localhost delegate_to: localhost
when: check_conn_after_change

View File

@ -53,3 +53,4 @@
ssl_certfile: "{{ jms_asset.secret_info.client_key | default('') }}" ssl_certfile: "{{ jms_asset.secret_info.client_key | default('') }}"
connection_options: connection_options:
- tlsAllowInvalidHostnames: "{{ jms_asset.spec_info.allow_invalid_cert}}" - tlsAllowInvalidHostnames: "{{ jms_asset.spec_info.allow_invalid_cert}}"
when: check_conn_after_change

View File

@ -54,3 +54,4 @@
client_cert: "{{ ssl_cert if check_ssl and ssl_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 }}" client_key: "{{ ssl_key if check_ssl and ssl_key | length > 0 else omit }}"
filter: version filter: version
when: check_conn_after_change

View File

@ -40,3 +40,4 @@
login_port: "{{ jms_asset.port }}" login_port: "{{ jms_asset.port }}"
login_database: "{{ jms_asset.spec_info.db_name }}" login_database: "{{ jms_asset.spec_info.db_name }}"
mode: "{{ account.mode }}" mode: "{{ account.mode }}"
when: check_conn_after_change

View File

@ -56,3 +56,4 @@
ssl_cert: "{{ ssl_cert if check_ssl and ssl_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_key: "{{ ssl_key if check_ssl and ssl_key | length > 0 else omit }}"
ssl_mode: "{{ 'verify-full' if check_ca else 'require' if check_ssl else 'prefer' }}" ssl_mode: "{{ 'verify-full' if check_ca else 'require' if check_ssl else 'prefer' }}"
when: check_conn_after_change

View File

@ -64,3 +64,4 @@
name: '{{ jms_asset.spec_info.db_name }}' name: '{{ jms_asset.spec_info.db_name }}'
script: | script: |
SELECT @@version SELECT @@version
when: check_conn_after_change

View File

@ -100,7 +100,7 @@
become_password: "{{ account.become.ansible_password | default('') }}" become_password: "{{ account.become.ansible_password | default('') }}"
become_private_key_path: "{{ account.become.ansible_ssh_private_key_file | default(None) }}" become_private_key_path: "{{ account.become.ansible_ssh_private_key_file | default(None) }}"
old_ssh_version: "{{ jms_asset.old_ssh_version | default(False) }}" old_ssh_version: "{{ jms_asset.old_ssh_version | default(False) }}"
when: account.secret_type == "password" when: account.secret_type == "password" and check_conn_after_change
delegate_to: localhost delegate_to: localhost
- name: "Verify {{ account.username }} SSH KEY (paramiko)" - name: "Verify {{ account.username }} SSH KEY (paramiko)"
@ -111,5 +111,5 @@
login_private_key_path: "{{ account.private_key_path }}" login_private_key_path: "{{ account.private_key_path }}"
gateway_args: "{{ jms_asset.ansible_ssh_common_args | default(None) }}" gateway_args: "{{ jms_asset.ansible_ssh_common_args | default(None) }}"
old_ssh_version: "{{ jms_asset.old_ssh_version | default(False) }}" old_ssh_version: "{{ jms_asset.old_ssh_version | default(False) }}"
when: account.secret_type == "ssh_key" when: account.secret_type == "ssh_key" and check_conn_after_change
delegate_to: localhost delegate_to: localhost

View File

@ -100,7 +100,7 @@
become_password: "{{ account.become.ansible_password | default('') }}" become_password: "{{ account.become.ansible_password | default('') }}"
become_private_key_path: "{{ account.become.ansible_ssh_private_key_file | default(None) }}" become_private_key_path: "{{ account.become.ansible_ssh_private_key_file | default(None) }}"
old_ssh_version: "{{ jms_asset.old_ssh_version | default(False) }}" old_ssh_version: "{{ jms_asset.old_ssh_version | default(False) }}"
when: account.secret_type == "password" when: account.secret_type == "password" and check_conn_after_change
delegate_to: localhost delegate_to: localhost
- name: "Verify {{ account.username }} SSH KEY (paramiko)" - name: "Verify {{ account.username }} SSH KEY (paramiko)"
@ -111,5 +111,5 @@
login_private_key_path: "{{ account.private_key_path }}" login_private_key_path: "{{ account.private_key_path }}"
gateway_args: "{{ jms_asset.ansible_ssh_common_args | default(None) }}" gateway_args: "{{ jms_asset.ansible_ssh_common_args | default(None) }}"
old_ssh_version: "{{ jms_asset.old_ssh_version | default(False) }}" old_ssh_version: "{{ jms_asset.old_ssh_version | default(False) }}"
when: account.secret_type == "ssh_key" when: account.secret_type == "ssh_key" and check_conn_after_change
delegate_to: localhost delegate_to: localhost

View File

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

View File

@ -4,10 +4,6 @@
- name: Test privileged account - name: Test privileged account
ansible.windows.win_ping: ansible.windows.win_ping:
# - name: Print variables
# debug:
# msg: "Username: {{ account.username }}, Password: {{ account.secret }}"
- name: Change password - name: Change password
ansible.windows.win_user: ansible.windows.win_user:
fullname: "{{ account.username}}" fullname: "{{ account.username}}"
@ -31,5 +27,5 @@
login_password: "{{ account.secret }}" login_password: "{{ account.secret }}"
login_secret_type: "{{ account.secret_type }}" login_secret_type: "{{ account.secret_type }}"
gateway_args: "{{ jms_gateway | default(None) }}" gateway_args: "{{ jms_gateway | default(None) }}"
when: account.secret_type == "password" when: account.secret_type == "password" and check_conn_after_change
delegate_to: localhost delegate_to: localhost

View File

@ -93,6 +93,8 @@ class ChangeSecretManager(AccountBasePlaybookManager):
if host.get('error'): if host.get('error'):
return host return host
host['check_conn_after_change'] = self.execution.snapshot.get('check_conn_after_change', True)
accounts = self.get_accounts(account) accounts = self.get_accounts(account)
error_msg = _("No pending accounts found") error_msg = _("No pending accounts found")
if not accounts: if not accounts:

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

@ -53,3 +53,4 @@
ssl_certfile: "{{ jms_asset.secret_info.client_key | default('') }}" ssl_certfile: "{{ jms_asset.secret_info.client_key | default('') }}"
connection_options: connection_options:
- tlsAllowInvalidHostnames: "{{ jms_asset.spec_info.allow_invalid_cert}}" - tlsAllowInvalidHostnames: "{{ jms_asset.spec_info.allow_invalid_cert}}"
when: check_conn_after_change

View File

@ -54,3 +54,4 @@
client_cert: "{{ ssl_cert if check_ssl and ssl_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 }}" client_key: "{{ ssl_key if check_ssl and ssl_key | length > 0 else omit }}"
filter: version filter: version
when: check_conn_after_change

View File

@ -40,3 +40,4 @@
login_port: "{{ jms_asset.port }}" login_port: "{{ jms_asset.port }}"
login_database: "{{ jms_asset.spec_info.db_name }}" login_database: "{{ jms_asset.spec_info.db_name }}"
mode: "{{ account.mode }}" mode: "{{ account.mode }}"
when: check_conn_after_change

View File

@ -60,5 +60,6 @@
when: when:
- result is succeeded - result is succeeded
- change_info is succeeded - change_info is succeeded
- check_conn_after_change
register: result register: result
failed_when: not result.is_available failed_when: not result.is_available

View File

@ -66,3 +66,4 @@
name: '{{ jms_asset.spec_info.db_name }}' name: '{{ jms_asset.spec_info.db_name }}'
script: | script: |
SELECT @@version SELECT @@version
when: check_conn_after_change

View File

@ -100,7 +100,7 @@
become_password: "{{ account.become.ansible_password | default('') }}" become_password: "{{ account.become.ansible_password | default('') }}"
become_private_key_path: "{{ account.become.ansible_ssh_private_key_file | default(None) }}" become_private_key_path: "{{ account.become.ansible_ssh_private_key_file | default(None) }}"
old_ssh_version: "{{ jms_asset.old_ssh_version | default(False) }}" old_ssh_version: "{{ jms_asset.old_ssh_version | default(False) }}"
when: account.secret_type == "password" when: account.secret_type == "password" and check_conn_after_change
delegate_to: localhost delegate_to: localhost
- name: "Verify {{ account.username }} SSH KEY (paramiko)" - name: "Verify {{ account.username }} SSH KEY (paramiko)"
@ -111,6 +111,6 @@
login_private_key_path: "{{ account.private_key_path }}" login_private_key_path: "{{ account.private_key_path }}"
gateway_args: "{{ jms_asset.ansible_ssh_common_args | default(None) }}" gateway_args: "{{ jms_asset.ansible_ssh_common_args | default(None) }}"
old_ssh_version: "{{ jms_asset.old_ssh_version | default(False) }}" old_ssh_version: "{{ jms_asset.old_ssh_version | default(False) }}"
when: account.secret_type == "ssh_key" when: account.secret_type == "ssh_key" and check_conn_after_change
delegate_to: localhost delegate_to: localhost

View File

@ -100,7 +100,7 @@
become_password: "{{ account.become.ansible_password | default('') }}" become_password: "{{ account.become.ansible_password | default('') }}"
become_private_key_path: "{{ account.become.ansible_ssh_private_key_file | default(None) }}" become_private_key_path: "{{ account.become.ansible_ssh_private_key_file | default(None) }}"
old_ssh_version: "{{ jms_asset.old_ssh_version | default(False) }}" old_ssh_version: "{{ jms_asset.old_ssh_version | default(False) }}"
when: account.secret_type == "password" when: account.secret_type == "password" and check_conn_after_change
delegate_to: localhost delegate_to: localhost
- name: "Verify {{ account.username }} SSH KEY (paramiko)" - name: "Verify {{ account.username }} SSH KEY (paramiko)"
@ -111,6 +111,6 @@
login_private_key_path: "{{ account.private_key_path }}" login_private_key_path: "{{ account.private_key_path }}"
gateway_args: "{{ jms_asset.ansible_ssh_common_args | default(None) }}" gateway_args: "{{ jms_asset.ansible_ssh_common_args | default(None) }}"
old_ssh_version: "{{ jms_asset.old_ssh_version | default(False) }}" old_ssh_version: "{{ jms_asset.old_ssh_version | default(False) }}"
when: account.secret_type == "ssh_key" when: account.secret_type == "ssh_key" and check_conn_after_change
delegate_to: localhost delegate_to: localhost

View File

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

View File

@ -4,10 +4,6 @@
- name: Test privileged account - name: Test privileged account
ansible.windows.win_ping: ansible.windows.win_ping:
# - name: Print variables
# debug:
# msg: "Username: {{ account.username }}, Password: {{ account.secret }}"
- name: Push user password - name: Push user password
ansible.windows.win_user: ansible.windows.win_user:
fullname: "{{ account.username}}" fullname: "{{ account.username}}"
@ -31,5 +27,5 @@
login_password: "{{ account.secret }}" login_password: "{{ account.secret }}"
login_secret_type: "{{ account.secret_type }}" login_secret_type: "{{ account.secret_type }}"
gateway_args: "{{ jms_gateway | default(None) }}" gateway_args: "{{ jms_gateway | default(None) }}"
when: account.secret_type == "password" when: account.secret_type == "password" and check_conn_after_change
delegate_to: localhost delegate_to: localhost

View File

@ -15,17 +15,17 @@ class Migration(migrations.Migration):
name="risk", name="risk",
field=models.CharField( field=models.CharField(
choices=[ choices=[
("zombie", "Long time no login"), ('zombie', 'Long time no login'),
("ghost", "Not managed"), ('ghost', 'Not managed'),
("long_time_password", "Long time no change"), ('long_time_password', 'Long time no change'),
("weak_password", "Weak password"), ('weak_password', 'Weak password'),
("group_changed", "Group change"), ('password_error', 'Password error'),
("sudo_changed", "Sudo changed"), ('password_expired', 'Password expired'),
("account_deleted", "Account delete"), ('group_changed', 'Group change'),
("password_expired", "Password expired"), ('sudo_changed', 'Sudo changed'),
("no_admin_account", "No admin account"), ('account_deleted', 'Account delete'),
("password_error", "Password error"), ('no_admin_account', 'No admin account'),
("others", "Others"), ('others', 'Others')
], ],
max_length=128, max_length=128,
verbose_name="Risk", verbose_name="Risk",

View File

@ -0,0 +1,23 @@
# Generated by Django 4.1.13 on 2024-10-21 09:00
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('accounts', '0007_alter_accountrisk_risk'),
]
operations = [
migrations.AddField(
model_name='changesecretautomation',
name='check_conn_after_change',
field=models.BooleanField(default=True, verbose_name='Check connection after change'),
),
migrations.AddField(
model_name='pushaccountautomation',
name='check_conn_after_change',
field=models.BooleanField(default=True, verbose_name='Check connection after change'),
),
]

View File

@ -50,8 +50,14 @@ class AutomationExecution(AssetAutomationExecution):
class ChangeSecretMixin(SecretWithRandomMixin): class ChangeSecretMixin(SecretWithRandomMixin):
ssh_key_change_strategy = models.CharField( ssh_key_change_strategy = models.CharField(
choices=SSHKeyStrategy.choices, max_length=16, choices=SSHKeyStrategy.choices,
default=SSHKeyStrategy.set_jms, verbose_name=_('SSH key change strategy') max_length=16,
default=SSHKeyStrategy.set_jms,
verbose_name=_('SSH key change strategy')
)
check_conn_after_change = models.BooleanField(
default=True,
verbose_name=_('Check connection after change')
) )
get_all_assets: callable # get all assets get_all_assets: callable # get all assets
@ -81,5 +87,6 @@ class ChangeSecretMixin(SecretWithRandomMixin):
'password_rules': self.password_rules, 'password_rules': self.password_rules,
'secret_strategy': self.secret_strategy, 'secret_strategy': self.secret_strategy,
'ssh_key_change_strategy': self.ssh_key_change_strategy, 'ssh_key_change_strategy': self.ssh_key_change_strategy,
'check_conn_after_change': self.check_conn_after_change,
}) })
return attr_json return attr_json

View File

@ -52,8 +52,7 @@ class ChangeSecretAutomationSerializer(AuthValidateMixin, BaseAutomationSerializ
read_only_fields = BaseAutomationSerializer.Meta.read_only_fields read_only_fields = BaseAutomationSerializer.Meta.read_only_fields
fields = BaseAutomationSerializer.Meta.fields + read_only_fields + [ fields = BaseAutomationSerializer.Meta.fields + read_only_fields + [
'secret_type', 'secret_strategy', 'secret', 'password_rules', 'secret_type', 'secret_strategy', 'secret', 'password_rules',
'ssh_key_change_strategy', 'passphrase', 'params', 'ssh_key_change_strategy', 'passphrase', 'recipients', 'params', 'check_conn_after_change'
'recipients',
] ]
extra_kwargs = {**BaseAutomationSerializer.Meta.extra_kwargs, **{ extra_kwargs = {**BaseAutomationSerializer.Meta.extra_kwargs, **{
'accounts': {'required': True, 'help_text': _('Please enter your account username')}, 'accounts': {'required': True, 'help_text': _('Please enter your account username')},

View File

@ -29,14 +29,14 @@ from terminal.models import EndpointRule, Endpoint
from users.const import FileNameConflictResolution from users.const import FileNameConflictResolution
from users.const import RDPSmartSize, RDPColorQuality from users.const import RDPSmartSize, RDPColorQuality
from users.models import Preference from users.models import Preference
from ..models import ConnectionToken, date_expired_default from ..models import ConnectionToken, AdminConnectionToken, date_expired_default
from ..serializers import ( from ..serializers import (
ConnectionTokenSerializer, ConnectionTokenSecretSerializer, ConnectionTokenSerializer, ConnectionTokenSecretSerializer,
SuperConnectionTokenSerializer, ConnectTokenAppletOptionSerializer, SuperConnectionTokenSerializer, ConnectTokenAppletOptionSerializer,
ConnectionTokenReusableSerializer, ConnectTokenVirtualAppOptionSerializer ConnectionTokenReusableSerializer, ConnectTokenVirtualAppOptionSerializer
) )
__all__ = ['ConnectionTokenViewSet', 'SuperConnectionTokenViewSet'] __all__ = ['ConnectionTokenViewSet', 'SuperConnectionTokenViewSet', 'AdminConnectionTokenViewSet']
logger = get_logger(__name__) logger = get_logger(__name__)
@ -558,3 +558,14 @@ class SuperConnectionTokenViewSet(ConnectionTokenViewSet):
else: else:
logger.error('Release applet account error: {}'.format(lock_key)) logger.error('Release applet account error: {}'.format(lock_key))
return Response({'error': 'not found or expired'}, status=400) return Response({'error': 'not found or expired'}, status=400)
class AdminConnectionTokenViewSet(SuperConnectionTokenViewSet):
def check_permissions(self, request):
user = request.user
if not user.is_superuser:
self.permission_denied(request)
def get_queryset(self):
return AdminConnectionToken.objects.all()

View File

@ -37,3 +37,9 @@ class MFAType(TextChoices):
SMS = MFASms.name, MFASms.display_name SMS = MFASms.name, MFASms.display_name
Radius = MFARadius.name, MFARadius.display_name Radius = MFARadius.name, MFARadius.display_name
Custom = MFACustom.name, MFACustom.display_name Custom = MFACustom.name, MFACustom.display_name
class ConnectionTokenType(TextChoices):
ADMIN = 'admin', 'Admin'
SUPER = 'super', 'Super'
USER = 'user', 'User'

View File

@ -0,0 +1,30 @@
# Generated by Django 4.1.13 on 2024-11-11 11:04
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('authentication', '0003_sshkey'),
]
operations = [
migrations.AddField(
model_name='connectiontoken',
name='type',
field=models.CharField(choices=[('admin', 'Admin'), ('super', 'Super'), ('user', 'User')], default='user', max_length=16, verbose_name='Type'),
),
migrations.CreateModel(
name='AdminConnectionToken',
fields=[
],
options={
'verbose_name': 'Admin connection token',
'proxy': True,
'indexes': [],
'constraints': [],
},
bases=('authentication.connectiontoken',),
),
]

View File

@ -12,6 +12,7 @@ from rest_framework.exceptions import PermissionDenied
from accounts.models import VirtualAccount from accounts.models import VirtualAccount
from assets.const import Protocol from assets.const import Protocol
from assets.const.host import GATEWAY_NAME from assets.const.host import GATEWAY_NAME
from authentication.const import ConnectionTokenType
from common.db.fields import EncryptTextField from common.db.fields import EncryptTextField
from common.exceptions import JMSException from common.exceptions import JMSException
from common.utils import lazyproperty, pretty_string, bulk_get from common.utils import lazyproperty, pretty_string, bulk_get
@ -26,6 +27,8 @@ def date_expired_default():
class ConnectionToken(JMSOrgBaseModel): class ConnectionToken(JMSOrgBaseModel):
_type = ConnectionTokenType.USER
value = models.CharField(max_length=64, default='', verbose_name=_("Value")) value = models.CharField(max_length=64, default='', verbose_name=_("Value"))
user = models.ForeignKey( user = models.ForeignKey(
'users.User', on_delete=models.SET_NULL, null=True, blank=True, 'users.User', on_delete=models.SET_NULL, null=True, blank=True,
@ -52,6 +55,11 @@ class ConnectionToken(JMSOrgBaseModel):
) )
is_active = models.BooleanField(default=True, verbose_name=_("Active")) is_active = models.BooleanField(default=True, verbose_name=_("Active"))
type = models.CharField(
max_length=16, choices=ConnectionTokenType.choices,
default=ConnectionTokenType.USER, verbose_name=_('Type')
)
class Meta: class Meta:
ordering = ('-date_expired',) ordering = ('-date_expired',)
permissions = [ permissions = [
@ -60,6 +68,10 @@ class ConnectionToken(JMSOrgBaseModel):
] ]
verbose_name = _('Connection token') verbose_name = _('Connection token')
def save(self, *args, **kwargs):
self.type = self._meta.model._type
return super().save(*args, **kwargs)
@property @property
def is_expired(self): def is_expired(self):
return self.date_expired < timezone.now() return self.date_expired < timezone.now()
@ -268,9 +280,28 @@ class ConnectionToken(JMSOrgBaseModel):
class SuperConnectionToken(ConnectionToken): class SuperConnectionToken(ConnectionToken):
_type = ConnectionTokenType.SUPER
class Meta: class Meta:
proxy = True proxy = True
permissions = [ permissions = [
('view_superconnectiontokensecret', _('Can view super connection token secret')) ('view_superconnectiontokensecret', _('Can view super connection token secret'))
] ]
verbose_name = _("Super connection token") verbose_name = _("Super connection token")
class AdminConnectionTokenManager(models.Manager):
def get_queryset(self):
queryset = super().get_queryset()
queryset = queryset.filter(type=ConnectionTokenType.ADMIN)
return queryset
class AdminConnectionToken(ConnectionToken):
_type = ConnectionTokenType.ADMIN
objects = AdminConnectionTokenManager()
class Meta:
proxy = True
verbose_name = _("Admin connection token")

View File

@ -13,6 +13,7 @@ router.register('sso', api.SSOViewSet, 'sso')
router.register('temp-tokens', api.TempTokenViewSet, 'temp-token') router.register('temp-tokens', api.TempTokenViewSet, 'temp-token')
router.register('connection-token', api.ConnectionTokenViewSet, 'connection-token') router.register('connection-token', api.ConnectionTokenViewSet, 'connection-token')
router.register('super-connection-token', api.SuperConnectionTokenViewSet, 'super-connection-token') router.register('super-connection-token', api.SuperConnectionTokenViewSet, 'super-connection-token')
router.register('admin-connection-token', api.AdminConnectionTokenViewSet, 'admin-connection-token')
router.register('confirm', api.UserConfirmationViewSet, 'confirm') router.register('confirm', api.UserConfirmationViewSet, 'confirm')
router.register('ssh-key', api.SSHkeyViewSet, 'ssh-key') router.register('ssh-key', api.SSHkeyViewSet, 'ssh-key')