diff --git a/apps/accounts/api/automations/push_account.py b/apps/accounts/api/automations/push_account.py index 7b27cc7ca..b91bad379 100644 --- a/apps/accounts/api/automations/push_account.py +++ b/apps/accounts/api/automations/push_account.py @@ -30,6 +30,7 @@ class PushAccountExecutionViewSet(AutomationExecutionViewSet): ("list", "accounts.view_pushaccountexecution"), ("retrieve", "accounts.view_pushaccountexecution"), ("create", "accounts.add_pushaccountexecution"), + ("report", "accounts.view_pushaccountexecution"), ) tp = AutomationTypes.push_account @@ -44,9 +45,15 @@ class PushAccountRecordViewSet(ChangeSecretRecordViewSet): serializer_class = serializers.ChangeSecretRecordSerializer tp = AutomationTypes.push_account + rbac_perms = { + 'list': 'accounts.view_pushsecretrecord', + 'execute': 'accounts.add_pushsecretexecution', + 'secret': 'accounts.view_pushsecretrecord', + } + def get_queryset(self): qs = ChangeSecretRecord.get_valid_records() - return qs.objects.filter( + return qs.filter( execution__automation__type=self.tp ) diff --git a/apps/accounts/automations/gather_account/database/sqlserver/main.yml b/apps/accounts/automations/gather_account/database/sqlserver/main.yml index 90bbe8cdb..252f5095e 100644 --- a/apps/accounts/automations/gather_account/database/sqlserver/main.yml +++ b/apps/accounts/automations/gather_account/database/sqlserver/main.yml @@ -12,7 +12,26 @@ login_port: "{{ jms_asset.port }}" name: '{{ jms_asset.spec_info.db_name }}' 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 register: db_info diff --git a/apps/accounts/automations/gather_account/filter.py b/apps/accounts/automations/gather_account/filter.py index 757232fe3..809e99f9d 100644 --- a/apps/accounts/automations/gather_account/filter.py +++ b/apps/accounts/automations/gather_account/filter.py @@ -72,11 +72,14 @@ class GatherAccountsFilter: 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': None, - 'date_password_expired': None, - 'date_last_login': None, + '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 = { @@ -84,6 +87,7 @@ class GatherAccountsFilter: 'is_disabled': user_info.get('is_disabled', ''), 'default_database_name': user_info.get('default_database_name', ''), } + print(user) user['detail'] = detail result[user['username']] = user return result diff --git a/apps/accounts/automations/gather_account/manager.py b/apps/accounts/automations/gather_account/manager.py index f2ee64bbb..4d7840073 100644 --- a/apps/accounts/automations/gather_account/manager.py +++ b/apps/accounts/automations/gather_account/manager.py @@ -270,7 +270,7 @@ class GatherAccountsManager(AccountBasePlaybookManager): lost_users = ori_ga_users - remote_users if lost_users: queryset.filter(username__in=lost_users).update( - status="", remote_present=False + status=ConfirmOrIgnore.pending, remote_present=False ) self.summary["lost_accounts"] += len(lost_users) for username in lost_users: @@ -285,7 +285,7 @@ class GatherAccountsManager(AccountBasePlaybookManager): # 标识状态为 待处理, 让管理员去确认 ga_added_users = ori_ga_users - ori_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) # 收集的账号 比 账号列表少的 # 这个好像不不用对比,原始情况就这样 diff --git a/apps/accounts/migrations/0022_alter_changesecretrecord_options_and_more.py b/apps/accounts/migrations/0022_alter_changesecretrecord_options_and_more.py new file mode 100644 index 000000000..21bbc2c08 --- /dev/null +++ b/apps/accounts/migrations/0022_alter_changesecretrecord_options_and_more.py @@ -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'), + ), + ] diff --git a/apps/accounts/models/automations/change_secret.py b/apps/accounts/models/automations/change_secret.py index 00e7cad04..d79c6660c 100644 --- a/apps/accounts/models/automations/change_secret.py +++ b/apps/accounts/models/automations/change_secret.py @@ -45,8 +45,11 @@ class ChangeSecretRecord(JMSBaseModel): error = models.TextField(blank=True, null=True, verbose_name=_('Error')) class Meta: - ordering = ('-date_created',) verbose_name = _("Change secret record") + permissions = [ + ('view_pushsecretrecord', _('Can view change secret execution')), + ('add_pushsecretexecution', _('Can add change secret execution')), + ] def __str__(self): return f'{self.account.username}@{self.asset}' diff --git a/apps/accounts/risk_handlers.py b/apps/accounts/risk_handlers.py index 389b8d963..e2911da7e 100644 --- a/apps/accounts/risk_handlers.py +++ b/apps/accounts/risk_handlers.py @@ -58,7 +58,8 @@ class RiskHandler: return r.first() 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): pass diff --git a/apps/accounts/templates/accounts/change_secret_report.html b/apps/accounts/templates/accounts/change_secret_report.html index 5459d23d6..2f836b2f8 100644 --- a/apps/accounts/templates/accounts/change_secret_report.html +++ b/apps/accounts/templates/accounts/change_secret_report.html @@ -1,7 +1,7 @@ {% load i18n %}
-

{% trans 'The following is a summary of account change secret tasks, please review and handle them' %}

+

{% trans 'The following is a summary of account change secret or push tasks, please read and process' %}

diff --git a/apps/common/utils/random.py b/apps/common/utils/random.py index 7978267b9..2fcebdf94 100644 --- a/apps/common/utils/random.py +++ b/apps/common/utils/random.py @@ -18,9 +18,8 @@ def random_ip(): 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() - seq = list(s) while length > 0: index = secrets.randbelow(len(seq) - 1) @@ -29,7 +28,7 @@ def random_replace_char(s, chars, length): seq[index] = secrets.choice(chars) using_index.add(index) length -= 1 - return ''.join(seq) + return seq def remove_exclude_char(s, exclude_chars): @@ -47,19 +46,40 @@ def random_string( if length < 4: raise ValueError('The length of the string must be greater than 3') - chars_map = ( - (lower, string.ascii_lowercase), - (upper, string.ascii_uppercase), - (digit, string.digits), - ) - chars = ''.join([i[1] for i in chars_map if i[0]]) - chars = remove_exclude_char(chars, exclude_chars) - texts = list(secrets.choice(chars) for __ in range(length)) - texts = ''.join(texts) + char_list = [] + if lower: + + lower_chars = remove_exclude_char(string.ascii_lowercase, exclude_chars) + if not lower_chars: + raise ValueError('After excluding characters, no lowercase letters are available.') + char_list.append(lower_chars) + + 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: - 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 - texts = random_replace_char(texts, symbols, symbol_num) - return texts + seq = random_replace_char(seq, symbols, symbol_num) + secret_chars += seq + + secrets.SystemRandom().shuffle(secret_chars) + return ''.join(secret_chars) diff --git a/apps/tickets/filters.py b/apps/tickets/filters.py index dcba10036..edfba460d 100644 --- a/apps/tickets/filters.py +++ b/apps/tickets/filters.py @@ -23,7 +23,8 @@ class TicketFilter(BaseFilterSet): def filter_assignees_id(self, queryset, name, value): 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):