mirror of https://github.com/jumpserver/jumpserver
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>pull/14586/head
parent
ab9d8afe80
commit
c658252c01
|
@ -0,0 +1,24 @@
|
|||
- hosts: sqlserver
|
||||
gather_facts: no
|
||||
vars:
|
||||
ansible_python_interpreter: /opt/py3/bin/python
|
||||
|
||||
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 * from sys.sql_logins
|
||||
output: dict
|
||||
register: db_info
|
||||
|
||||
- name: Define info by set_fact
|
||||
set_fact:
|
||||
info: "{{ db_info.query_results_dict }}"
|
||||
|
||||
- debug:
|
||||
var: info
|
|
@ -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 アカウントの収集
|
|
@ -4,16 +4,25 @@ from datetime import datetime
|
|||
__all__ = ['GatherAccountsFilter']
|
||||
|
||||
|
||||
def parse_date(date_str, default=''):
|
||||
def parse_date(date_str, default=None):
|
||||
if not date_str:
|
||||
return default
|
||||
if date_str == 'Never':
|
||||
return None
|
||||
try:
|
||||
dt = datetime.strptime(date_str, '%Y/%m/%d %H:%M:%S')
|
||||
return timezone.make_aware(dt, timezone.get_current_timezone())
|
||||
except ValueError:
|
||||
if date_str in ['Never', 'null']:
|
||||
return default
|
||||
formats = [
|
||||
'%Y/%m/%d %H:%M:%S',
|
||||
'%Y-%m-%dT%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
|
||||
|
||||
|
||||
# TODO 后期会挪到 playbook 中
|
||||
|
@ -24,17 +33,83 @@ class GatherAccountsFilter:
|
|||
@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] = {}
|
||||
for username, user_info in info.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 in info:
|
||||
result[username] = {}
|
||||
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 = {
|
||||
'canlogin': 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]:
|
||||
user = {
|
||||
'username': user_info.get('name', ''),
|
||||
'date_password_change': None,
|
||||
'date_password_expired': None,
|
||||
'date_last_login': None,
|
||||
'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
|
||||
|
@ -105,10 +180,12 @@ class GatherAccountsFilter:
|
|||
user['date_password_change'] = start_date + timezone.timedelta(days=int(_password_date[0]))
|
||||
if _password_date[1] and _password_date[1] != '0':
|
||||
user['date_password_expired'] = start_date + timezone.timedelta(days=int(_password_date[1]))
|
||||
|
||||
user['groups'] = username_groups.get(username) or ''
|
||||
user['sudoers'] = username_sudo.get(username) or ''
|
||||
user['authorized_keys'] = username_authorized.get(username) or ''
|
||||
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
|
||||
|
||||
|
@ -125,13 +202,13 @@ class GatherAccountsFilter:
|
|||
if len(parts) == 2:
|
||||
key, value = parts
|
||||
user_info[key.strip()] = value.strip()
|
||||
detail = {'groups': user_info.get('Global Group memberships', ''), }
|
||||
user = {
|
||||
'username': user_info.get('User name', ''),
|
||||
'groups': user_info.get('Global Group memberships', ''),
|
||||
'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', '')),
|
||||
'can_change_password': user_info.get('User may change password', 'Yes')
|
||||
'groups': detail,
|
||||
}
|
||||
result[user['username']] = user
|
||||
return result
|
||||
|
|
|
@ -16,9 +16,9 @@ from ..base.manager import AccountBasePlaybookManager
|
|||
logger = get_logger(__name__)
|
||||
|
||||
risk_items = [
|
||||
"authorized_keys",
|
||||
"sudoers",
|
||||
"groups",
|
||||
# "authorized_keys",
|
||||
# "sudoers",
|
||||
# "groups",
|
||||
]
|
||||
|
||||
diff_items = risk_items + [
|
||||
|
@ -81,8 +81,8 @@ class AnalyseAccountRisk:
|
|||
|
||||
risks = []
|
||||
for k, v in diff.items():
|
||||
if k not in risk_items:
|
||||
continue
|
||||
# if k not in risk_items:
|
||||
# continue
|
||||
risks.append(
|
||||
dict(
|
||||
asset=ori_account.asset,
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
# Generated by Django 4.1.13 on 2024-12-03 07:01
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('accounts', '0018_changesecretrecord_ignore_fail_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='gatheredaccount',
|
||||
name='authorized_keys',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='gatheredaccount',
|
||||
name='groups',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='gatheredaccount',
|
||||
name='sudoers',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='gatheredaccount',
|
||||
name='detail',
|
||||
field=models.JSONField(blank=True, default=dict, verbose_name='Detail'),
|
||||
),
|
||||
]
|
|
@ -9,7 +9,7 @@ from common.utils.timezone import is_date_more_than
|
|||
from orgs.mixins.models import JMSOrgBaseModel
|
||||
from .base import AccountBaseAutomation
|
||||
|
||||
__all__ = ['GatherAccountsAutomation', 'GatheredAccount',]
|
||||
__all__ = ['GatherAccountsAutomation', 'GatheredAccount']
|
||||
|
||||
|
||||
class GatheredAccount(JMSOrgBaseModel):
|
||||
|
@ -17,14 +17,13 @@ class GatheredAccount(JMSOrgBaseModel):
|
|||
username = models.CharField(max_length=32, blank=True, db_index=True, verbose_name=_('Username'))
|
||||
address_last_login = models.CharField(max_length=39, default='', verbose_name=_("Address login"))
|
||||
date_last_login = models.DateTimeField(null=True, verbose_name=_("Date login"))
|
||||
authorized_keys = models.TextField(default='', blank=True, verbose_name=_("Authorized keys"))
|
||||
sudoers = models.TextField(default='', verbose_name=_("Sudoers"), blank=True)
|
||||
groups = models.TextField(default='', blank=True, verbose_name=_("Groups"))
|
||||
remote_present = models.BooleanField(default=True, verbose_name=_("Remote present")) # 远端资产上是否还存在
|
||||
present = models.BooleanField(default=False, verbose_name=_("Present")) # 系统资产上是否还存在
|
||||
date_password_change = models.DateTimeField(null=True, verbose_name=_("Date change password"))
|
||||
date_password_expired = models.DateTimeField(null=True, verbose_name=_("Date password expired"))
|
||||
status = models.CharField(max_length=32, default=ConfirmOrIgnore.pending, blank=True, choices=ConfirmOrIgnore.choices, verbose_name=_("Status"))
|
||||
status = models.CharField(max_length=32, default=ConfirmOrIgnore.pending, blank=True,
|
||||
choices=ConfirmOrIgnore.choices, verbose_name=_("Status"))
|
||||
detail = models.JSONField(default=dict, blank=True, verbose_name=_("Detail"))
|
||||
|
||||
@property
|
||||
def address(self):
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
# Generated by Django 4.1.13 on 2024-12-04 07:16
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
def migrate_platform_sqlserver_automation(apps, schema_editor):
|
||||
platform_model = apps.get_model('assets', 'Platform')
|
||||
platform = platform_model.objects.filter(name='SQLServer').first()
|
||||
|
||||
if platform:
|
||||
automation = platform.automation
|
||||
automation.gather_accounts_method = 'gather_accounts_sqlserver'
|
||||
automation.save()
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
dependencies = [
|
||||
('assets', '0010_alter_automationexecution_duration'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RunPython(migrate_platform_sqlserver_automation)
|
||||
]
|
|
@ -1,6 +1,7 @@
|
|||
#!/usr/bin/python
|
||||
|
||||
from __future__ import absolute_import, division, print_function
|
||||
|
||||
__metaclass__ = type
|
||||
|
||||
DOCUMENTATION = r'''
|
||||
|
@ -161,6 +162,7 @@ class OracleInfo(object):
|
|||
|
||||
def __get_settings(self):
|
||||
"""Get global variables (instance settings)."""
|
||||
|
||||
def _set_settings_value(item_dict):
|
||||
try:
|
||||
self.info['settings'][item_dict['name']] = item_dict['value']
|
||||
|
@ -178,11 +180,30 @@ class OracleInfo(object):
|
|||
|
||||
def __get_users(self):
|
||||
"""Get user info."""
|
||||
|
||||
def _set_users_roles(username, item_dict):
|
||||
users_sql = f"SELECT GRANTED_ROLE FROM DBA_ROLE_PRIVS WHERE GRANTEE = '{username}';"
|
||||
try:
|
||||
rtn, err = self.oracle_client.execute(users_sql, exception_to_fail=True)
|
||||
item_dict['roles'] = [r['role'] for r in rtn]
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def _set_users_privileges(username, item_dict):
|
||||
users_sql = f"SELECT PRIVILEGE FROM DBA_SYS_PRIVS WHERE GRANTEE = '{username}';"
|
||||
try:
|
||||
rtn, err = self.oracle_client.execute(users_sql, exception_to_fail=True)
|
||||
item_dict['privileges'] = [r['privilege'] for r in rtn]
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
def _set_users_value(item_dict):
|
||||
try:
|
||||
tablespace = item_dict.pop('default_tablespace')
|
||||
username = item_dict.pop('username')
|
||||
partial_users = self.info['users'].get(tablespace, {})
|
||||
_set_users_roles(username, item_dict)
|
||||
_set_users_privileges(username, item_dict)
|
||||
partial_users[username] = item_dict
|
||||
self.info['users'][tablespace] = partial_users
|
||||
except KeyError:
|
||||
|
@ -198,6 +219,7 @@ class OracleInfo(object):
|
|||
|
||||
def __get_databases(self, exclude_fields):
|
||||
"""Get info about databases."""
|
||||
|
||||
def _set_databases_value(item_dict):
|
||||
try:
|
||||
tablespace_name = item_dict.pop('tablespace_name')
|
||||
|
|
|
@ -125,7 +125,8 @@
|
|||
fontSize: 13,
|
||||
lineHeight: 1.2,
|
||||
rightClickSelectsWord: true,
|
||||
disableStdin: true
|
||||
disableStdin: true,
|
||||
scrollback: 9999999,
|
||||
});
|
||||
term.open(document.getElementById('term'));
|
||||
window.fit.fit(term);
|
||||
|
|
Loading…
Reference in New Issue