mirror of https://github.com/jumpserver/jumpserver
perf: password 等使用 rsa 加密传输 (#8188)
* perf: 修改 model fields 路径 * stash it * pref: 统一加密方式,密码字段采用 rsa 加密 * pref: 临时密码使用 rsa * perf: 去掉 debug msg * perf: 去掉 Debug * perf: 去掉 debug * perf: 抽出来 Co-authored-by: ibuler <ibuler@qq.com>pull/8199/head
parent
3f856e68f0
commit
031077c298
|
@ -1,6 +1,6 @@
|
|||
# Generated by Django 2.1.7 on 2019-05-20 11:04
|
||||
|
||||
import common.fields.model
|
||||
import common.db.fields
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import uuid
|
||||
|
@ -23,7 +23,7 @@ class Migration(migrations.Migration):
|
|||
('name', models.CharField(max_length=128, verbose_name='Name')),
|
||||
('type', models.CharField(choices=[('Browser', (('chrome', 'Chrome'),)), ('Database tools', (('mysql_workbench', 'MySQL Workbench'),)), ('Virtualization tools', (('vmware_client', 'vSphere Client'),)), ('custom', 'Custom')], default='chrome', max_length=128, verbose_name='App type')),
|
||||
('path', models.CharField(max_length=128, verbose_name='App path')),
|
||||
('params', common.fields.model.EncryptJsonDictTextField(blank=True, default={}, max_length=4096, null=True, verbose_name='Parameters')),
|
||||
('params', common.db.fields.EncryptJsonDictTextField(blank=True, default={}, max_length=4096, null=True, verbose_name='Parameters')),
|
||||
('created_by', models.CharField(blank=True, max_length=32, null=True, verbose_name='Created by')),
|
||||
('date_created', models.DateTimeField(auto_now_add=True, null=True, verbose_name='Date created')),
|
||||
('comment', models.TextField(blank=True, default='', max_length=128, verbose_name='Comment')),
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# Generated by Django 3.1.12 on 2021-08-26 09:07
|
||||
|
||||
import assets.models.base
|
||||
import common.fields.model
|
||||
import common.db.fields
|
||||
from django.conf import settings
|
||||
import django.core.validators
|
||||
from django.db import migrations, models
|
||||
|
@ -26,9 +26,9 @@ class Migration(migrations.Migration):
|
|||
('id', models.UUIDField(db_index=True, default=uuid.uuid4)),
|
||||
('name', models.CharField(max_length=128, verbose_name='Name')),
|
||||
('username', models.CharField(blank=True, db_index=True, max_length=128, validators=[django.core.validators.RegexValidator('^[0-9a-zA-Z_@\\-\\.]*$', 'Special char not allowed')], verbose_name='Username')),
|
||||
('password', common.fields.model.EncryptCharField(blank=True, max_length=256, null=True, verbose_name='Password')),
|
||||
('private_key', common.fields.model.EncryptTextField(blank=True, null=True, verbose_name='SSH private key')),
|
||||
('public_key', common.fields.model.EncryptTextField(blank=True, null=True, verbose_name='SSH public key')),
|
||||
('password', common.db.fields.EncryptCharField(blank=True, max_length=256, null=True, verbose_name='Password')),
|
||||
('private_key', common.db.fields.EncryptTextField(blank=True, null=True, verbose_name='SSH private key')),
|
||||
('public_key', common.db.fields.EncryptTextField(blank=True, null=True, verbose_name='SSH public key')),
|
||||
('comment', models.TextField(blank=True, verbose_name='Comment')),
|
||||
('date_created', models.DateTimeField(blank=True, editable=False, verbose_name='Date created')),
|
||||
('date_updated', models.DateTimeField(blank=True, editable=False, verbose_name='Date updated')),
|
||||
|
@ -56,9 +56,9 @@ class Migration(migrations.Migration):
|
|||
('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
|
||||
('name', models.CharField(max_length=128, verbose_name='Name')),
|
||||
('username', models.CharField(blank=True, db_index=True, max_length=128, validators=[django.core.validators.RegexValidator('^[0-9a-zA-Z_@\\-\\.]*$', 'Special char not allowed')], verbose_name='Username')),
|
||||
('password', common.fields.model.EncryptCharField(blank=True, max_length=256, null=True, verbose_name='Password')),
|
||||
('private_key', common.fields.model.EncryptTextField(blank=True, null=True, verbose_name='SSH private key')),
|
||||
('public_key', common.fields.model.EncryptTextField(blank=True, null=True, verbose_name='SSH public key')),
|
||||
('password', common.db.fields.EncryptCharField(blank=True, max_length=256, null=True, verbose_name='Password')),
|
||||
('private_key', common.db.fields.EncryptTextField(blank=True, null=True, verbose_name='SSH private key')),
|
||||
('public_key', common.db.fields.EncryptTextField(blank=True, null=True, verbose_name='SSH public key')),
|
||||
('comment', models.TextField(blank=True, verbose_name='Comment')),
|
||||
('date_created', models.DateTimeField(auto_now_add=True, verbose_name='Date created')),
|
||||
('date_updated', models.DateTimeField(auto_now=True, verbose_name='Date updated')),
|
||||
|
|
|
@ -4,7 +4,6 @@ from rest_framework.response import Response
|
|||
from rest_framework.decorators import action
|
||||
|
||||
from common.utils import get_logger, get_object_or_none
|
||||
from common.utils.crypto import get_aes_crypto
|
||||
from common.permissions import IsValidUser
|
||||
from common.mixins.api import SuggestionMixin
|
||||
from orgs.mixins.api import OrgBulkModelViewSet
|
||||
|
@ -102,27 +101,17 @@ class SystemUserTempAuthInfoApi(generics.CreateAPIView):
|
|||
permission_classes = (IsValidUser,)
|
||||
serializer_class = SystemUserTempAuthSerializer
|
||||
|
||||
def decrypt_data_if_need(self, data):
|
||||
csrf_token = self.request.META.get('CSRF_COOKIE')
|
||||
aes = get_aes_crypto(csrf_token, 'ECB')
|
||||
password = data.get('password', '')
|
||||
try:
|
||||
data['password'] = aes.decrypt(password)
|
||||
except:
|
||||
pass
|
||||
return data
|
||||
|
||||
def create(self, request, *args, **kwargs):
|
||||
serializer = super().get_serializer(data=request.data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
|
||||
pk = kwargs.get('pk')
|
||||
data = self.decrypt_data_if_need(serializer.validated_data)
|
||||
instance_id = data.get('instance_id')
|
||||
data = serializer.validated_data
|
||||
asset_or_app_id = data.get('instance_id')
|
||||
|
||||
with tmp_to_root_org():
|
||||
instance = get_object_or_404(SystemUser, pk=pk)
|
||||
instance.set_temp_auth(instance_id, self.request.user.id, data)
|
||||
instance.set_temp_auth(asset_or_app_id, self.request.user.id, data)
|
||||
return Response(serializer.data, status=201)
|
||||
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
# Generated by Django 2.1.7 on 2019-06-24 13:08
|
||||
|
||||
import assets.models.utils
|
||||
import common.fields.model
|
||||
import common.db.fields
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
|
@ -15,61 +15,61 @@ class Migration(migrations.Migration):
|
|||
migrations.AlterField(
|
||||
model_name='adminuser',
|
||||
name='_password',
|
||||
field=common.fields.model.EncryptCharField(blank=True, max_length=256, null=True, verbose_name='Password'),
|
||||
field=common.db.fields.EncryptCharField(blank=True, max_length=256, null=True, verbose_name='Password'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='adminuser',
|
||||
name='_private_key',
|
||||
field=common.fields.model.EncryptTextField(blank=True, null=True, validators=[assets.models.utils.private_key_validator], verbose_name='SSH private key'),
|
||||
field=common.db.fields.EncryptTextField(blank=True, null=True, validators=[assets.models.utils.private_key_validator], verbose_name='SSH private key'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='adminuser',
|
||||
name='_public_key',
|
||||
field=common.fields.model.EncryptTextField(blank=True, null=True, verbose_name='SSH public key'),
|
||||
field=common.db.fields.EncryptTextField(blank=True, null=True, verbose_name='SSH public key'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='authbook',
|
||||
name='_password',
|
||||
field=common.fields.model.EncryptCharField(blank=True, max_length=256, null=True, verbose_name='Password'),
|
||||
field=common.db.fields.EncryptCharField(blank=True, max_length=256, null=True, verbose_name='Password'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='authbook',
|
||||
name='_private_key',
|
||||
field=common.fields.model.EncryptTextField(blank=True, null=True, validators=[assets.models.utils.private_key_validator], verbose_name='SSH private key'),
|
||||
field=common.db.fields.EncryptTextField(blank=True, null=True, validators=[assets.models.utils.private_key_validator], verbose_name='SSH private key'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='authbook',
|
||||
name='_public_key',
|
||||
field=common.fields.model.EncryptTextField(blank=True, null=True, verbose_name='SSH public key'),
|
||||
field=common.db.fields.EncryptTextField(blank=True, null=True, verbose_name='SSH public key'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='gateway',
|
||||
name='_password',
|
||||
field=common.fields.model.EncryptCharField(blank=True, max_length=256, null=True, verbose_name='Password'),
|
||||
field=common.db.fields.EncryptCharField(blank=True, max_length=256, null=True, verbose_name='Password'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='gateway',
|
||||
name='_private_key',
|
||||
field=common.fields.model.EncryptTextField(blank=True, null=True, validators=[assets.models.utils.private_key_validator], verbose_name='SSH private key'),
|
||||
field=common.db.fields.EncryptTextField(blank=True, null=True, validators=[assets.models.utils.private_key_validator], verbose_name='SSH private key'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='gateway',
|
||||
name='_public_key',
|
||||
field=common.fields.model.EncryptTextField(blank=True, null=True, verbose_name='SSH public key'),
|
||||
field=common.db.fields.EncryptTextField(blank=True, null=True, verbose_name='SSH public key'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='systemuser',
|
||||
name='_password',
|
||||
field=common.fields.model.EncryptCharField(blank=True, max_length=256, null=True, verbose_name='Password'),
|
||||
field=common.db.fields.EncryptCharField(blank=True, max_length=256, null=True, verbose_name='Password'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='systemuser',
|
||||
name='_private_key',
|
||||
field=common.fields.model.EncryptTextField(blank=True, null=True, validators=[assets.models.utils.private_key_validator], verbose_name='SSH private key'),
|
||||
field=common.db.fields.EncryptTextField(blank=True, null=True, validators=[assets.models.utils.private_key_validator], verbose_name='SSH private key'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='systemuser',
|
||||
name='_public_key',
|
||||
field=common.fields.model.EncryptTextField(blank=True, null=True, verbose_name='SSH public key'),
|
||||
field=common.db.fields.EncryptTextField(blank=True, null=True, verbose_name='SSH public key'),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# Generated by Django 2.1.7 on 2019-07-11 12:18
|
||||
|
||||
import common.fields.model
|
||||
import common.db.fields
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
|
@ -14,21 +14,21 @@ class Migration(migrations.Migration):
|
|||
migrations.AlterField(
|
||||
model_name='adminuser',
|
||||
name='private_key',
|
||||
field=common.fields.model.EncryptTextField(blank=True, null=True, verbose_name='SSH private key'),
|
||||
field=common.db.fields.EncryptTextField(blank=True, null=True, verbose_name='SSH private key'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='authbook',
|
||||
name='private_key',
|
||||
field=common.fields.model.EncryptTextField(blank=True, null=True, verbose_name='SSH private key'),
|
||||
field=common.db.fields.EncryptTextField(blank=True, null=True, verbose_name='SSH private key'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='gateway',
|
||||
name='private_key',
|
||||
field=common.fields.model.EncryptTextField(blank=True, null=True, verbose_name='SSH private key'),
|
||||
field=common.db.fields.EncryptTextField(blank=True, null=True, verbose_name='SSH private key'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='systemuser',
|
||||
name='private_key',
|
||||
field=common.fields.model.EncryptTextField(blank=True, null=True, verbose_name='SSH private key'),
|
||||
field=common.db.fields.EncryptTextField(blank=True, null=True, verbose_name='SSH private key'),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# Generated by Django 2.2.7 on 2019-12-06 07:26
|
||||
|
||||
import common.fields.model
|
||||
import common.db.fields
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
|
@ -36,7 +36,7 @@ class Migration(migrations.Migration):
|
|||
('name', models.SlugField(allow_unicode=True, unique=True, verbose_name='Name')),
|
||||
('base', models.CharField(choices=[('Linux', 'Linux'), ('Unix', 'Unix'), ('MacOS', 'MacOS'), ('BSD', 'BSD'), ('Windows', 'Windows'), ('Other', 'Other')], default='Linux', max_length=16, verbose_name='Base')),
|
||||
('charset', models.CharField(choices=[('utf8', 'UTF-8'), ('gbk', 'GBK')], default='utf8', max_length=8, verbose_name='Charset')),
|
||||
('meta', common.fields.model.JsonDictTextField(blank=True, null=True, verbose_name='Meta')),
|
||||
('meta', common.db.fields.JsonDictTextField(blank=True, null=True, verbose_name='Meta')),
|
||||
('internal', models.BooleanField(default=False, verbose_name='Internal')),
|
||||
('comment', models.TextField(blank=True, null=True, verbose_name='Comment')),
|
||||
],
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# Generated by Django 3.1.6 on 2021-06-05 16:10
|
||||
|
||||
import common.fields.model
|
||||
import common.db.fields
|
||||
from django.conf import settings
|
||||
import django.core.validators
|
||||
from django.db import migrations, models
|
||||
|
@ -58,9 +58,9 @@ class Migration(migrations.Migration):
|
|||
('id', models.UUIDField(db_index=True, default=uuid.uuid4)),
|
||||
('name', models.CharField(max_length=128, verbose_name='Name')),
|
||||
('username', models.CharField(blank=True, db_index=True, max_length=128, validators=[django.core.validators.RegexValidator('^[0-9a-zA-Z_@\\-\\.]*$', 'Special char not allowed')], verbose_name='Username')),
|
||||
('password', common.fields.model.EncryptCharField(blank=True, max_length=256, null=True, verbose_name='Password')),
|
||||
('private_key', common.fields.model.EncryptTextField(blank=True, null=True, verbose_name='SSH private key')),
|
||||
('public_key', common.fields.model.EncryptTextField(blank=True, null=True, verbose_name='SSH public key')),
|
||||
('password', common.db.fields.EncryptCharField(blank=True, max_length=256, null=True, verbose_name='Password')),
|
||||
('private_key', common.db.fields.EncryptTextField(blank=True, null=True, verbose_name='SSH private key')),
|
||||
('public_key', common.db.fields.EncryptTextField(blank=True, null=True, verbose_name='SSH public key')),
|
||||
('comment', models.TextField(blank=True, verbose_name='Comment')),
|
||||
('date_created', models.DateTimeField(blank=True, editable=False, verbose_name='Date created')),
|
||||
('date_updated', models.DateTimeField(blank=True, editable=False, verbose_name='Date updated')),
|
||||
|
|
|
@ -11,7 +11,7 @@ from django.db import models
|
|||
from django.utils.translation import ugettext_lazy as _
|
||||
from rest_framework.exceptions import ValidationError
|
||||
|
||||
from common.fields.model import JsonDictTextField
|
||||
from common.db.fields import JsonDictTextField
|
||||
from common.utils import lazyproperty
|
||||
from orgs.mixins.models import OrgModelMixin, OrgManager
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ from common.utils import (
|
|||
)
|
||||
from common.utils.encode import ssh_pubkey_gen
|
||||
from common.validators import alphanumeric
|
||||
from common import fields
|
||||
from common.db import fields
|
||||
from orgs.mixins.models import OrgModelMixin
|
||||
|
||||
|
||||
|
|
|
@ -6,12 +6,13 @@ from django.utils.translation import ugettext_lazy as _
|
|||
from rest_framework import serializers
|
||||
|
||||
from common.utils import ssh_pubkey_gen, ssh_private_key_gen, validate_ssh_private_key
|
||||
from common.drf.fields import EncryptedField
|
||||
from assets.models import Type
|
||||
|
||||
|
||||
class AuthSerializer(serializers.ModelSerializer):
|
||||
password = serializers.CharField(required=False, allow_blank=True, allow_null=True, max_length=1024)
|
||||
private_key = serializers.CharField(required=False, allow_blank=True, allow_null=True, max_length=4096)
|
||||
password = EncryptedField(required=False, allow_blank=True, allow_null=True, max_length=1024)
|
||||
private_key = EncryptedField(required=False, allow_blank=True, allow_null=True, max_length=4096)
|
||||
|
||||
def gen_keys(self, private_key=None, password=None):
|
||||
if private_key is None:
|
||||
|
@ -31,6 +32,8 @@ class AuthSerializer(serializers.ModelSerializer):
|
|||
|
||||
|
||||
class AuthSerializerMixin(serializers.ModelSerializer):
|
||||
password = EncryptedField(required=False, allow_blank=True, allow_null=True, max_length=1024)
|
||||
private_key = EncryptedField(required=False, allow_blank=True, allow_null=True, max_length=4096)
|
||||
passphrase = serializers.CharField(
|
||||
allow_blank=True, allow_null=True, required=False, max_length=512,
|
||||
write_only=True, label=_('Key password')
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
# Generated by Django 3.1.14 on 2022-05-05 11:02
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('audits', '0013_auto_20211130_1037'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='operatelog',
|
||||
name='action',
|
||||
field=models.CharField(choices=[('create', 'Create'), ('view', 'View'), ('update', 'Update'), ('delete', 'Delete')], max_length=16, verbose_name='Action'),
|
||||
),
|
||||
]
|
|
@ -27,8 +27,10 @@ class TokenCreateApi(AuthMixin, CreateAPIView):
|
|||
def create(self, request, *args, **kwargs):
|
||||
self.create_session_if_need()
|
||||
# 如果认证没有过,检查账号密码
|
||||
serializer = self.get_serializer(data=request.data)
|
||||
serializer.is_valid(raise_exception=True)
|
||||
try:
|
||||
user = self.check_user_auth_if_need()
|
||||
user = self.get_user_or_auth(serializer.validated_data)
|
||||
self.check_user_mfa_if_need(user)
|
||||
self.check_user_login_confirm_if_need(user)
|
||||
self.send_auth_signal(success=True, user=user)
|
||||
|
|
|
@ -1,15 +1,25 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
|
||||
from django import forms
|
||||
from django.conf import settings
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from captcha.fields import CaptchaField, CaptchaTextInput
|
||||
|
||||
from common.utils import get_logger, rsa_decrypt_by_session_pkey
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
|
||||
class EncryptedField(forms.CharField):
|
||||
def to_python(self, value):
|
||||
value = super().to_python(value)
|
||||
return rsa_decrypt_by_session_pkey(value)
|
||||
|
||||
|
||||
class UserLoginForm(forms.Form):
|
||||
days_auto_login = int(settings.SESSION_COOKIE_AGE / 3600 / 24)
|
||||
disable_days_auto_login = settings.SESSION_EXPIRE_AT_BROWSER_CLOSE_FORCE or days_auto_login < 1
|
||||
disable_days_auto_login = settings.SESSION_EXPIRE_AT_BROWSER_CLOSE_FORCE \
|
||||
or days_auto_login < 1
|
||||
|
||||
username = forms.CharField(
|
||||
label=_('Username'), max_length=100,
|
||||
|
@ -18,7 +28,7 @@ class UserLoginForm(forms.Form):
|
|||
'autofocus': 'autofocus'
|
||||
})
|
||||
)
|
||||
password = forms.CharField(
|
||||
password = EncryptedField(
|
||||
label=_('Password'), widget=forms.PasswordInput,
|
||||
max_length=1024, strip=False
|
||||
)
|
||||
|
|
|
@ -1,8 +1,12 @@
|
|||
import base64
|
||||
|
||||
from django.shortcuts import redirect, reverse
|
||||
from django.utils.deprecation import MiddlewareMixin
|
||||
from django.http import HttpResponse
|
||||
from django.conf import settings
|
||||
|
||||
from common.utils import gen_key_pair
|
||||
|
||||
|
||||
class MFAMiddleware:
|
||||
"""
|
||||
|
@ -48,3 +52,28 @@ class SessionCookieMiddleware(MiddlewareMixin):
|
|||
return response
|
||||
response.set_cookie(key, value)
|
||||
return response
|
||||
|
||||
|
||||
class EncryptedMiddleware:
|
||||
def __init__(self, get_response):
|
||||
self.get_response = get_response
|
||||
|
||||
@staticmethod
|
||||
def check_key_pair(request, response):
|
||||
pub_key_name = settings.SESSION_RSA_PUBLIC_KEY_NAME
|
||||
public_key = request.session.get(pub_key_name)
|
||||
cookie_key = request.COOKIES.get(pub_key_name)
|
||||
if public_key and public_key == cookie_key:
|
||||
return
|
||||
|
||||
pri_key_name = settings.SESSION_RSA_PRIVATE_KEY_NAME
|
||||
private_key, public_key = gen_key_pair()
|
||||
public_key_decode = base64.b64encode(public_key.encode()).decode()
|
||||
request.session[pub_key_name] = public_key_decode
|
||||
request.session[pri_key_name] = private_key
|
||||
response.set_cookie(pub_key_name, public_key_decode)
|
||||
|
||||
def __call__(self, request):
|
||||
response = self.get_response(request)
|
||||
self.check_key_pair(request, response)
|
||||
return response
|
||||
|
|
|
@ -23,9 +23,7 @@ from acls.models import LoginACL
|
|||
from users.models import User
|
||||
from users.utils import LoginBlockUtil, MFABlockUtils, LoginIpBlockUtil
|
||||
from . import errors
|
||||
from .utils import rsa_decrypt, gen_key_pair
|
||||
from .signals import post_auth_success, post_auth_failed
|
||||
from .const import RSA_PRIVATE_KEY, RSA_PUBLIC_KEY
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
|
@ -91,46 +89,8 @@ def authenticate(request=None, **credentials):
|
|||
auth.authenticate = authenticate
|
||||
|
||||
|
||||
class PasswordEncryptionViewMixin:
|
||||
request = None
|
||||
|
||||
def get_decrypted_password(self, password=None, username=None):
|
||||
request = self.request
|
||||
if hasattr(request, 'data'):
|
||||
data = request.data
|
||||
else:
|
||||
data = request.POST
|
||||
|
||||
username = username or data.get('username')
|
||||
password = password or data.get('password')
|
||||
|
||||
password = self.decrypt_passwd(password)
|
||||
if not password:
|
||||
self.raise_password_decrypt_failed(username=username)
|
||||
return password
|
||||
|
||||
def raise_password_decrypt_failed(self, username):
|
||||
ip = self.get_request_ip()
|
||||
raise errors.CredentialError(
|
||||
error=errors.reason_password_decrypt_failed,
|
||||
username=username, ip=ip, request=self.request
|
||||
)
|
||||
|
||||
def decrypt_passwd(self, raw_passwd):
|
||||
# 获取解密密钥,对密码进行解密
|
||||
rsa_private_key = self.request.session.get(RSA_PRIVATE_KEY)
|
||||
if rsa_private_key is None:
|
||||
return raw_passwd
|
||||
|
||||
try:
|
||||
return rsa_decrypt(raw_passwd, rsa_private_key)
|
||||
except Exception as e:
|
||||
logger.error(e, exc_info=True)
|
||||
logger.error(
|
||||
f'Decrypt password failed: password[{raw_passwd}] '
|
||||
f'rsa_private_key[{rsa_private_key}]'
|
||||
)
|
||||
return None
|
||||
class CommonMixin:
|
||||
request: Request
|
||||
|
||||
def get_request_ip(self):
|
||||
ip = ''
|
||||
|
@ -139,26 +99,6 @@ class PasswordEncryptionViewMixin:
|
|||
ip = ip or get_request_ip(self.request)
|
||||
return ip
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
# 生成加解密密钥对,public_key传递给前端,private_key存入session中供解密使用
|
||||
rsa_public_key = self.request.session.get(RSA_PUBLIC_KEY)
|
||||
rsa_private_key = self.request.session.get(RSA_PRIVATE_KEY)
|
||||
if not all([rsa_private_key, rsa_public_key]):
|
||||
rsa_private_key, rsa_public_key = gen_key_pair()
|
||||
rsa_public_key = rsa_public_key.replace('\n', '\\n')
|
||||
self.request.session[RSA_PRIVATE_KEY] = rsa_private_key
|
||||
self.request.session[RSA_PUBLIC_KEY] = rsa_public_key
|
||||
|
||||
kwargs.update({
|
||||
'rsa_public_key': rsa_public_key,
|
||||
})
|
||||
return super().get_context_data(**kwargs)
|
||||
|
||||
|
||||
class CommonMixin(PasswordEncryptionViewMixin):
|
||||
request: Request
|
||||
get_request_ip: Callable
|
||||
|
||||
def raise_credential_error(self, error):
|
||||
raise self.partial_credential_error(error=error)
|
||||
|
||||
|
@ -193,20 +133,13 @@ class CommonMixin(PasswordEncryptionViewMixin):
|
|||
user.backend = self.request.session.get("auth_backend")
|
||||
return user
|
||||
|
||||
def get_auth_data(self, decrypt_passwd=False):
|
||||
def get_auth_data(self, data):
|
||||
request = self.request
|
||||
if hasattr(request, 'data'):
|
||||
data = request.data
|
||||
else:
|
||||
data = request.POST
|
||||
|
||||
items = ['username', 'password', 'challenge', 'public_key', 'auto_login']
|
||||
username, password, challenge, public_key, auto_login = bulk_get(data, items, default='')
|
||||
ip = self.get_request_ip()
|
||||
self._set_partial_credential_error(username=username, ip=ip, request=request)
|
||||
|
||||
if decrypt_passwd:
|
||||
password = self.get_decrypted_password()
|
||||
password = password + challenge.strip()
|
||||
return username, password, public_key, ip, auto_login
|
||||
|
||||
|
@ -482,10 +415,10 @@ class AuthMixin(CommonMixin, AuthPreCheckMixin, AuthACLMixin, MFAMixin, AuthPost
|
|||
need = cache.get(self.key_prefix_captcha.format(ip))
|
||||
return need
|
||||
|
||||
def check_user_auth(self, decrypt_passwd=False):
|
||||
def check_user_auth(self, valid_data=None):
|
||||
# pre check
|
||||
self.check_is_block()
|
||||
username, password, public_key, ip, auto_login = self.get_auth_data(decrypt_passwd)
|
||||
username, password, public_key, ip, auto_login = self.get_auth_data(valid_data)
|
||||
self._check_only_allow_exists_user_auth(username)
|
||||
|
||||
# check auth
|
||||
|
@ -537,11 +470,12 @@ class AuthMixin(CommonMixin, AuthPreCheckMixin, AuthACLMixin, MFAMixin, AuthPost
|
|||
self.mark_password_ok(user, False)
|
||||
return user
|
||||
|
||||
def check_user_auth_if_need(self, decrypt_passwd=False):
|
||||
def get_user_or_auth(self, valid_data):
|
||||
request = self.request
|
||||
if not request.session.get('auth_password'):
|
||||
return self.check_user_auth(decrypt_passwd=decrypt_passwd)
|
||||
return self.get_user_from_session()
|
||||
if request.session.get('auth_password'):
|
||||
return self.get_user_from_session()
|
||||
else:
|
||||
return self.check_user_auth(valid_data)
|
||||
|
||||
def clear_auth_mark(self):
|
||||
keys = ['auth_password', 'user_id', 'auth_confirm', 'auth_ticket_id']
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
#
|
||||
from rest_framework import serializers
|
||||
|
||||
from common.drf.fields import EncryptedField
|
||||
|
||||
|
||||
__all__ = [
|
||||
'OtpVerifySerializer', 'MFAChallengeSerializer', 'MFASelectTypeSerializer',
|
||||
|
@ -10,7 +12,7 @@ __all__ = [
|
|||
|
||||
|
||||
class PasswordVerifySerializer(serializers.Serializer):
|
||||
password = serializers.CharField()
|
||||
password = EncryptedField()
|
||||
|
||||
|
||||
class MFASelectTypeSerializer(serializers.Serializer):
|
||||
|
|
|
@ -161,6 +161,7 @@
|
|||
<span style="font-size: 21px;font-weight:400;color: #151515;letter-spacing: 0;">{{ JMS_TITLE }}</span>
|
||||
</div>
|
||||
<div class="contact-form col-md-10 col-md-offset-1">
|
||||
|
||||
<form id="login-form" action="" method="post" role="form" novalidate="novalidate">
|
||||
{% csrf_token %}
|
||||
<div style="line-height: 17px;margin-bottom: 20px;color: #999999;">
|
||||
|
@ -241,20 +242,10 @@
|
|||
{% include '_foot_js.html' %}
|
||||
<script type="text/javascript" src="/static/js/plugins/jsencrypt/jsencrypt.min.js"></script>
|
||||
<script>
|
||||
function encryptLoginPassword(password, rsaPublicKey) {
|
||||
if (!password) {
|
||||
return ''
|
||||
}
|
||||
var jsencrypt = new JSEncrypt(); //加密对象
|
||||
jsencrypt.setPublicKey(rsaPublicKey); // 设置密钥
|
||||
return jsencrypt.encrypt(password); //加密
|
||||
}
|
||||
|
||||
function doLogin() {
|
||||
//公钥加密
|
||||
var rsaPublicKey = "{{ rsa_public_key }}"
|
||||
var password = $('#password').val(); //明文密码
|
||||
var passwordEncrypted = encryptLoginPassword(password, rsaPublicKey)
|
||||
var passwordEncrypted = encryptPassword(password)
|
||||
$('#password-hidden').val(passwordEncrypted); //返回给密码输入input
|
||||
$('#login-form').submit(); //post提交
|
||||
}
|
||||
|
|
|
@ -1,62 +1,22 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
import base64
|
||||
from Cryptodome.PublicKey import RSA
|
||||
from Cryptodome.Cipher import PKCS1_v1_5
|
||||
from Cryptodome import Random
|
||||
|
||||
from django.conf import settings
|
||||
from .notifications import DifferentCityLoginMessage
|
||||
from audits.models import UserLoginLog
|
||||
from audits.const import DEFAULT_CITY
|
||||
|
||||
from common.utils import validate_ip, get_ip_city, get_request_ip
|
||||
from common.utils import get_logger
|
||||
from audits.models import UserLoginLog
|
||||
from audits.const import DEFAULT_CITY
|
||||
from .notifications import DifferentCityLoginMessage
|
||||
|
||||
logger = get_logger(__file__)
|
||||
|
||||
|
||||
def gen_key_pair():
|
||||
""" 生成加密key
|
||||
用于登录页面提交用户名/密码时,对密码进行加密(前端)/解密(后端)
|
||||
"""
|
||||
random_generator = Random.new().read
|
||||
rsa = RSA.generate(1024, random_generator)
|
||||
rsa_private_key = rsa.exportKey().decode()
|
||||
rsa_public_key = rsa.publickey().exportKey().decode()
|
||||
return rsa_private_key, rsa_public_key
|
||||
|
||||
|
||||
def rsa_encrypt(message, rsa_public_key):
|
||||
""" 加密登录密码 """
|
||||
key = RSA.importKey(rsa_public_key)
|
||||
cipher = PKCS1_v1_5.new(key)
|
||||
cipher_text = base64.b64encode(cipher.encrypt(message.encode())).decode()
|
||||
return cipher_text
|
||||
|
||||
|
||||
def rsa_decrypt(cipher_text, rsa_private_key=None):
|
||||
""" 解密登录密码 """
|
||||
if rsa_private_key is None:
|
||||
# rsa_private_key 为 None,可以能是API请求认证,不需要解密
|
||||
return cipher_text
|
||||
|
||||
key = RSA.importKey(rsa_private_key)
|
||||
cipher = PKCS1_v1_5.new(key)
|
||||
cipher_decoded = base64.b64decode(cipher_text.encode())
|
||||
# Todo: 弄明白为何要以下这么写,https://xbuba.com/questions/57035263
|
||||
if len(cipher_decoded) == 127:
|
||||
hex_fixed = '00' + cipher_decoded.hex()
|
||||
cipher_decoded = base64.b16decode(hex_fixed.upper())
|
||||
message = cipher.decrypt(cipher_decoded, b'error').decode()
|
||||
return message
|
||||
|
||||
|
||||
def check_different_city_login_if_need(user, request):
|
||||
if not settings.SECURITY_CHECK_DIFFERENT_CITY_LOGIN:
|
||||
return
|
||||
|
||||
ip = get_request_ip(request) or '0.0.0.0'
|
||||
|
||||
if not (ip and validate_ip(ip)):
|
||||
city = DEFAULT_CITY
|
||||
else:
|
||||
|
|
|
@ -96,7 +96,7 @@ class UserLoginView(mixins.AuthMixin, FormView):
|
|||
self.request.session.delete_test_cookie()
|
||||
|
||||
try:
|
||||
self.check_user_auth(decrypt_passwd=True)
|
||||
self.check_user_auth(form.cleaned_data)
|
||||
except errors.AuthFailedError as e:
|
||||
form.add_error(None, e.msg)
|
||||
self.set_login_failed_mark()
|
||||
|
@ -219,7 +219,7 @@ class UserLoginGuardView(mixins.AuthMixin, RedirectView):
|
|||
|
||||
def get_redirect_url(self, *args, **kwargs):
|
||||
try:
|
||||
user = self.check_user_auth_if_need()
|
||||
user = self.get_user_from_session()
|
||||
self.check_user_mfa_if_need(user)
|
||||
self.check_user_login_confirm_if_need(user)
|
||||
except (errors.CredentialError, errors.SessionEmptyError) as e:
|
||||
|
|
|
@ -5,7 +5,7 @@ from django.db import models
|
|||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.utils.encoding import force_text
|
||||
from django.core.validators import MinValueValidator, MaxValueValidator
|
||||
from ..utils import signer, crypto
|
||||
from common.utils import signer, crypto
|
||||
|
||||
|
||||
__all__ = [
|
|
@ -3,9 +3,10 @@
|
|||
|
||||
from rest_framework import serializers
|
||||
|
||||
from common.utils import rsa_decrypt_by_session_pkey
|
||||
|
||||
__all__ = [
|
||||
'ReadableHiddenField',
|
||||
'ReadableHiddenField', 'EncryptedField'
|
||||
]
|
||||
|
||||
|
||||
|
@ -23,3 +24,9 @@ class ReadableHiddenField(serializers.HiddenField):
|
|||
if hasattr(value, 'id'):
|
||||
return getattr(value, 'id')
|
||||
return value
|
||||
|
||||
|
||||
class EncryptedField(serializers.CharField):
|
||||
def to_internal_value(self, value):
|
||||
value = super().to_internal_value(value)
|
||||
return rsa_decrypt_by_session_pkey(value)
|
||||
|
|
|
@ -1,4 +0,0 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
|
||||
from .model import *
|
|
@ -1,7 +1,10 @@
|
|||
import base64
|
||||
from Cryptodome.Cipher import AES
|
||||
import logging
|
||||
from Cryptodome.Cipher import AES, PKCS1_v1_5
|
||||
from Cryptodome.Util.Padding import pad
|
||||
from Cryptodome.Random import get_random_bytes
|
||||
from Cryptodome.PublicKey import RSA
|
||||
from Cryptodome import Random
|
||||
from gmssl.sm4 import CryptSM4, SM4_ENCRYPT, SM4_DECRYPT
|
||||
|
||||
from django.conf import settings
|
||||
|
@ -193,4 +196,55 @@ class Crypto:
|
|||
continue
|
||||
|
||||
|
||||
def gen_key_pair(length=1024):
|
||||
""" 生成加密key
|
||||
用于登录页面提交用户名/密码时,对密码进行加密(前端)/解密(后端)
|
||||
"""
|
||||
random_generator = Random.new().read
|
||||
rsa = RSA.generate(length, random_generator)
|
||||
rsa_private_key = rsa.exportKey().decode()
|
||||
rsa_public_key = rsa.publickey().exportKey().decode()
|
||||
return rsa_private_key, rsa_public_key
|
||||
|
||||
|
||||
def rsa_encrypt(message, rsa_public_key):
|
||||
""" 加密登录密码 """
|
||||
key = RSA.importKey(rsa_public_key)
|
||||
cipher = PKCS1_v1_5.new(key)
|
||||
cipher_text = base64.b64encode(cipher.encrypt(message.encode())).decode()
|
||||
return cipher_text
|
||||
|
||||
|
||||
def rsa_decrypt(cipher_text, rsa_private_key=None):
|
||||
""" 解密登录密码 """
|
||||
if rsa_private_key is None:
|
||||
# rsa_private_key 为 None,可以能是API请求认证,不需要解密
|
||||
return cipher_text
|
||||
|
||||
key = RSA.importKey(rsa_private_key)
|
||||
cipher = PKCS1_v1_5.new(key)
|
||||
cipher_decoded = base64.b64decode(cipher_text.encode())
|
||||
# Todo: 弄明白为何要以下这么写,https://xbuba.com/questions/57035263
|
||||
if len(cipher_decoded) == 127:
|
||||
hex_fixed = '00' + cipher_decoded.hex()
|
||||
cipher_decoded = base64.b16decode(hex_fixed.upper())
|
||||
message = cipher.decrypt(cipher_decoded, b'error').decode()
|
||||
return message
|
||||
|
||||
|
||||
def rsa_decrypt_by_session_pkey(value):
|
||||
from jumpserver.utils import current_request
|
||||
private_key_name = settings.SESSION_RSA_PRIVATE_KEY_NAME
|
||||
private_key = current_request.session.get(private_key_name)
|
||||
|
||||
if not private_key or not value:
|
||||
return value
|
||||
|
||||
try:
|
||||
value= rsa_decrypt(value, private_key)
|
||||
except Exception as e:
|
||||
logging.error('Decrypt field error: {}'.format(e))
|
||||
return value
|
||||
|
||||
|
||||
crypto = Crypto()
|
||||
|
|
|
@ -95,6 +95,7 @@ MIDDLEWARE = [
|
|||
'authentication.backends.cas.middleware.CASMiddleware',
|
||||
'authentication.middleware.MFAMiddleware',
|
||||
'authentication.middleware.SessionCookieMiddleware',
|
||||
'authentication.middleware.EncryptedMiddleware',
|
||||
'simple_history.middleware.HistoryRequestMiddleware',
|
||||
]
|
||||
|
||||
|
|
|
@ -169,3 +169,6 @@ ANNOUNCEMENT = CONFIG.ANNOUNCEMENT
|
|||
# help
|
||||
HELP_DOCUMENT_URL = CONFIG.HELP_DOCUMENT_URL
|
||||
HELP_SUPPORT_URL = CONFIG.HELP_SUPPORT_URL
|
||||
|
||||
SESSION_RSA_PRIVATE_KEY_NAME = 'jms_private_key'
|
||||
SESSION_RSA_PUBLIC_KEY_NAME = 'jms_public_key'
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# Generated by Django 2.2.7 on 2019-12-17 09:58
|
||||
|
||||
import common.fields.model
|
||||
import common.db.fields
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
|
@ -18,17 +18,17 @@ class Migration(migrations.Migration):
|
|||
migrations.AlterField(
|
||||
model_name='adhoc',
|
||||
name='_become',
|
||||
field=common.fields.model.EncryptJsonDictCharField(blank=True, null=True, default='', max_length=1024, verbose_name='Become'),
|
||||
field=common.db.fields.EncryptJsonDictCharField(blank=True, null=True, default='', max_length=1024, verbose_name='Become'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='adhoc',
|
||||
name='_options',
|
||||
field=common.fields.model.JsonDictCharField(default='', max_length=1024, verbose_name='Options'),
|
||||
field=common.db.fields.JsonDictCharField(default='', max_length=1024, verbose_name='Options'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='adhoc',
|
||||
name='_tasks',
|
||||
field=common.fields.model.JsonListTextField(verbose_name='Tasks'),
|
||||
field=common.db.fields.JsonListTextField(verbose_name='Tasks'),
|
||||
),
|
||||
migrations.RenameField(
|
||||
model_name='adhoc',
|
||||
|
@ -48,12 +48,12 @@ class Migration(migrations.Migration):
|
|||
migrations.AlterField(
|
||||
model_name='adhocrunhistory',
|
||||
name='_result',
|
||||
field=common.fields.model.JsonDictTextField(blank=True, null=True, verbose_name='Adhoc raw result'),
|
||||
field=common.db.fields.JsonDictTextField(blank=True, null=True, verbose_name='Adhoc raw result'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='adhocrunhistory',
|
||||
name='_summary',
|
||||
field=common.fields.model.JsonDictTextField(blank=True, null=True, verbose_name='Adhoc result summary'),
|
||||
field=common.db.fields.JsonDictTextField(blank=True, null=True, verbose_name='Adhoc result summary'),
|
||||
),
|
||||
migrations.RenameField(
|
||||
model_name='adhocrunhistory',
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# Generated by Django 2.2.7 on 2020-01-06 07:34
|
||||
|
||||
import common.fields.model
|
||||
import common.db.fields
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
|
@ -14,6 +14,6 @@ class Migration(migrations.Migration):
|
|||
migrations.AlterField(
|
||||
model_name='adhoc',
|
||||
name='become',
|
||||
field=common.fields.model.EncryptJsonDictCharField(blank=True, default='', max_length=1024, null=True, verbose_name='Become'),
|
||||
field=common.db.fields.EncryptJsonDictCharField(blank=True, default='', max_length=1024, null=True, verbose_name='Become'),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -9,11 +9,11 @@ from celery import current_task
|
|||
from django.db import models
|
||||
from django.conf import settings
|
||||
from django.utils import timezone
|
||||
from django.utils.translation import ugettext_lazy as _, gettext
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
|
||||
from common.utils import get_logger, lazyproperty
|
||||
from common.utils.translate import translate_value
|
||||
from common.fields.model import (
|
||||
from common.db.fields import (
|
||||
JsonListTextField, JsonDictCharField, EncryptJsonDictCharField,
|
||||
JsonDictTextField,
|
||||
)
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
from django.utils.translation import ugettext_lazy as _
|
||||
from rest_framework import serializers
|
||||
|
||||
from common.drf.fields import EncryptedField
|
||||
|
||||
__all__ = [
|
||||
'LDAPTestConfigSerializer', 'LDAPUserSerializer', 'LDAPTestLoginSerializer',
|
||||
'LDAPSettingSerializer',
|
||||
|
@ -20,7 +22,7 @@ class LDAPTestConfigSerializer(serializers.Serializer):
|
|||
|
||||
class LDAPTestLoginSerializer(serializers.Serializer):
|
||||
username = serializers.CharField(max_length=1024, required=True)
|
||||
password = serializers.CharField(max_length=2014, required=True)
|
||||
password = EncryptedField(max_length=2014, required=True)
|
||||
|
||||
|
||||
class LDAPUserSerializer(serializers.Serializer):
|
||||
|
|
|
@ -1501,3 +1501,19 @@ function getStatusIcon(status, mapping, title) {
|
|||
}
|
||||
return icon;
|
||||
}
|
||||
|
||||
function encryptPassword(password) {
|
||||
if (!password) {
|
||||
return ''
|
||||
}
|
||||
var rsaPublicKeyText = getCookie('jms_public_key')
|
||||
.replaceAll('"', '')
|
||||
var rsaPublicKey = atob(rsaPublicKeyText)
|
||||
var jsencrypt = new JSEncrypt(); //加密对象
|
||||
jsencrypt.setPublicKey(rsaPublicKey); // 设置密钥
|
||||
var value = jsencrypt.encrypt(password); //加密
|
||||
return value
|
||||
}
|
||||
|
||||
|
||||
window.encryptPassword = encryptPassword
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# Generated by Django 2.2.5 on 2019-11-22 10:07
|
||||
|
||||
import common.fields.model
|
||||
import common.db.fields
|
||||
from django.db import migrations, models
|
||||
import uuid
|
||||
|
||||
|
@ -21,7 +21,7 @@ class Migration(migrations.Migration):
|
|||
('date_updated', models.DateTimeField(auto_now=True, verbose_name='Date updated')),
|
||||
('name', models.CharField(max_length=32, unique=True, verbose_name='Name')),
|
||||
('type', models.CharField(choices=[('null', 'Null'), ('server', 'Server'), ('es', 'Elasticsearch')], default='server', max_length=16, verbose_name='Type')),
|
||||
('meta', common.fields.model.EncryptJsonDictTextField(default={})),
|
||||
('meta', common.db.fields.EncryptJsonDictTextField(default={})),
|
||||
('comment', models.TextField(blank=True, default='', max_length=128, verbose_name='Comment')),
|
||||
],
|
||||
options={
|
||||
|
@ -37,7 +37,7 @@ class Migration(migrations.Migration):
|
|||
('date_updated', models.DateTimeField(auto_now=True, verbose_name='Date updated')),
|
||||
('name', models.CharField(max_length=32, unique=True, verbose_name='Name')),
|
||||
('type', models.CharField(choices=[('null', 'Null'), ('server', 'Server'), ('s3', 'S3'), ('ceph', 'Ceph'), ('swift', 'Swift'), ('oss', 'OSS'), ('azure', 'Azure')], default='server', max_length=16, verbose_name='Type')),
|
||||
('meta', common.fields.model.EncryptJsonDictTextField(default={})),
|
||||
('meta', common.db.fields.EncryptJsonDictTextField(default={})),
|
||||
('comment', models.TextField(blank=True, default='', max_length=128, verbose_name='Comment')),
|
||||
],
|
||||
options={
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
# Generated by Django 3.1.14 on 2022-04-12 07:39
|
||||
|
||||
import copy
|
||||
import common.fields.model
|
||||
import common.db.fields
|
||||
import django.core.validators
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
@ -83,13 +82,13 @@ class Migration(migrations.Migration):
|
|||
('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
|
||||
('name', models.CharField(max_length=128, unique=True, verbose_name='Name')),
|
||||
('host', models.CharField(max_length=256, verbose_name='Host', blank=True)),
|
||||
('https_port', common.fields.model.PortField(default=443, validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(65535)], verbose_name='HTTPS Port')),
|
||||
('http_port', common.fields.model.PortField(default=80, validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(65535)], verbose_name='HTTP Port')),
|
||||
('ssh_port', common.fields.model.PortField(default=2222, validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(65535)], verbose_name='SSH Port')),
|
||||
('rdp_port', common.fields.model.PortField(default=3389, validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(65535)], verbose_name='RDP Port')),
|
||||
('mysql_port', common.fields.model.PortField(default=33060, validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(65535)], verbose_name='MySQL Port')),
|
||||
('mariadb_port', common.fields.model.PortField(default=33061, validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(65535)], verbose_name='MariaDB Port')),
|
||||
('postgresql_port', common.fields.model.PortField(default=54320, validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(65535)], verbose_name='PostgreSQL Port')),
|
||||
('https_port', common.db.fields.PortField(default=443, validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(65535)], verbose_name='HTTPS Port')),
|
||||
('http_port', common.db.fields.PortField(default=80, validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(65535)], verbose_name='HTTP Port')),
|
||||
('ssh_port', common.db.fields.PortField(default=2222, validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(65535)], verbose_name='SSH Port')),
|
||||
('rdp_port', common.db.fields.PortField(default=3389, validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(65535)], verbose_name='RDP Port')),
|
||||
('mysql_port', common.db.fields.PortField(default=33060, validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(65535)], verbose_name='MySQL Port')),
|
||||
('mariadb_port', common.db.fields.PortField(default=33061, validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(65535)], verbose_name='MariaDB Port')),
|
||||
('postgresql_port', common.db.fields.PortField(default=54320, validators=[django.core.validators.MinValueValidator(0), django.core.validators.MaxValueValidator(65535)], verbose_name='PostgreSQL Port')),
|
||||
('comment', models.TextField(blank=True, default='', verbose_name='Comment')),
|
||||
],
|
||||
options={
|
||||
|
|
|
@ -2,7 +2,7 @@ from django.db import models
|
|||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.core.validators import MinValueValidator, MaxValueValidator
|
||||
from common.db.models import JMSModel
|
||||
from common.fields.model import PortField
|
||||
from common.db.fields import PortField
|
||||
from common.utils.ip import contains_ip
|
||||
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ from django.utils.translation import ugettext_lazy as _
|
|||
from django.conf import settings
|
||||
from common.mixins import CommonModelMixin
|
||||
from common.utils import get_logger
|
||||
from common.fields.model import EncryptJsonDictTextField
|
||||
from common.db.fields import EncryptJsonDictTextField
|
||||
from terminal.backends import TYPE_ENGINE_MAPPING
|
||||
from .terminal import Terminal
|
||||
from .command import Command
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# Generated by Django 2.2.5 on 2019-11-15 06:57
|
||||
|
||||
import common.fields.model
|
||||
import common.db.fields
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
@ -26,7 +26,7 @@ class Migration(migrations.Migration):
|
|||
('user_display', models.CharField(max_length=128, verbose_name='User display name')),
|
||||
('title', models.CharField(max_length=256, verbose_name='Title')),
|
||||
('body', models.TextField(verbose_name='Body')),
|
||||
('meta', common.fields.model.JsonDictTextField(default='{}', verbose_name='Meta')),
|
||||
('meta', common.db.fields.JsonDictTextField(default='{}', verbose_name='Meta')),
|
||||
('assignee_display', models.CharField(blank=True, max_length=128, null=True, verbose_name='Assignee display name')),
|
||||
('assignees_display', models.CharField(blank=True, max_length=128, verbose_name='Assignees display name')),
|
||||
('type', models.CharField(choices=[('general', 'General'), ('login_confirm', 'Login confirm')], default='general', max_length=16, verbose_name='Type')),
|
||||
|
|
|
@ -5,6 +5,7 @@ from django.utils.translation import gettext_lazy as _
|
|||
from captcha.fields import CaptchaField
|
||||
|
||||
from common.utils import validate_ssh_public_key
|
||||
from authentication.forms import EncryptedField
|
||||
from ..models import User
|
||||
|
||||
|
||||
|
@ -17,7 +18,7 @@ __all__ = [
|
|||
|
||||
|
||||
class UserCheckPasswordForm(forms.Form):
|
||||
password = forms.CharField(
|
||||
password = EncryptedField(
|
||||
label=_('Password'), widget=forms.PasswordInput,
|
||||
max_length=1024, strip=False
|
||||
)
|
||||
|
@ -77,12 +78,12 @@ UserFirstLoginFinishForm.verbose_name = _("Finish")
|
|||
|
||||
|
||||
class UserTokenResetPasswordForm(forms.Form):
|
||||
new_password = forms.CharField(
|
||||
new_password = EncryptedField(
|
||||
min_length=5, max_length=128,
|
||||
widget=forms.PasswordInput,
|
||||
label=_("New password")
|
||||
)
|
||||
confirm_password = forms.CharField(
|
||||
confirm_password = EncryptedField(
|
||||
min_length=5, max_length=128,
|
||||
widget=forms.PasswordInput,
|
||||
label=_("Confirm password")
|
||||
|
@ -103,7 +104,7 @@ class UserForgotPasswordForm(forms.Form):
|
|||
|
||||
|
||||
class UserPasswordForm(UserTokenResetPasswordForm):
|
||||
old_password = forms.CharField(
|
||||
old_password = EncryptedField(
|
||||
max_length=128, widget=forms.PasswordInput,
|
||||
label=_("Old password")
|
||||
)
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# Generated by Django 2.1.7 on 2019-06-25 03:04
|
||||
|
||||
import common.fields.model
|
||||
import common.db.fields
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
|
@ -14,17 +14,17 @@ class Migration(migrations.Migration):
|
|||
migrations.AlterField(
|
||||
model_name='user',
|
||||
name='_otp_secret_key',
|
||||
field=common.fields.model.EncryptCharField(blank=True, max_length=128, null=True),
|
||||
field=common.db.fields.EncryptCharField(blank=True, max_length=128, null=True),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='user',
|
||||
name='_private_key',
|
||||
field=common.fields.model.EncryptTextField(blank=True, null=True, verbose_name='Private key'),
|
||||
field=common.db.fields.EncryptTextField(blank=True, null=True, verbose_name='Private key'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='user',
|
||||
name='_public_key',
|
||||
field=common.fields.model.EncryptTextField(blank=True, null=True, verbose_name='Public key'),
|
||||
field=common.db.fields.EncryptTextField(blank=True, null=True, verbose_name='Public key'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='user',
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# Generated by Django 3.1.13 on 2021-12-07 08:23
|
||||
|
||||
import common.fields.model
|
||||
import common.db.fields
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
|
@ -14,6 +14,6 @@ class Migration(migrations.Migration):
|
|||
migrations.AddField(
|
||||
model_name='user',
|
||||
name='secret_key',
|
||||
field=common.fields.model.EncryptCharField(blank=True, max_length=256, null=True, verbose_name='Secret key'),
|
||||
field=common.db.fields.EncryptCharField(blank=True, max_length=256, null=True, verbose_name='Secret key'),
|
||||
),
|
||||
]
|
||||
|
|
|
@ -20,7 +20,7 @@ from django.shortcuts import reverse
|
|||
from orgs.utils import current_org
|
||||
from orgs.models import Organization
|
||||
from rbac.const import Scope
|
||||
from common import fields
|
||||
from common.db import fields
|
||||
from common.utils import (
|
||||
date_expired_default, get_logger, lazyproperty, random_string, bulk_create_with_signal
|
||||
)
|
||||
|
|
|
@ -3,6 +3,7 @@ from django.utils.translation import ugettext_lazy as _
|
|||
from rest_framework import serializers
|
||||
|
||||
from common.utils import validate_ssh_public_key
|
||||
from common.drf.fields import EncryptedField
|
||||
from ..models import User
|
||||
|
||||
from .user import UserSerializer
|
||||
|
@ -16,9 +17,9 @@ class UserOrgSerializer(serializers.Serializer):
|
|||
|
||||
|
||||
class UserUpdatePasswordSerializer(serializers.ModelSerializer):
|
||||
old_password = serializers.CharField(required=True, max_length=128, write_only=True)
|
||||
new_password = serializers.CharField(required=True, max_length=128, write_only=True)
|
||||
new_password_again = serializers.CharField(required=True, max_length=128, write_only=True)
|
||||
old_password = EncryptedField(required=True, max_length=128, write_only=True)
|
||||
new_password = EncryptedField(required=True, max_length=128, write_only=True)
|
||||
new_password_again = EncryptedField(required=True, max_length=128, write_only=True)
|
||||
|
||||
class Meta:
|
||||
model = User
|
||||
|
@ -41,11 +42,13 @@ class UserUpdatePasswordSerializer(serializers.ModelSerializer):
|
|||
raise serializers.ValidationError(msg)
|
||||
return value
|
||||
|
||||
def validate_new_password_again(self, value):
|
||||
if value != self.initial_data.get('new_password', ''):
|
||||
def validate(self, values):
|
||||
new_password = values.get('new_password', '')
|
||||
new_password_again = values.get('new_password_again', '')
|
||||
if new_password != new_password_again:
|
||||
msg = _('The newly set password is inconsistent')
|
||||
raise serializers.ValidationError(msg)
|
||||
return value
|
||||
raise serializers.ValidationError({'new_password_again': msg})
|
||||
return values
|
||||
|
||||
def update(self, instance, validated_data):
|
||||
new_password = self.validated_data.get('new_password')
|
||||
|
|
|
@ -75,6 +75,19 @@ $(document).ready(function () {
|
|||
var password = idPassword.val();
|
||||
checkPasswordRules(password, minLength);
|
||||
})
|
||||
|
||||
$("form").submit(function(){
|
||||
// Let's find the input to check
|
||||
var ids = ['id_new_password', 'id_confirm_password']
|
||||
for (id of ids) {
|
||||
var passwordRef = $('#' + id)
|
||||
var value = passwordRef.val()
|
||||
if (value) {
|
||||
value = encryptPassword(value)
|
||||
passwordRef.val(value)
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
</script>
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
|
|
@ -15,10 +15,23 @@
|
|||
{% endif %}
|
||||
{% csrf_token %}
|
||||
<div class="form-input form-group">
|
||||
<input type="password" class="form-control" name="{{ form.password.html_name }}" placeholder="{% trans 'Password' %}" required="">
|
||||
<input type="password" id="password" class="form-control" name="{{ form.password.html_name }}" placeholder="{% trans 'Password' %}" required="">
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary">{% trans 'Confirm' %}</button>
|
||||
</form>
|
||||
<script>
|
||||
$("form").submit(function(){
|
||||
// Let's find the input to check
|
||||
var passwordRef = $('#password')
|
||||
var value = passwordRef.val()
|
||||
if (value) {
|
||||
// Value is falsey (i.e. null), lets set a new one
|
||||
value = encryptPassword(value)
|
||||
passwordRef.val(value)
|
||||
}
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ from django.shortcuts import redirect
|
|||
from django.utils.translation import ugettext as _
|
||||
from django.views.generic.edit import FormView
|
||||
|
||||
from authentication.mixins import PasswordEncryptionViewMixin, AuthMixin
|
||||
from authentication.mixins import AuthMixin
|
||||
from authentication import errors
|
||||
|
||||
from common.utils import get_logger
|
||||
|
@ -31,7 +31,7 @@ class UserVerifyPasswordView(AuthMixin, FormView):
|
|||
return redirect('authentication:login')
|
||||
|
||||
try:
|
||||
password = self.get_decrypted_password(username=user.username)
|
||||
password = form.cleaned_data['password']
|
||||
except errors.AuthFailedError as e:
|
||||
form.add_error("password", _(f"Password invalid") + f'({e.msg})')
|
||||
return self.form_invalid(form)
|
||||
|
|
Loading…
Reference in New Issue