Compare commits

..

5 Commits

Author SHA1 Message Date
fit2bot
c28cca7ca2 feat: Update v2.28.20 2023-09-26 14:51:33 +08:00
ibuler
29ab6bbee7 fix: pubkey auth require svc sign 2023-09-25 23:31:41 +08:00
ibuler
3352f78e01 perf: 修改验证码多次验证 2023-09-25 23:28:17 +08:00
吴小白
39affc4989 perf: 添加 patch 命令 2023-09-22 10:25:00 +08:00
ibuler
42337f0d00 perf: 修复随机 error 2023-09-19 18:18:51 +08:00
8 changed files with 68 additions and 29 deletions

View File

@@ -38,6 +38,7 @@ ARG TOOLS=" \
default-mysql-client \
iputils-ping \
locales \
patch \
procps \
redis-tools \
telnet \

View File

@@ -1,4 +0,0 @@
ARG VERSION
FROM registry.fit2cloud.com/jumpserver/xpack:${VERSION} as build-xpack
FROM jumpserver/core:${VERSION}
COPY --from=build-xpack /opt/xpack /opt/jumpserver/apps/xpack

2
GITSHA
View File

@@ -1 +1 @@
31d2bdd3bb5afd6e9bf33131373d5e657eac4138
29ab6bbee7f1ac2dee1826e42b65039f260f6fd0

View File

@@ -1,8 +1,9 @@
# -*- coding: utf-8 -*-
#
from django.contrib.auth import get_user_model
from django.conf import settings
from django.contrib.auth import get_user_model
from common.permissions import ServiceAccountSignaturePermission
from .base import JMSBaseAuthBackend
UserModel = get_user_model()
@@ -18,6 +19,10 @@ class PublicKeyAuthBackend(JMSBaseAuthBackend):
def authenticate(self, request, username=None, public_key=None, **kwargs):
if not public_key:
return None
permission = ServiceAccountSignaturePermission()
if not permission.has_permission(request, None):
return None
if username is None:
username = kwargs.get(UserModel.USERNAME_FIELD)
try:
@@ -26,7 +31,7 @@ class PublicKeyAuthBackend(JMSBaseAuthBackend):
return None
else:
if user.check_public_key(public_key) and \
self.user_can_authenticate(user):
self.user_can_authenticate(user):
return user
def get_user(self, user_id):

View File

@@ -81,3 +81,38 @@ class UserConfirmation(permissions.BasePermission):
min_level = ConfirmType.values.index(confirm_type) + 1
name = 'UserConfirmationLevel{}TTL{}'.format(min_level, ttl)
return type(name, (cls,), {'min_level': min_level, 'ttl': ttl, 'confirm_type': confirm_type})
class ServiceAccountSignaturePermission(permissions.BasePermission):
def has_permission(self, request, view):
from authentication.models import AccessKey
from common.utils.crypto import get_aes_crypto
signature = request.META.get('HTTP_X_JMS_SVC', '')
if not signature or not signature.startswith('Sign'):
return False
data = signature[4:].strip()
if not data or ':' not in data:
return False
ak_id, time_sign = data.split(':', 1)
if not ak_id or not time_sign:
return False
ak = AccessKey.objects.filter(id=ak_id).first()
if not ak or not ak.is_active:
return False
if not ak.user or not ak.user.is_active or not ak.user.is_service_account:
return False
aes = get_aes_crypto(str(ak.secret).replace('-', ''), mode='ECB')
try:
timestamp = aes.decrypt(time_sign)
if not timestamp or not timestamp.isdigit():
return False
timestamp = int(timestamp)
interval = abs(int(time.time()) - timestamp)
if interval > 30:
return False
return True
except Exception:
return False
def has_object_permission(self, request, view, obj):
return False

View File

@@ -1,10 +1,9 @@
# -*- coding: utf-8 -*-
#
import struct
import random
import socket
import string
import struct
string_punctuation = '!#$%&()*+,-.:;<=>?@[]^_~'
@@ -19,6 +18,7 @@ def random_ip():
def random_string(length: int, lower=True, upper=True, digit=True, special_char=False):
random.seed()
args_names = ['lower', 'upper', 'digit', 'special_char']
args_values = [lower, upper, digit, special_char]
args_string = [string.ascii_lowercase, string.ascii_uppercase, string.digits, string_punctuation]

View File

@@ -1,14 +1,13 @@
from django.core.cache import cache
from django.conf import settings
from django.core.mail import send_mail
from celery import shared_task
from django.conf import settings
from django.core.cache import cache
from django.core.mail import send_mail
from common.sdk.sms.exceptions import CodeError, CodeExpired, CodeSendTooFrequently
from common.sdk.sms.endpoint import SMS
from common.exceptions import JMSException
from common.utils.random import random_string
from common.sdk.sms.endpoint import SMS
from common.sdk.sms.exceptions import CodeError, CodeExpired, CodeSendTooFrequently
from common.utils import get_logger
from common.utils.random import random_string
logger = get_logger(__file__)
@@ -27,6 +26,7 @@ class SendAndVerifyCodeUtil(object):
self.timeout = timeout
self.backend = backend
self.key = key or self.KEY_TMPL.format(target)
self.verify_key = self.key + '_verify'
self.other_args = kwargs
def gen_and_send_async(self):
@@ -47,6 +47,11 @@ class SendAndVerifyCodeUtil(object):
raise
def verify(self, code):
times = cache.get(self.verify_key, 0)
if times >= 3:
self.__clear()
raise CodeExpired
cache.set(self.verify_key, times + 1, timeout=self.timeout)
right = cache.get(self.key)
if not right:
raise CodeExpired
@@ -59,6 +64,7 @@ class SendAndVerifyCodeUtil(object):
def __clear(self):
cache.delete(self.key)
cache.delete(self.verify_key)
def __ttl(self):
return cache.ttl(self.key)

View File

@@ -1,30 +1,27 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*-
#
import uuid
import base64
import string
import random
import datetime
import uuid
from typing import Callable
from django.db import models
from django.conf import settings
from django.utils import timezone
from django.core.cache import cache
from django.contrib.auth.models import AbstractUser
from django.contrib.auth.hashers import check_password
from django.utils.translation import ugettext_lazy as _
from django.contrib.auth.models import AbstractUser
from django.core.cache import cache
from django.db import models
from django.shortcuts import reverse
from django.utils import timezone
from django.utils.module_loading import import_string
from django.utils.translation import ugettext_lazy as _
from orgs.utils import current_org
from orgs.models import Organization
from rbac.const import Scope
from common.db import fields, models as jms_models
from common.utils import (
date_expired_default, get_logger, lazyproperty, random_string, bulk_create_with_signal
)
from orgs.utils import current_org
from rbac.const import Scope
from ..signals import post_user_change_password, post_user_leave_org, pre_user_leave_org
__all__ = ['User', 'UserPasswordHistory']
@@ -518,8 +515,7 @@ class TokenMixin:
return self.access_keys.first()
def generate_reset_token(self):
letter = string.ascii_letters + string.digits
token = ''.join([random.choice(letter) for _ in range(50)])
token = random_string(50)
self.set_cache(token)
return token