mirror of https://github.com/jumpserver/jumpserver
Merge branch 'pam' of github.com:jumpserver/jumpserver into pam
commit
3d5b6376e8
|
@ -30,6 +30,7 @@ class PushAccountExecutionViewSet(AutomationExecutionViewSet):
|
||||||
("list", "accounts.view_pushaccountexecution"),
|
("list", "accounts.view_pushaccountexecution"),
|
||||||
("retrieve", "accounts.view_pushaccountexecution"),
|
("retrieve", "accounts.view_pushaccountexecution"),
|
||||||
("create", "accounts.add_pushaccountexecution"),
|
("create", "accounts.add_pushaccountexecution"),
|
||||||
|
("report", "accounts.view_pushaccountexecution"),
|
||||||
)
|
)
|
||||||
|
|
||||||
tp = AutomationTypes.push_account
|
tp = AutomationTypes.push_account
|
||||||
|
@ -44,9 +45,15 @@ class PushAccountRecordViewSet(ChangeSecretRecordViewSet):
|
||||||
serializer_class = serializers.ChangeSecretRecordSerializer
|
serializer_class = serializers.ChangeSecretRecordSerializer
|
||||||
tp = AutomationTypes.push_account
|
tp = AutomationTypes.push_account
|
||||||
|
|
||||||
|
rbac_perms = {
|
||||||
|
'list': 'accounts.view_pushsecretrecord',
|
||||||
|
'execute': 'accounts.add_pushsecretexecution',
|
||||||
|
'secret': 'accounts.view_pushsecretrecord',
|
||||||
|
}
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
qs = ChangeSecretRecord.get_valid_records()
|
qs = ChangeSecretRecord.get_valid_records()
|
||||||
return qs.objects.filter(
|
return qs.filter(
|
||||||
execution__automation__type=self.tp
|
execution__automation__type=self.tp
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,26 @@
|
||||||
login_port: "{{ jms_asset.port }}"
|
login_port: "{{ jms_asset.port }}"
|
||||||
name: '{{ jms_asset.spec_info.db_name }}'
|
name: '{{ jms_asset.spec_info.db_name }}'
|
||||||
script: |
|
script: |
|
||||||
select * from sys.sql_logins
|
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
|
output: dict
|
||||||
register: db_info
|
register: db_info
|
||||||
|
|
||||||
|
|
|
@ -72,11 +72,14 @@ class GatherAccountsFilter:
|
||||||
return {}
|
return {}
|
||||||
result = {}
|
result = {}
|
||||||
for user_info in info[0][0]:
|
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 = {
|
user = {
|
||||||
'username': user_info.get('name', ''),
|
'username': user_info.get('name', ''),
|
||||||
'date_password_change': None,
|
'date_password_change': parse_date(user_info.get('modify_date')),
|
||||||
'date_password_expired': None,
|
'date_password_expired': date_password_expired,
|
||||||
'date_last_login': None,
|
'date_last_login': parse_date(user_info.get('last_login_time')),
|
||||||
'groups': '',
|
'groups': '',
|
||||||
}
|
}
|
||||||
detail = {
|
detail = {
|
||||||
|
@ -84,6 +87,7 @@ class GatherAccountsFilter:
|
||||||
'is_disabled': user_info.get('is_disabled', ''),
|
'is_disabled': user_info.get('is_disabled', ''),
|
||||||
'default_database_name': user_info.get('default_database_name', ''),
|
'default_database_name': user_info.get('default_database_name', ''),
|
||||||
}
|
}
|
||||||
|
print(user)
|
||||||
user['detail'] = detail
|
user['detail'] = detail
|
||||||
result[user['username']] = user
|
result[user['username']] = user
|
||||||
return result
|
return result
|
||||||
|
|
|
@ -270,7 +270,7 @@ class GatherAccountsManager(AccountBasePlaybookManager):
|
||||||
lost_users = ori_ga_users - remote_users
|
lost_users = ori_ga_users - remote_users
|
||||||
if lost_users:
|
if lost_users:
|
||||||
queryset.filter(username__in=lost_users).update(
|
queryset.filter(username__in=lost_users).update(
|
||||||
status="", remote_present=False
|
status=ConfirmOrIgnore.pending, remote_present=False
|
||||||
)
|
)
|
||||||
self.summary["lost_accounts"] += len(lost_users)
|
self.summary["lost_accounts"] += len(lost_users)
|
||||||
for username in lost_users:
|
for username in lost_users:
|
||||||
|
@ -285,7 +285,7 @@ class GatherAccountsManager(AccountBasePlaybookManager):
|
||||||
# 标识状态为 待处理, 让管理员去确认
|
# 标识状态为 待处理, 让管理员去确认
|
||||||
ga_added_users = ori_ga_users - ori_users
|
ga_added_users = ori_ga_users - ori_users
|
||||||
if ga_added_users:
|
if ga_added_users:
|
||||||
queryset.filter(username__in=ga_added_users).update(status="")
|
queryset.filter(username__in=ga_added_users).update(status=ConfirmOrIgnore.pending)
|
||||||
|
|
||||||
# 收集的账号 比 账号列表少的
|
# 收集的账号 比 账号列表少的
|
||||||
# 这个好像不不用对比,原始情况就这样
|
# 这个好像不不用对比,原始情况就这样
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
# Generated by Django 4.1.13 on 2024-12-09 03:15
|
||||||
|
|
||||||
|
from django.db import migrations
|
||||||
|
import private_storage.fields
|
||||||
|
import private_storage.storage.files
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('accounts', '0021_remove_pushaccountautomation_action_and_more'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AlterModelOptions(
|
||||||
|
name='changesecretrecord',
|
||||||
|
options={'permissions': [('view_pushsecretrecord', 'Can view change secret execution'), ('add_pushsecretexecution', 'Can add change secret execution')], 'verbose_name': 'Change secret record'},
|
||||||
|
),
|
||||||
|
migrations.AlterField(
|
||||||
|
model_name='integrationapplication',
|
||||||
|
name='logo',
|
||||||
|
field=private_storage.fields.PrivateImageField(max_length=128, storage=private_storage.storage.files.PrivateFileSystemStorage(), upload_to='images', verbose_name='Logo'),
|
||||||
|
),
|
||||||
|
]
|
|
@ -45,8 +45,11 @@ class ChangeSecretRecord(JMSBaseModel):
|
||||||
error = models.TextField(blank=True, null=True, verbose_name=_('Error'))
|
error = models.TextField(blank=True, null=True, verbose_name=_('Error'))
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
ordering = ('-date_created',)
|
|
||||||
verbose_name = _("Change secret record")
|
verbose_name = _("Change secret record")
|
||||||
|
permissions = [
|
||||||
|
('view_pushsecretrecord', _('Can view change secret execution')),
|
||||||
|
('add_pushsecretexecution', _('Can add change secret execution')),
|
||||||
|
]
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f'{self.account.username}@{self.asset}'
|
return f'{self.account.username}@{self.asset}'
|
||||||
|
|
|
@ -58,7 +58,8 @@ class RiskHandler:
|
||||||
return r.first()
|
return r.first()
|
||||||
|
|
||||||
def handle_ignore(self):
|
def handle_ignore(self):
|
||||||
pass
|
GatheredAccount.objects.filter(asset=self.asset, username=self.username).update(status=ConfirmOrIgnore.ignored)
|
||||||
|
self.risk = 'ignored'
|
||||||
|
|
||||||
def handle_review(self):
|
def handle_review(self):
|
||||||
pass
|
pass
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
<div class='summary'>
|
<div class='summary'>
|
||||||
<p>{% trans 'The following is a summary of account change secret tasks, please review and handle them' %}</p>
|
<p>{% trans 'The following is a summary of account change secret or push tasks, please read and process' %}</p>
|
||||||
<table>
|
<table>
|
||||||
<caption></caption>
|
<caption></caption>
|
||||||
<thead>
|
<thead>
|
||||||
|
|
|
@ -18,9 +18,8 @@ def random_ip():
|
||||||
return socket.inet_ntoa(struct.pack('>I', random.randint(1, 0xffffffff)))
|
return socket.inet_ntoa(struct.pack('>I', random.randint(1, 0xffffffff)))
|
||||||
|
|
||||||
|
|
||||||
def random_replace_char(s, chars, length):
|
def random_replace_char(seq, chars, length):
|
||||||
using_index = set()
|
using_index = set()
|
||||||
seq = list(s)
|
|
||||||
|
|
||||||
while length > 0:
|
while length > 0:
|
||||||
index = secrets.randbelow(len(seq) - 1)
|
index = secrets.randbelow(len(seq) - 1)
|
||||||
|
@ -29,7 +28,7 @@ def random_replace_char(s, chars, length):
|
||||||
seq[index] = secrets.choice(chars)
|
seq[index] = secrets.choice(chars)
|
||||||
using_index.add(index)
|
using_index.add(index)
|
||||||
length -= 1
|
length -= 1
|
||||||
return ''.join(seq)
|
return seq
|
||||||
|
|
||||||
|
|
||||||
def remove_exclude_char(s, exclude_chars):
|
def remove_exclude_char(s, exclude_chars):
|
||||||
|
@ -47,19 +46,40 @@ def random_string(
|
||||||
if length < 4:
|
if length < 4:
|
||||||
raise ValueError('The length of the string must be greater than 3')
|
raise ValueError('The length of the string must be greater than 3')
|
||||||
|
|
||||||
chars_map = (
|
char_list = []
|
||||||
(lower, string.ascii_lowercase),
|
if lower:
|
||||||
(upper, string.ascii_uppercase),
|
|
||||||
(digit, string.digits),
|
lower_chars = remove_exclude_char(string.ascii_lowercase, exclude_chars)
|
||||||
)
|
if not lower_chars:
|
||||||
chars = ''.join([i[1] for i in chars_map if i[0]])
|
raise ValueError('After excluding characters, no lowercase letters are available.')
|
||||||
chars = remove_exclude_char(chars, exclude_chars)
|
char_list.append(lower_chars)
|
||||||
texts = list(secrets.choice(chars) for __ in range(length))
|
|
||||||
texts = ''.join(texts)
|
if upper:
|
||||||
|
upper_chars = remove_exclude_char(string.ascii_uppercase, exclude_chars)
|
||||||
|
if not upper_chars:
|
||||||
|
raise ValueError('After excluding characters, no uppercase letters are available.')
|
||||||
|
char_list.append(upper_chars)
|
||||||
|
|
||||||
|
if digit:
|
||||||
|
digit_chars = remove_exclude_char(string.digits, exclude_chars)
|
||||||
|
if not digit_chars:
|
||||||
|
raise ValueError('After excluding characters, no digits are available.')
|
||||||
|
char_list.append(digit_chars)
|
||||||
|
|
||||||
|
secret_chars = [secrets.choice(chars) for chars in char_list]
|
||||||
|
|
||||||
|
all_chars = ''.join(char_list)
|
||||||
|
|
||||||
|
remaining_length = length - len(secret_chars)
|
||||||
|
seq = [secrets.choice(all_chars) for _ in range(remaining_length)]
|
||||||
|
|
||||||
# 控制一下特殊字符的数量, 别随机出来太多
|
|
||||||
if special_char:
|
if special_char:
|
||||||
symbols = remove_exclude_char(symbols, exclude_chars)
|
special_chars = remove_exclude_char(symbols, exclude_chars)
|
||||||
|
if not special_chars:
|
||||||
|
raise ValueError('After excluding characters, no special characters are available.')
|
||||||
symbol_num = length // 16 + 1
|
symbol_num = length // 16 + 1
|
||||||
texts = random_replace_char(texts, symbols, symbol_num)
|
seq = random_replace_char(seq, symbols, symbol_num)
|
||||||
return texts
|
secret_chars += seq
|
||||||
|
|
||||||
|
secrets.SystemRandom().shuffle(secret_chars)
|
||||||
|
return ''.join(secret_chars)
|
||||||
|
|
|
@ -23,7 +23,8 @@ class TicketFilter(BaseFilterSet):
|
||||||
|
|
||||||
def filter_assignees_id(self, queryset, name, value):
|
def filter_assignees_id(self, queryset, name, value):
|
||||||
return queryset.filter(
|
return queryset.filter(
|
||||||
ticket_steps__ticket_assignees__assignee__id=value
|
ticket_steps__level=F('approval_step'),
|
||||||
|
ticket_steps__ticket_assignees__assignee_id=value
|
||||||
)
|
)
|
||||||
|
|
||||||
def filter_relevant_asset(self, queryset, name, value):
|
def filter_relevant_asset(self, queryset, name, value):
|
||||||
|
|
Loading…
Reference in New Issue