mirror of https://github.com/jumpserver/jumpserver
feng
1 year ago
committed by
Bryan
18 changed files with 227 additions and 10 deletions
@ -1,4 +1,4 @@
|
||||
from .command import * |
||||
from .session import * |
||||
from .replay import * |
||||
from .session import * |
||||
from .sharing import * |
||||
|
@ -0,0 +1,51 @@
|
||||
from importlib import import_module |
||||
|
||||
from django.conf import settings |
||||
from django.utils import timezone |
||||
from rest_framework import status |
||||
from rest_framework import viewsets |
||||
from rest_framework.decorators import action |
||||
from rest_framework.response import Response |
||||
|
||||
from common.api import CommonApiMixin |
||||
from orgs.utils import current_org |
||||
from users import serializers |
||||
from ..models import UserSession |
||||
|
||||
|
||||
class UserSessionViewSet(CommonApiMixin, viewsets.ModelViewSet): |
||||
http_method_names = ('get', 'post', 'head', 'options', 'trace') |
||||
serializer_class = serializers.UserSessionSerializer |
||||
filterset_fields = ['id', 'ip', 'city', 'type'] |
||||
search_fields = ['id', 'ip', 'city'] |
||||
|
||||
rbac_perms = { |
||||
'offline': ['users.offline_usersession'] |
||||
} |
||||
|
||||
@property |
||||
def org_user_ids(self): |
||||
user_ids = current_org.get_members().values_list('id', flat=True) |
||||
return user_ids |
||||
|
||||
def get_queryset(self): |
||||
queryset = UserSession.objects.filter(date_expired__gt=timezone.now()) |
||||
if current_org.is_root(): |
||||
return queryset |
||||
user_ids = self.org_user_ids |
||||
queryset = queryset.filter(user_id__in=user_ids) |
||||
return queryset |
||||
|
||||
@action(['POST'], detail=False, url_path='offline') |
||||
def offline(self, request, *args, **kwargs): |
||||
ids = request.data.get('ids', []) |
||||
queryset = self.get_queryset().exclude(key=request.session.session_key).filter(id__in=ids) |
||||
if not queryset.exists(): |
||||
return Response(status=status.HTTP_200_OK) |
||||
|
||||
keys = queryset.values_list('key', flat=True) |
||||
session_store_cls = import_module(settings.SESSION_ENGINE).SessionStore |
||||
for key in keys: |
||||
session_store_cls(key).delete() |
||||
queryset.delete() |
||||
return Response(status=status.HTTP_200_OK) |
@ -0,0 +1,36 @@
|
||||
# Generated by Django 4.1.10 on 2023-09-14 07:23 |
||||
|
||||
from django.conf import settings |
||||
from django.db import migrations, models |
||||
import django.db.models.deletion |
||||
import uuid |
||||
|
||||
|
||||
class Migration(migrations.Migration): |
||||
|
||||
dependencies = [ |
||||
('users', '0043_remove_user_secret_key_preference'), |
||||
] |
||||
|
||||
operations = [ |
||||
migrations.CreateModel( |
||||
name='UserSession', |
||||
fields=[ |
||||
('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)), |
||||
('ip', models.GenericIPAddressField(verbose_name='Login IP')), |
||||
('key', models.CharField(max_length=128, verbose_name='Session key')), |
||||
('city', models.CharField(blank=True, max_length=254, null=True, verbose_name='Login city')), |
||||
('user_agent', models.CharField(blank=True, max_length=254, null=True, verbose_name='User agent')), |
||||
('type', models.CharField(choices=[('W', 'Web'), ('T', 'Terminal'), ('U', 'Unknown')], max_length=2, verbose_name='Login type')), |
||||
('backend', models.CharField(default='', max_length=32, verbose_name='Authentication backend')), |
||||
('date_created', models.DateTimeField(blank=True, null=True, verbose_name='Date created')), |
||||
('date_expired', models.DateTimeField(blank=True, db_index=True, null=True, verbose_name='Date expired')), |
||||
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='sessions', to=settings.AUTH_USER_MODEL, verbose_name='User')), |
||||
], |
||||
options={ |
||||
'verbose_name': 'User session', |
||||
'ordering': ['-date_created'], |
||||
'permissions': [('offline_usersession', 'Offline ussr session')], |
||||
}, |
||||
), |
||||
] |
@ -0,0 +1,40 @@
|
||||
import uuid |
||||
|
||||
from django.db import models |
||||
from django.utils import timezone |
||||
from django.utils.translation import gettext, gettext_lazy as _ |
||||
|
||||
from audits.const import LoginTypeChoices |
||||
|
||||
|
||||
class UserSession(models.Model): |
||||
id = models.UUIDField(default=uuid.uuid4, primary_key=True) |
||||
ip = models.GenericIPAddressField(verbose_name=_("Login IP")) |
||||
key = models.CharField(max_length=128, verbose_name=_("Session key")) |
||||
city = models.CharField(max_length=254, blank=True, null=True, verbose_name=_("Login city")) |
||||
user_agent = models.CharField(max_length=254, blank=True, null=True, verbose_name=_("User agent")) |
||||
type = models.CharField(choices=LoginTypeChoices.choices, max_length=2, verbose_name=_("Login type")) |
||||
backend = models.CharField(max_length=32, default="", verbose_name=_("Authentication backend")) |
||||
date_created = models.DateTimeField(null=True, blank=True, verbose_name=_('Date created')) |
||||
date_expired = models.DateTimeField(null=True, blank=True, verbose_name=_("Date expired"), db_index=True) |
||||
user = models.ForeignKey( |
||||
'users.User', verbose_name=_('User'), related_name='sessions', on_delete=models.CASCADE |
||||
) |
||||
|
||||
def __str__(self): |
||||
return '%s(%s)' % (self.user, self.ip) |
||||
|
||||
@property |
||||
def backend_display(self): |
||||
return gettext(self.backend) |
||||
|
||||
@classmethod |
||||
def clear_expired_sessions(cls): |
||||
cls.objects.filter(date_expired__lt=timezone.now()).delete() |
||||
|
||||
class Meta: |
||||
ordering = ['-date_created'] |
||||
verbose_name = _('User session') |
||||
permissions = [ |
||||
('offline_usersession', _('Offline ussr session')), |
||||
] |
@ -0,0 +1,32 @@
|
||||
from django.utils.translation import gettext_lazy as _ |
||||
from rest_framework import serializers |
||||
|
||||
from audits.const import LoginTypeChoices |
||||
from common.serializers.fields import LabeledChoiceField, ObjectRelatedField |
||||
from users.models import User |
||||
from ..models import UserSession |
||||
|
||||
|
||||
class UserSessionSerializer(serializers.ModelSerializer): |
||||
type = LabeledChoiceField(choices=LoginTypeChoices.choices, label=_("Type")) |
||||
user = ObjectRelatedField(required=False, queryset=User.objects, label=_('User')) |
||||
is_current_user_session = serializers.SerializerMethodField() |
||||
|
||||
class Meta: |
||||
model = UserSession |
||||
fields_mini = ['id'] |
||||
fields_small = fields_mini + [ |
||||
'type', 'ip', 'city', 'user_agent', 'user', 'is_current_user_session', |
||||
'backend', 'backend_display', 'date_created', 'date_expired' |
||||
] |
||||
fields = fields_small |
||||
extra_kwargs = { |
||||
"backend_display": {"label": _("Authentication backend")}, |
||||
} |
||||
|
||||
def get_is_current_user_session(self, obj): |
||||
request = self.context.get('request') |
||||
if not request: |
||||
return False |
||||
return request.session.session_key == obj.key |
||||
|
Loading…
Reference in new issue