mirror of https://github.com/jumpserver/jumpserver
318 lines
11 KiB
Python
318 lines
11 KiB
Python
# -*- coding: utf-8 -*-
|
||
#
|
||
from functools import reduce
|
||
from django.db.models import F, CharField, Value, IntegerField, Q, Count
|
||
from django.db.models.functions import Concat
|
||
|
||
from common.utils import get_object_or_none
|
||
from orgs.utils import current_org
|
||
from ..models import AuthBook, SystemUser, Asset, AdminUser
|
||
from .base import BaseBackend
|
||
|
||
|
||
class DBBackend(BaseBackend):
|
||
union_id_length = 2
|
||
|
||
def __init__(self, queryset=None):
|
||
if queryset is None:
|
||
queryset = self.all()
|
||
self.queryset = queryset
|
||
|
||
def _clone(self):
|
||
return self.__class__(self.queryset)
|
||
|
||
def all(self):
|
||
return AuthBook.objects.none()
|
||
|
||
def count(self):
|
||
return self.queryset.count()
|
||
|
||
def get_queryset(self):
|
||
return self.queryset
|
||
|
||
def delete(self, union_id):
|
||
cleaned_union_id = union_id.split('_')
|
||
# 如果union_id通不过本检查,代表可能不是本backend, 应该返回空
|
||
if not self._check_union_id(union_id, cleaned_union_id):
|
||
return
|
||
return self._perform_delete_by_union_id(cleaned_union_id)
|
||
|
||
def _perform_delete_by_union_id(self, union_id_cleaned):
|
||
pass
|
||
|
||
def filter(self, assets=None, node=None, prefer=None, prefer_id=None,
|
||
union_id=None, id__in=None, **kwargs):
|
||
clone = self._clone()
|
||
clone._filter_union_id(union_id)
|
||
clone._filter_prefer(prefer, prefer_id)
|
||
clone._filter_node(node)
|
||
clone._filter_assets(assets)
|
||
clone._filter_other(kwargs)
|
||
clone._filter_id_in(id__in)
|
||
return clone
|
||
|
||
def _filter_union_id(self, union_id):
|
||
if not union_id:
|
||
return
|
||
cleaned_union_id = union_id.split('_')
|
||
# 如果union_id通不过本检查,代表可能不是本backend, 应该返回空
|
||
if not self._check_union_id(union_id, cleaned_union_id):
|
||
self.queryset = self.queryset.none()
|
||
return
|
||
return self._perform_filter_union_id(union_id, cleaned_union_id)
|
||
|
||
def _check_union_id(self, union_id, cleaned_union_id):
|
||
return union_id and len(cleaned_union_id) == self.union_id_length
|
||
|
||
def _perform_filter_union_id(self, union_id, union_id_cleaned):
|
||
self.queryset = self.queryset.filter(union_id=union_id)
|
||
|
||
def _filter_assets(self, assets):
|
||
assets_id = self.make_assets_as_id(assets)
|
||
if assets_id:
|
||
self.queryset = self.queryset.filter(asset_id__in=assets_id)
|
||
|
||
def _filter_node(self, node):
|
||
pass
|
||
|
||
def _filter_id_in(self, ids):
|
||
if ids and isinstance(ids, list):
|
||
self.queryset = self.queryset.filter(union_id__in=ids)
|
||
|
||
@staticmethod
|
||
def clean_kwargs(kwargs):
|
||
return {k: v for k, v in kwargs.items() if v}
|
||
|
||
def _filter_other(self, kwargs):
|
||
kwargs = self.clean_kwargs(kwargs)
|
||
if kwargs:
|
||
self.queryset = self.queryset.filter(**kwargs)
|
||
|
||
def _filter_prefer(self, prefer, prefer_id):
|
||
pass
|
||
|
||
def search(self, item):
|
||
qs = []
|
||
for i in ['hostname', 'ip', 'username']:
|
||
kwargs = {i + '__startswith': item}
|
||
qs.append(Q(**kwargs))
|
||
q = reduce(lambda x, y: x | y, qs)
|
||
clone = self._clone()
|
||
clone.queryset = clone.queryset.filter(q).distinct()
|
||
return clone
|
||
|
||
|
||
class SystemUserBackend(DBBackend):
|
||
model = SystemUser.assets.through
|
||
backend = 'system_user'
|
||
prefer = backend
|
||
base_score = 0
|
||
union_id_length = 2
|
||
|
||
def _filter_prefer(self, prefer, prefer_id):
|
||
if prefer and prefer != self.prefer:
|
||
self.queryset = self.queryset.none()
|
||
|
||
if prefer_id:
|
||
self.queryset = self.queryset.filter(systemuser__id=prefer_id)
|
||
|
||
def _perform_filter_union_id(self, union_id, union_id_cleaned):
|
||
system_user_id, asset_id = union_id_cleaned
|
||
self.queryset = self.queryset.filter(
|
||
asset_id=asset_id, systemuser__id=system_user_id,
|
||
)
|
||
|
||
def _perform_delete_by_union_id(self, union_id_cleaned):
|
||
system_user_id, asset_id = union_id_cleaned
|
||
system_user = get_object_or_none(SystemUser, pk=system_user_id)
|
||
asset = get_object_or_none(Asset, pk=asset_id)
|
||
if all((system_user, asset)):
|
||
system_user.assets.remove(asset)
|
||
|
||
def _filter_node(self, node):
|
||
if node:
|
||
self.queryset = self.queryset.filter(asset__nodes__id=node.id)
|
||
|
||
def get_annotate(self):
|
||
kwargs = dict(
|
||
hostname=F("asset__hostname"),
|
||
ip=F("asset__ip"),
|
||
username=F("systemuser__username"),
|
||
password=F("systemuser__password"),
|
||
private_key=F("systemuser__private_key"),
|
||
public_key=F("systemuser__public_key"),
|
||
score=F("systemuser__priority") + self.base_score,
|
||
version=Value(0, IntegerField()),
|
||
date_created=F("systemuser__date_created"),
|
||
date_updated=F("systemuser__date_updated"),
|
||
asset_username=Concat(F("asset__id"), Value("_"),
|
||
F("systemuser__username"),
|
||
output_field=CharField()),
|
||
union_id=Concat(F("systemuser_id"), Value("_"), F("asset_id"),
|
||
output_field=CharField()),
|
||
org_id=F("asset__org_id"),
|
||
backend=Value(self.backend, CharField())
|
||
)
|
||
return kwargs
|
||
|
||
def get_filter(self):
|
||
return dict(
|
||
systemuser__username_same_with_user=False,
|
||
)
|
||
|
||
def all(self):
|
||
kwargs = self.get_annotate()
|
||
filters = self.get_filter()
|
||
qs = self.model.objects.all().annotate(**kwargs)
|
||
if current_org.org_id() is not None:
|
||
filters['org_id'] = current_org.org_id()
|
||
qs = qs.filter(**filters)
|
||
qs = self.qs_to_values(qs)
|
||
return qs
|
||
|
||
|
||
class DynamicSystemUserBackend(SystemUserBackend):
|
||
backend = 'system_user_dynamic'
|
||
prefer = 'system_user'
|
||
union_id_length = 3
|
||
|
||
def get_annotate(self):
|
||
kwargs = super().get_annotate()
|
||
kwargs.update(dict(
|
||
username=F("systemuser__users__username"),
|
||
asset_username=Concat(
|
||
F("asset__id"), Value("_"),
|
||
F("systemuser__users__username"),
|
||
output_field=CharField()
|
||
),
|
||
union_id=Concat(
|
||
F("systemuser_id"), Value("_"), F("asset_id"),
|
||
Value("_"), F("systemuser__users__id"),
|
||
output_field=CharField()
|
||
),
|
||
users_count=Count('systemuser__users'),
|
||
))
|
||
return kwargs
|
||
|
||
def _perform_filter_union_id(self, union_id, union_id_cleaned):
|
||
system_user_id, asset_id, user_id = union_id_cleaned
|
||
self.queryset = self.queryset.filter(
|
||
asset_id=asset_id, systemuser_id=system_user_id,
|
||
union_id=union_id,
|
||
)
|
||
|
||
def _perform_delete_by_union_id(self, union_id_cleaned):
|
||
system_user_id, asset_id, user_id = union_id_cleaned
|
||
system_user = get_object_or_none(SystemUser, pk=system_user_id)
|
||
if not system_user:
|
||
return
|
||
system_user.users.remove(user_id)
|
||
if system_user.users.count() == 0:
|
||
system_user.assets.remove(asset_id)
|
||
|
||
def get_filter(self):
|
||
return dict(
|
||
users_count__gt=0,
|
||
systemuser__username_same_with_user=True
|
||
)
|
||
|
||
|
||
class AdminUserBackend(DBBackend):
|
||
model = Asset
|
||
backend = 'admin_user'
|
||
prefer = backend
|
||
base_score = 200
|
||
|
||
def _filter_prefer(self, prefer, prefer_id):
|
||
if prefer and prefer != self.backend:
|
||
self.queryset = self.queryset.none()
|
||
if prefer_id:
|
||
self.queryset = self.queryset.filter(admin_user__id=prefer_id)
|
||
|
||
def _filter_node(self, node):
|
||
if node:
|
||
self.queryset = self.queryset.filter(nodes__id=node.id)
|
||
|
||
def _perform_filter_union_id(self, union_id, union_id_cleaned):
|
||
admin_user_id, asset_id = union_id_cleaned
|
||
self.queryset = self.queryset.filter(
|
||
id=asset_id, admin_user_id=admin_user_id,
|
||
)
|
||
|
||
def _perform_delete_by_union_id(self, union_id_cleaned):
|
||
raise PermissionError("Could remove asset admin user")
|
||
|
||
def all(self):
|
||
qs = self.model.objects.all().annotate(
|
||
asset_id=F("id"),
|
||
username=F("admin_user__username"),
|
||
password=F("admin_user__password"),
|
||
private_key=F("admin_user__private_key"),
|
||
public_key=F("admin_user__public_key"),
|
||
score=Value(self.base_score, IntegerField()),
|
||
version=Value(0, IntegerField()),
|
||
date_updated=F("admin_user__date_updated"),
|
||
asset_username=Concat(F("id"), Value("_"), F("admin_user__username"), output_field=CharField()),
|
||
union_id=Concat(F("admin_user_id"), Value("_"), F("id"), output_field=CharField()),
|
||
backend=Value(self.backend, CharField()),
|
||
)
|
||
qs = self.qs_to_values(qs)
|
||
return qs
|
||
|
||
|
||
class AuthbookBackend(DBBackend):
|
||
model = AuthBook
|
||
backend = 'db'
|
||
prefer = backend
|
||
base_score = 400
|
||
|
||
def _filter_node(self, node):
|
||
if node:
|
||
self.queryset = self.queryset.filter(asset__nodes__id=node.id)
|
||
|
||
def _filter_prefer(self, prefer, prefer_id):
|
||
if not prefer or not prefer_id:
|
||
return
|
||
if prefer.lower() == "admin_user":
|
||
model = AdminUser
|
||
elif prefer.lower() == "system_user":
|
||
model = SystemUser
|
||
else:
|
||
self.queryset = self.queryset.none()
|
||
return
|
||
obj = get_object_or_none(model, pk=prefer_id)
|
||
if obj is None:
|
||
self.queryset = self.queryset.none()
|
||
return
|
||
username = obj.get_username()
|
||
if isinstance(username, str):
|
||
self.queryset = self.queryset.filter(username=username)
|
||
# dynamic system user return more username
|
||
else:
|
||
self.queryset = self.queryset.filter(username__in=username)
|
||
|
||
def _perform_filter_union_id(self, union_id, union_id_cleaned):
|
||
authbook_id, asset_id = union_id_cleaned
|
||
self.queryset = self.queryset.filter(
|
||
id=authbook_id, asset_id=asset_id,
|
||
)
|
||
|
||
def _perform_delete_by_union_id(self, union_id_cleaned):
|
||
authbook_id, asset_id = union_id_cleaned
|
||
authbook = get_object_or_none(AuthBook, pk=authbook_id)
|
||
if authbook.is_latest:
|
||
raise PermissionError("Latest version could be delete")
|
||
AuthBook.objects.filter(id=authbook_id).delete()
|
||
|
||
def all(self):
|
||
qs = self.model.objects.all().annotate(
|
||
hostname=F("asset__hostname"),
|
||
ip=F("asset__ip"),
|
||
score=F('version') + self.base_score,
|
||
asset_username=Concat(F("asset__id"), Value("_"), F("username"), output_field=CharField()),
|
||
union_id=Concat(F("id"), Value("_"), F("asset_id"), output_field=CharField()),
|
||
backend=Value(self.backend, CharField()),
|
||
)
|
||
qs = self.qs_to_values(qs)
|
||
return qs
|