Merge pull request #8076 from jumpserver/dev

v2.21.0-rc3
pull/8133/head
Jiangjie.Bai 2022-04-18 11:43:40 +08:00 committed by GitHub
commit afcbe60531
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 66 additions and 10 deletions

View File

@ -288,15 +288,14 @@ class Application(CommonModelMixin, OrgModelMixin, ApplicationTreeNodeMixin):
raise ValueError("Remote App not has asset attr") raise ValueError("Remote App not has asset attr")
def get_target_ip(self): def get_target_ip(self):
target_ip = ''
if self.category_remote_app: if self.category_remote_app:
asset = self.get_remote_app_asset() asset = self.get_remote_app_asset()
target_ip = asset.ip target_ip = asset.ip if asset else target_ip
elif self.category_cloud: elif self.category_cloud:
target_ip = self.attrs.get('cluster') target_ip = self.attrs.get('cluster')
elif self.category_db: elif self.category_db:
target_ip = self.attrs.get('host') target_ip = self.attrs.get('host')
else:
target_ip = ''
return target_ip return target_ip

View File

@ -484,7 +484,8 @@ class UserConnectionTokenViewSet(
tp = 'app' if application else 'asset' tp = 'app' if application else 'asset'
data = { data = {
"id": token, 'secret': secret, "id": token, 'secret': secret,
'type': tp, 'protocol': system_user.protocol 'type': tp, 'protocol': system_user.protocol,
'expire_time': self.get_token_ttl(token),
} }
return Response(data, status=201) return Response(data, status=201)

View File

@ -1,5 +1,7 @@
from django.shortcuts import redirect, reverse from django.shortcuts import redirect, reverse
from django.utils.deprecation import MiddlewareMixin
from django.http import HttpResponse from django.http import HttpResponse
from django.conf import settings
class MFAMiddleware: class MFAMiddleware:
@ -34,3 +36,15 @@ class MFAMiddleware:
url = reverse('authentication:login-mfa') + '?_=middleware' url = reverse('authentication:login-mfa') + '?_=middleware'
return redirect(url) return redirect(url)
class SessionCookieMiddleware(MiddlewareMixin):
@staticmethod
def process_response(request, response: HttpResponse):
key = settings.SESSION_COOKIE_NAME_PREFIX_KEY
value = settings.SESSION_COOKIE_NAME_PREFIX
if request.COOKIES.get(key) == value:
return response
response.set_cookie(key, value)
return response

View File

@ -1,5 +1,7 @@
import time import time
from channels_redis.core import RedisChannelLayer as _RedisChannelLayer
from common.utils.lock import DistributedLock from common.utils.lock import DistributedLock
from common.utils.connection import get_redis_client from common.utils.connection import get_redis_client
from common.utils import lazyproperty from common.utils import lazyproperty
@ -216,3 +218,29 @@ class CacheValueDesc:
def to_internal_value(self, value): def to_internal_value(self, value):
return self.field_type.field_type(value) return self.field_type.field_type(value)
class RedisChannelLayer(_RedisChannelLayer):
async def _brpop_with_clean(self, index, channel, timeout):
cleanup_script = """
local backed_up = redis.call('ZRANGE', ARGV[2], 0, -1, 'WITHSCORES')
for i = #backed_up, 1, -2 do
redis.call('ZADD', ARGV[1], backed_up[i], backed_up[i - 1])
end
redis.call('DEL', ARGV[2])
"""
backup_queue = self._backup_channel_name(channel)
async with self.connection(index) as connection:
# 部分云厂商的 Redis 此操作会报错(不支持,比如阿里云有限制)
try:
await connection.eval(cleanup_script, keys=[], args=[channel, backup_queue])
except:
pass
result = await connection.bzpopmin(channel, timeout=timeout)
if result is not None:
_, member, timestamp = result
await connection.zadd(backup_queue, float(timestamp), member)
else:
member = None
return member

View File

@ -157,6 +157,7 @@ class Config(dict):
'DEFAULT_EXPIRED_YEARS': 70, 'DEFAULT_EXPIRED_YEARS': 70,
'SESSION_COOKIE_DOMAIN': None, 'SESSION_COOKIE_DOMAIN': None,
'CSRF_COOKIE_DOMAIN': None, 'CSRF_COOKIE_DOMAIN': None,
'SESSION_COOKIE_NAME_PREFIX': None,
'SESSION_COOKIE_AGE': 3600 * 24, 'SESSION_COOKIE_AGE': 3600 * 24,
'SESSION_EXPIRE_AT_BROWSER_CLOSE': False, 'SESSION_EXPIRE_AT_BROWSER_CLOSE': False,
'LOGIN_URL': reverse_lazy('authentication:login'), 'LOGIN_URL': reverse_lazy('authentication:login'),

View File

@ -94,6 +94,7 @@ MIDDLEWARE = [
'authentication.backends.oidc.middleware.OIDCRefreshIDTokenMiddleware', 'authentication.backends.oidc.middleware.OIDCRefreshIDTokenMiddleware',
'authentication.backends.cas.middleware.CASMiddleware', 'authentication.backends.cas.middleware.CASMiddleware',
'authentication.middleware.MFAMiddleware', 'authentication.middleware.MFAMiddleware',
'authentication.middleware.SessionCookieMiddleware',
'simple_history.middleware.HistoryRequestMiddleware', 'simple_history.middleware.HistoryRequestMiddleware',
] ]
@ -128,6 +129,20 @@ LOGIN_URL = reverse_lazy('authentication:login')
SESSION_COOKIE_DOMAIN = CONFIG.SESSION_COOKIE_DOMAIN SESSION_COOKIE_DOMAIN = CONFIG.SESSION_COOKIE_DOMAIN
CSRF_COOKIE_DOMAIN = CONFIG.SESSION_COOKIE_DOMAIN CSRF_COOKIE_DOMAIN = CONFIG.SESSION_COOKIE_DOMAIN
# 设置 SESSION_COOKIE_NAME_PREFIX_KEY
# 解决 不同域 session csrf cookie 获取混乱问题
SESSION_COOKIE_NAME_PREFIX_KEY = 'SESSION_COOKIE_NAME_PREFIX'
SESSION_COOKIE_NAME_PREFIX = CONFIG.SESSION_COOKIE_NAME_PREFIX
if SESSION_COOKIE_NAME_PREFIX is not None:
pass
elif SESSION_COOKIE_DOMAIN is not None:
SESSION_COOKIE_NAME_PREFIX = SESSION_COOKIE_DOMAIN.split('.')[0]
else:
SESSION_COOKIE_NAME_PREFIX = 'jms_'
CSRF_COOKIE_NAME = '{}csrftoken'.format(SESSION_COOKIE_NAME_PREFIX)
SESSION_COOKIE_NAME = '{}sessionid'.format(SESSION_COOKIE_NAME_PREFIX)
SESSION_COOKIE_AGE = CONFIG.SESSION_COOKIE_AGE SESSION_COOKIE_AGE = CONFIG.SESSION_COOKIE_AGE
SESSION_EXPIRE_AT_BROWSER_CLOSE = True SESSION_EXPIRE_AT_BROWSER_CLOSE = True
# 自定义的配置SESSION_EXPIRE_AT_BROWSER_CLOSE 始终为 True, 下面这个来控制是否强制关闭后过期 cookie # 自定义的配置SESSION_EXPIRE_AT_BROWSER_CLOSE 始终为 True, 下面这个来控制是否强制关闭后过期 cookie

View File

@ -96,12 +96,12 @@ else:
CHANNEL_LAYERS = { CHANNEL_LAYERS = {
'default': { 'default': {
'BACKEND': 'channels_redis.core.RedisChannelLayer', 'BACKEND': 'common.cache.RedisChannelLayer',
'CONFIG': { 'CONFIG': {
"hosts": [{ "hosts": [{
'address': (CONFIG.REDIS_HOST, CONFIG.REDIS_PORT), 'address': (CONFIG.REDIS_HOST, CONFIG.REDIS_PORT),
'db': CONFIG.REDIS_DB_WS, 'db': CONFIG.REDIS_DB_WS,
'password': CONFIG.REDIS_PASSWORD, 'password': CONFIG.REDIS_PASSWORD or None,
'ssl': context 'ssl': context
}], }],
}, },

View File

@ -8,6 +8,7 @@ from assets.models import Asset
from orgs.utils import tmp_to_root_org from orgs.utils import tmp_to_root_org
from applications.models import Application from applications.models import Application
from terminal.models import Session from terminal.models import Session
from common.permissions import IsValidUser
from ..models import Endpoint, EndpointRule from ..models import Endpoint, EndpointRule
from .. import serializers from .. import serializers
@ -20,9 +21,6 @@ class EndpointViewSet(JMSBulkModelViewSet):
search_fields = filterset_fields search_fields = filterset_fields
serializer_class = serializers.EndpointSerializer serializer_class = serializers.EndpointSerializer
queryset = Endpoint.objects.all() queryset = Endpoint.objects.all()
rbac_perms = {
'smart': 'terminal.view_endpoint'
}
@staticmethod @staticmethod
def get_target_ip(request): def get_target_ip(request):
@ -57,7 +55,7 @@ class EndpointViewSet(JMSBulkModelViewSet):
target_ip = instance.get_target_ip() target_ip = instance.get_target_ip()
return target_ip return target_ip
@action(methods=['get'], detail=False, url_path='smart') @action(methods=['get'], detail=False, permission_classes=[IsValidUser], url_path='smart')
def smart(self, request, *args, **kwargs): def smart(self, request, *args, **kwargs):
protocol = request.GET.get('protocol') protocol = request.GET.get('protocol')
if not protocol: if not protocol: