Unverified Commit 16cc4a0f authored by 老广's avatar 老广 Committed by GitHub

[Update] 修改settings配置 (#2067)

* [Update] 修改settings配置

* [Update] 修改settings

* [Update] 修改密码校验规则前后端逻辑

* [Update] 修改用户config机制

* [Update] 修改配置

* [Update] 修改config example增加翻译
parent 5931c5a0
...@@ -168,13 +168,16 @@ class DjangoSettingsAPI(APIView): ...@@ -168,13 +168,16 @@ class DjangoSettingsAPI(APIView):
return Response("Not in debug mode") return Response("Not in debug mode")
data = {} data = {}
for k, v in settings.__dict__.items(): for i in [settings, getattr(settings, '_wrapped')]:
if k and k.isupper(): if not i:
try: continue
json.dumps(v) for k, v in i.__dict__.items():
data[k] = v if k and k.isupper():
except (json.JSONDecodeError, TypeError): try:
data[k] = str(v) json.dumps(v)
data[k] = v
except (json.JSONDecodeError, TypeError):
data[k] = str(v)
return Response(data) return Response(data)
......
...@@ -5,7 +5,7 @@ from django import forms ...@@ -5,7 +5,7 @@ from django import forms
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.db import transaction from django.db import transaction
from .models import Setting, common_settings from .models import Setting, settings
from .fields import FormDictField, FormEncryptCharField, \ from .fields import FormDictField, FormEncryptCharField, \
FormEncryptMixin FormEncryptMixin
...@@ -14,7 +14,7 @@ class BaseForm(forms.Form): ...@@ -14,7 +14,7 @@ class BaseForm(forms.Form):
def __init__(self, *args, **kwargs): def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)
for name, field in self.fields.items(): for name, field in self.fields.items():
value = getattr(common_settings, name) value = getattr(settings, name, None)
# django_value = getattr(settings, name) if hasattr(settings, name) else None # django_value = getattr(settings, name) if hasattr(settings, name) else None
if value is None: # and django_value is None: if value is None: # and django_value is None:
...@@ -43,7 +43,7 @@ class BaseForm(forms.Form): ...@@ -43,7 +43,7 @@ class BaseForm(forms.Form):
field = self.fields[name] field = self.fields[name]
if isinstance(field.widget, forms.PasswordInput) and not value: if isinstance(field.widget, forms.PasswordInput) and not value:
continue continue
if value == getattr(common_settings, name): if value == getattr(settings, name):
continue continue
encrypted = True if isinstance(field, FormEncryptMixin) else False encrypted = True if isinstance(field, FormEncryptMixin) else False
...@@ -69,7 +69,6 @@ class BasicSettingForm(BaseForm): ...@@ -69,7 +69,6 @@ class BasicSettingForm(BaseForm):
) )
EMAIL_SUBJECT_PREFIX = forms.CharField( EMAIL_SUBJECT_PREFIX = forms.CharField(
max_length=1024, label=_("Email Subject Prefix"), max_length=1024, label=_("Email Subject Prefix"),
initial="[Jumpserver] "
) )
...@@ -97,21 +96,21 @@ class EmailSettingForm(BaseForm): ...@@ -97,21 +96,21 @@ class EmailSettingForm(BaseForm):
class LDAPSettingForm(BaseForm): class LDAPSettingForm(BaseForm):
AUTH_LDAP_SERVER_URI = forms.CharField( AUTH_LDAP_SERVER_URI = forms.CharField(
label=_("LDAP server"), initial='ldap://localhost:389' label=_("LDAP server"),
) )
AUTH_LDAP_BIND_DN = forms.CharField( AUTH_LDAP_BIND_DN = forms.CharField(
label=_("Bind DN"), initial='cn=admin,dc=jumpserver,dc=org' label=_("Bind DN"),
) )
AUTH_LDAP_BIND_PASSWORD = FormEncryptCharField( AUTH_LDAP_BIND_PASSWORD = FormEncryptCharField(
label=_("Password"), initial='', label=_("Password"),
widget=forms.PasswordInput, required=False widget=forms.PasswordInput, required=False
) )
AUTH_LDAP_SEARCH_OU = forms.CharField( AUTH_LDAP_SEARCH_OU = forms.CharField(
label=_("User OU"), initial='ou=tech,dc=jumpserver,dc=org', label=_("User OU"),
help_text=_("Use | split User OUs") help_text=_("Use | split User OUs")
) )
AUTH_LDAP_SEARCH_FILTER = forms.CharField( AUTH_LDAP_SEARCH_FILTER = forms.CharField(
label=_("User search filter"), initial='(cn=%(user)s)', label=_("User search filter"),
help_text=_("Choice may be (cn|uid|sAMAccountName)=%(user)s)") help_text=_("Choice may be (cn|uid|sAMAccountName)=%(user)s)")
) )
AUTH_LDAP_USER_ATTR_MAP = FormDictField( AUTH_LDAP_USER_ATTR_MAP = FormDictField(
...@@ -119,14 +118,14 @@ class LDAPSettingForm(BaseForm): ...@@ -119,14 +118,14 @@ class LDAPSettingForm(BaseForm):
help_text=_( help_text=_(
"User attr map present how to map LDAP user attr to jumpserver, " "User attr map present how to map LDAP user attr to jumpserver, "
"username,name,email is jumpserver attr" "username,name,email is jumpserver attr"
) ),
) )
# AUTH_LDAP_GROUP_SEARCH_OU = CONFIG.AUTH_LDAP_GROUP_SEARCH_OU # AUTH_LDAP_GROUP_SEARCH_OU = CONFIG.AUTH_LDAP_GROUP_SEARCH_OU
# AUTH_LDAP_GROUP_SEARCH_FILTER = CONFIG.AUTH_LDAP_GROUP_SEARCH_FILTER # AUTH_LDAP_GROUP_SEARCH_FILTER = CONFIG.AUTH_LDAP_GROUP_SEARCH_FILTER
AUTH_LDAP_START_TLS = forms.BooleanField( AUTH_LDAP_START_TLS = forms.BooleanField(
label=_("Use SSL"), initial=False, required=False label=_("Use SSL"), required=False
) )
AUTH_LDAP = forms.BooleanField(label=_("Enable LDAP auth"), initial=False, required=False) AUTH_LDAP = forms.BooleanField(label=_("Enable LDAP auth"), required=False)
class TerminalSettingForm(BaseForm): class TerminalSettingForm(BaseForm):
......
...@@ -2,6 +2,7 @@ import json ...@@ -2,6 +2,7 @@ import json
import ldap import ldap
from django.db import models from django.db import models
from django.core.cache import cache
from django.db.utils import ProgrammingError, OperationalError from django.db.utils import ProgrammingError, OperationalError
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from django.conf import settings from django.conf import settings
...@@ -40,15 +41,7 @@ class Setting(models.Model): ...@@ -40,15 +41,7 @@ class Setting(models.Model):
return self.name return self.name
def __getattr__(self, item): def __getattr__(self, item):
default = getattr(settings, item, None) return cache.get(item)
try:
instances = self.__class__.objects.filter(name=item)
except Exception:
return default
if len(instances) == 1:
return instances[0].cleaned_value
else:
return default
@property @property
def cleaned_value(self): def cleaned_value(self):
...@@ -106,22 +99,15 @@ class Setting(models.Model): ...@@ -106,22 +99,15 @@ class Setting(models.Model):
def refresh_setting(self): def refresh_setting(self):
setattr(settings, self.name, self.cleaned_value) setattr(settings, self.name, self.cleaned_value)
if self.name == "AUTH_LDAP": if self.name == "AUTH_LDAP":
if self.cleaned_value and settings.AUTH_LDAP_BACKEND not in settings.AUTHENTICATION_BACKENDS: if self.cleaned_value and settings.AUTH_LDAP_BACKEND not in settings.AUTHENTICATION_BACKENDS:
settings.AUTHENTICATION_BACKENDS.insert(0, settings.AUTH_LDAP_BACKEND) old_setting = settings.AUTHENTICATION_BACKENDS
old_setting.insert(0, settings.AUTH_LDAP_BACKEND)
settings.AUTHENTICATION_BACKENDS = old_setting
elif not self.cleaned_value and settings.AUTH_LDAP_BACKEND in settings.AUTHENTICATION_BACKENDS: elif not self.cleaned_value and settings.AUTH_LDAP_BACKEND in settings.AUTHENTICATION_BACKENDS:
settings.AUTHENTICATION_BACKENDS.remove(settings.AUTH_LDAP_BACKEND) old_setting = settings.AUTHENTICATION_BACKENDS
old_setting.remove(settings.AUTH_LDAP_BACKEND)
if self.name == "AUTH_LDAP_SEARCH_FILTER": settings.AUTHENTICATION_BACKENDS = old_setting
settings.AUTH_LDAP_USER_SEARCH_UNION = [
LDAPSearch(USER_SEARCH, ldap.SCOPE_SUBTREE, settings.AUTH_LDAP_SEARCH_FILTER)
for USER_SEARCH in str(settings.AUTH_LDAP_SEARCH_OU).split("|")
]
settings.AUTH_LDAP_USER_SEARCH = LDAPSearchUnion(*settings.AUTH_LDAP_USER_SEARCH_UNION)
class Meta: class Meta:
db_table = "settings" db_table = "settings"
common_settings = Setting()
...@@ -4,4 +4,3 @@ ...@@ -4,4 +4,3 @@
from django.dispatch import Signal from django.dispatch import Signal
django_ready = Signal() django_ready = Signal()
ldap_auth_enable = Signal(providing_args=["enabled"])
...@@ -2,13 +2,14 @@ ...@@ -2,13 +2,14 @@
# #
from django.dispatch import receiver from django.dispatch import receiver
from django.db.models.signals import post_save, pre_save from django.db.models.signals import post_save, pre_save
from django.conf import settings from django.conf import LazySettings, empty
from django.db.utils import ProgrammingError, OperationalError from django.db.utils import ProgrammingError, OperationalError
from django.core.cache import cache
from jumpserver.utils import current_request from jumpserver.utils import current_request
from .models import Setting from .models import Setting
from .utils import get_logger from .utils import get_logger
from .signals import django_ready, ldap_auth_enable from .signals import django_ready
logger = get_logger(__file__) logger = get_logger(__file__)
...@@ -25,25 +26,43 @@ def refresh_settings_on_changed(sender, instance=None, **kwargs): ...@@ -25,25 +26,43 @@ def refresh_settings_on_changed(sender, instance=None, **kwargs):
def refresh_all_settings_on_django_ready(sender, **kwargs): def refresh_all_settings_on_django_ready(sender, **kwargs):
logger.debug("Receive django ready signal") logger.debug("Receive django ready signal")
logger.debug(" - fresh all settings") logger.debug(" - fresh all settings")
CACHE_KEY_PREFIX = '_SETTING_'
def monkey_patch_getattr(self, name):
key = CACHE_KEY_PREFIX + name
cached = cache.get(key)
if cached is not None:
return cached
if self._wrapped is empty:
self._setup(name)
val = getattr(self._wrapped, name)
# self.__dict__[name] = val # Never set it
return val
def monkey_patch_setattr(self, name, value):
key = CACHE_KEY_PREFIX + name
cache.set(key, value, None)
if name == '_wrapped':
self.__dict__.clear()
else:
self.__dict__.pop(name, None)
super(LazySettings, self).__setattr__(name, value)
def monkey_patch_delattr(self, name):
super(LazySettings, self).__delattr__(name)
self.__dict__.pop(name, None)
key = CACHE_KEY_PREFIX + name
cache.delete(key)
try: try:
LazySettings.__getattr__ = monkey_patch_getattr
LazySettings.__setattr__ = monkey_patch_setattr
LazySettings.__delattr__ = monkey_patch_delattr
Setting.refresh_all_settings() Setting.refresh_all_settings()
except (ProgrammingError, OperationalError): except (ProgrammingError, OperationalError):
pass pass
@receiver(ldap_auth_enable, dispatch_uid="my_unique_identifier")
def ldap_auth_on_changed(sender, enabled=True, **kwargs):
if enabled:
logger.debug("Enable LDAP auth")
if settings.AUTH_LDAP_BACKEND not in settings.AUTHENTICATION_BACKENDS:
settings.AUTHENTICATION_BACKENDS.insert(0, settings.AUTH_LDAP_BACKEND)
else:
logger.debug("Disable LDAP auth")
if settings.AUTH_LDAP_BACKEND in settings.AUTHENTICATION_BACKENDS:
settings.AUTHENTICATION_BACKENDS.remove(settings.AUTH_LDAP_BACKEND)
@receiver(pre_save, dispatch_uid="my_unique_identifier") @receiver(pre_save, dispatch_uid="my_unique_identifier")
def on_create_set_created_by(sender, instance=None, **kwargs): def on_create_set_created_by(sender, instance=None, **kwargs):
if hasattr(instance, 'created_by') and not instance.created_by: if hasattr(instance, 'created_by') and not instance.created_by:
......
...@@ -3,7 +3,6 @@ from django.conf import settings ...@@ -3,7 +3,6 @@ from django.conf import settings
from celery import shared_task from celery import shared_task
from .utils import get_logger from .utils import get_logger
from .models import Setting from .models import Setting
from common.models import common_settings
logger = get_logger(__file__) logger = get_logger(__file__)
...@@ -23,13 +22,9 @@ def send_mail_async(*args, **kwargs): ...@@ -23,13 +22,9 @@ def send_mail_async(*args, **kwargs):
Example: Example:
send_mail_sync.delay(subject, message, recipient_list, fail_silently=False, html_message=None) send_mail_sync.delay(subject, message, recipient_list, fail_silently=False, html_message=None)
""" """
configs = Setting.objects.filter(name__startswith='EMAIL')
for config in configs:
setattr(settings, config.name, config.cleaned_value)
if len(args) == 3: if len(args) == 3:
args = list(args) args = list(args)
args[0] = common_settings.EMAIL_SUBJECT_PREFIX + args[0] args[0] = settings.EMAIL_SUBJECT_PREFIX + args[0]
args.insert(2, settings.EMAIL_HOST_USER) args.insert(2, settings.EMAIL_HOST_USER)
args = tuple(args) args = tuple(args)
......
...@@ -13,5 +13,5 @@ urlpatterns = [ ...@@ -13,5 +13,5 @@ urlpatterns = [
path('terminal/replay-storage/delete/', api.ReplayStorageDeleteAPI.as_view(), name='replay-storage-delete'), path('terminal/replay-storage/delete/', api.ReplayStorageDeleteAPI.as_view(), name='replay-storage-delete'),
path('terminal/command-storage/create/', api.CommandStorageCreateAPI.as_view(), name='command-storage-create'), path('terminal/command-storage/create/', api.CommandStorageCreateAPI.as_view(), name='command-storage-create'),
path('terminal/command-storage/delete/', api.CommandStorageDeleteAPI.as_view(), name='command-storage-delete'), path('terminal/command-storage/delete/', api.CommandStorageDeleteAPI.as_view(), name='command-storage-delete'),
# path('django-settings/', api.DjangoSettingsAPI.as_view(), name='django-settings'), path('django-settings/', api.DjangoSettingsAPI.as_view(), name='django-settings'),
] ]
...@@ -37,8 +37,7 @@ def reverse(view_name, urlconf=None, args=None, kwargs=None, ...@@ -37,8 +37,7 @@ def reverse(view_name, urlconf=None, args=None, kwargs=None,
kwargs=kwargs, current_app=current_app) kwargs=kwargs, current_app=current_app)
if external: if external:
from common.models import common_settings site_url = settings.SITE_URL
site_url = common_settings.SITE_URL
url = site_url.strip('/') + url url = site_url.strip('/') + url
return url return url
...@@ -390,17 +389,15 @@ def get_request_ip(request): ...@@ -390,17 +389,15 @@ def get_request_ip(request):
def get_command_storage_setting(): def get_command_storage_setting():
from common.models import common_settings default = settings.DEFAULT_TERMINAL_COMMAND_STORAGE
default = settings.TERMINAL_COMMAND_STORAGE value = settings.TERMINAL_COMMAND_STORAGE
value = common_settings.TERMINAL_COMMAND_STORAGE
value.update(default) value.update(default)
return value return value
def get_replay_storage_setting(): def get_replay_storage_setting():
from common.models import common_settings default = settings.DEFAULT_TERMINAL_REPLAY_STORAGE
default = settings.TERMINAL_REPLAY_STORAGE value = settings.TERMINAL_REPLAY_STORAGE
value = common_settings.TERMINAL_REPLAY_STORAGE
value.update(default) value.update(default)
return value return value
......
...@@ -6,7 +6,6 @@ from django.utils.translation import ugettext as _ ...@@ -6,7 +6,6 @@ from django.utils.translation import ugettext as _
from .forms import EmailSettingForm, LDAPSettingForm, BasicSettingForm, \ from .forms import EmailSettingForm, LDAPSettingForm, BasicSettingForm, \
TerminalSettingForm, SecuritySettingForm TerminalSettingForm, SecuritySettingForm
from common.permissions import SuperUserRequiredMixin from common.permissions import SuperUserRequiredMixin
from .signals import ldap_auth_enable
from . import utils from . import utils
...@@ -79,8 +78,6 @@ class LDAPSettingView(SuperUserRequiredMixin, TemplateView): ...@@ -79,8 +78,6 @@ class LDAPSettingView(SuperUserRequiredMixin, TemplateView):
form = self.form_class(request.POST) form = self.form_class(request.POST)
if form.is_valid(): if form.is_valid():
form.save() form.save()
if "AUTH_LDAP" in form.cleaned_data:
ldap_auth_enable.send(sender=self.__class__, enabled=form.cleaned_data["AUTH_LDAP"])
msg = _("Update setting successfully, please restart program") msg = _("Update setting successfully, please restart program")
messages.success(request, msg) messages.success(request, msg)
return redirect('settings:ldap-setting') return redirect('settings:ldap-setting')
......
This diff is collapsed.
...@@ -17,24 +17,12 @@ import ldap ...@@ -17,24 +17,12 @@ import ldap
from django_auth_ldap.config import LDAPSearch, LDAPSearchUnion from django_auth_ldap.config import LDAPSearch, LDAPSearchUnion
from django.urls import reverse_lazy from django.urls import reverse_lazy
from .conf import load_user_config
# Build paths inside the project like this: os.path.join(BASE_DIR, ...) # Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
PROJECT_DIR = os.path.dirname(BASE_DIR) PROJECT_DIR = os.path.dirname(BASE_DIR)
CONFIG = load_user_config()
sys.path.append(PROJECT_DIR)
# Import project config setting
try:
from config import config as CONFIG
except ImportError:
msg = """
Error: No config file found.
You can run `cp config_example.py config.py`, and edit it.
"""
raise ImportError(msg)
# CONFIG = type('_', (), {'__getattr__': lambda arg1, arg2: None})()
# Quick-start development settings - unsuitable for production # Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/1.10/howto/deployment/checklist/ # See https://docs.djangoproject.com/en/1.10/howto/deployment/checklist/
...@@ -43,15 +31,15 @@ except ImportError: ...@@ -43,15 +31,15 @@ except ImportError:
SECRET_KEY = CONFIG.SECRET_KEY SECRET_KEY = CONFIG.SECRET_KEY
# SECURITY WARNING: don't run with debug turned on in production! # SECURITY WARNING: don't run with debug turned on in production!
DEBUG = CONFIG.DEBUG or False DEBUG = CONFIG.DEBUG
# Absolute url for some case, for example email link # Absolute url for some case, for example email link
SITE_URL = CONFIG.SITE_URL or 'http://localhost' SITE_URL = CONFIG.SITE_URL
# LOG LEVEL # LOG LEVEL
LOG_LEVEL = 'DEBUG' if DEBUG else CONFIG.LOG_LEVEL or 'WARNING' LOG_LEVEL = CONFIG.LOG_LEVEL
ALLOWED_HOSTS = CONFIG.ALLOWED_HOSTS or [] ALLOWED_HOSTS = ['*']
# Application definition # Application definition
...@@ -152,9 +140,9 @@ TEMPLATES = [ ...@@ -152,9 +140,9 @@ TEMPLATES = [
LOGIN_REDIRECT_URL = reverse_lazy('index') LOGIN_REDIRECT_URL = reverse_lazy('index')
LOGIN_URL = reverse_lazy('users:login') LOGIN_URL = reverse_lazy('users:login')
SESSION_COOKIE_DOMAIN = CONFIG.SESSION_COOKIE_DOMAIN or None SESSION_COOKIE_DOMAIN = CONFIG.SESSION_COOKIE_DOMAIN
CSRF_COOKIE_DOMAIN = CONFIG.CSRF_COOKIE_DOMAIN or None CSRF_COOKIE_DOMAIN = CONFIG.CSRF_COOKIE_DOMAIN
SESSION_COOKIE_AGE = CONFIG.SESSION_COOKIE_AGE or 3600 * 24 SESSION_COOKIE_AGE = CONFIG.SESSION_COOKIE_AGE
MESSAGE_STORAGE = 'django.contrib.messages.storage.cookie.CookieStorage' MESSAGE_STORAGE = 'django.contrib.messages.storage.cookie.CookieStorage'
# Database # Database
...@@ -317,13 +305,13 @@ MEDIA_ROOT = os.path.join(PROJECT_DIR, 'data', 'media').replace('\\', '/') + '/' ...@@ -317,13 +305,13 @@ MEDIA_ROOT = os.path.join(PROJECT_DIR, 'data', 'media').replace('\\', '/') + '/'
FIXTURE_DIRS = [os.path.join(BASE_DIR, 'fixtures'), ] FIXTURE_DIRS = [os.path.join(BASE_DIR, 'fixtures'), ]
# Email config # Email config
EMAIL_HOST = CONFIG.EMAIL_HOST EMAIL_HOST = 'smtp.jumpserver.org'
EMAIL_PORT = CONFIG.EMAIL_PORT EMAIL_PORT = 25
EMAIL_HOST_USER = CONFIG.EMAIL_HOST_USER EMAIL_HOST_USER = 'noreply@jumpserver.org'
EMAIL_HOST_PASSWORD = CONFIG.EMAIL_HOST_PASSWORD EMAIL_HOST_PASSWORD = ''
EMAIL_USE_SSL = CONFIG.EMAIL_USE_SSL EMAIL_USE_SSL = False
EMAIL_USE_TLS = CONFIG.EMAIL_USE_TLS EMAIL_USE_TLS = False
EMAIL_SUBJECT_PREFIX = CONFIG.EMAIL_SUBJECT_PREFIX EMAIL_SUBJECT_PREFIX = '[Jumpserver] '
REST_FRAMEWORK = { REST_FRAMEWORK = {
# Use Django's standard `django.contrib.auth` permissions, # Use Django's standard `django.contrib.auth` permissions,
...@@ -363,23 +351,23 @@ FILE_UPLOAD_PERMISSIONS = 0o644 ...@@ -363,23 +351,23 @@ FILE_UPLOAD_PERMISSIONS = 0o644
FILE_UPLOAD_DIRECTORY_PERMISSIONS = 0o755 FILE_UPLOAD_DIRECTORY_PERMISSIONS = 0o755
# Auth LDAP settings # Auth LDAP settings
AUTH_LDAP = CONFIG.AUTH_LDAP AUTH_LDAP = False
AUTH_LDAP_SERVER_URI = CONFIG.AUTH_LDAP_SERVER_URI AUTH_LDAP_SERVER_URI = 'ldap://localhost:389'
AUTH_LDAP_BIND_DN = CONFIG.AUTH_LDAP_BIND_DN AUTH_LDAP_BIND_DN = 'cn=admin,dc=jumpserver,dc=org'
AUTH_LDAP_BIND_PASSWORD = CONFIG.AUTH_LDAP_BIND_PASSWORD AUTH_LDAP_BIND_PASSWORD = ''
AUTH_LDAP_SEARCH_OU = CONFIG.AUTH_LDAP_SEARCH_OU AUTH_LDAP_SEARCH_OU = 'ou=tech,dc=jumpserver,dc=org'
AUTH_LDAP_SEARCH_FILTER = CONFIG.AUTH_LDAP_SEARCH_FILTER AUTH_LDAP_SEARCH_FILTER = '(cn=%(user)s)'
AUTH_LDAP_START_TLS = CONFIG.AUTH_LDAP_START_TLS AUTH_LDAP_START_TLS = False
AUTH_LDAP_USER_ATTR_MAP = CONFIG.AUTH_LDAP_USER_ATTR_MAP AUTH_LDAP_USER_ATTR_MAP = {"username": "cn", "name": "sn", "email": "mail"}
AUTH_LDAP_USER_SEARCH_UNION = [ AUTH_LDAP_USER_SEARCH_UNION = lambda: [
LDAPSearch(USER_SEARCH, ldap.SCOPE_SUBTREE, AUTH_LDAP_SEARCH_FILTER) LDAPSearch(USER_SEARCH, ldap.SCOPE_SUBTREE, AUTH_LDAP_SEARCH_FILTER)
for USER_SEARCH in str(AUTH_LDAP_SEARCH_OU).split("|") for USER_SEARCH in str(AUTH_LDAP_SEARCH_OU).split("|")
] ]
AUTH_LDAP_USER_SEARCH = LDAPSearchUnion(*AUTH_LDAP_USER_SEARCH_UNION) AUTH_LDAP_USER_SEARCH = lambda: LDAPSearchUnion(*AUTH_LDAP_USER_SEARCH_UNION())
AUTH_LDAP_GROUP_SEARCH_OU = CONFIG.AUTH_LDAP_GROUP_SEARCH_OU AUTH_LDAP_GROUP_SEARCH_OU = CONFIG.AUTH_LDAP_GROUP_SEARCH_OU
AUTH_LDAP_GROUP_SEARCH_FILTER = CONFIG.AUTH_LDAP_GROUP_SEARCH_FILTER AUTH_LDAP_GROUP_SEARCH_FILTER = CONFIG.AUTH_LDAP_GROUP_SEARCH_FILTER
AUTH_LDAP_GROUP_SEARCH = LDAPSearch( AUTH_LDAP_GROUP_SEARCH = LDAPSearch(
AUTH_LDAP_GROUP_SEARCH_OU, ldap.SCOPE_SUBTREE, AUTH_LDAP_GROUP_SEARCH_FILTER AUTH_LDAP_GROUP_SEARCH_OU, ldap.SCOPE_SUBTREE, AUTH_LDAP_GROUP_SEARCH_FILTER
) )
AUTH_LDAP_CONNECTION_OPTIONS = { AUTH_LDAP_CONNECTION_OPTIONS = {
ldap.OPT_TIMEOUT: 5 ldap.OPT_TIMEOUT: 5
...@@ -414,7 +402,7 @@ CELERY_BROKER_URL = 'redis://:%(password)s@%(host)s:%(port)s/%(db)s' % { ...@@ -414,7 +402,7 @@ CELERY_BROKER_URL = 'redis://:%(password)s@%(host)s:%(port)s/%(db)s' % {
'password': CONFIG.REDIS_PASSWORD if CONFIG.REDIS_PASSWORD else '', 'password': CONFIG.REDIS_PASSWORD if CONFIG.REDIS_PASSWORD else '',
'host': CONFIG.REDIS_HOST or '127.0.0.1', 'host': CONFIG.REDIS_HOST or '127.0.0.1',
'port': CONFIG.REDIS_PORT or 6379, 'port': CONFIG.REDIS_PORT or 6379,
'db':CONFIG.REDIS_DB_CELERY_BROKER or 3, 'db': CONFIG.REDIS_DB_CELERY_BROKER or 3,
} }
CELERY_TASK_SERIALIZER = 'pickle' CELERY_TASK_SERIALIZER = 'pickle'
CELERY_RESULT_SERIALIZER = 'pickle' CELERY_RESULT_SERIALIZER = 'pickle'
...@@ -436,10 +424,10 @@ CACHES = { ...@@ -436,10 +424,10 @@ CACHES = {
'default': { 'default': {
'BACKEND': 'redis_cache.RedisCache', 'BACKEND': 'redis_cache.RedisCache',
'LOCATION': 'redis://:%(password)s@%(host)s:%(port)s/%(db)s' % { 'LOCATION': 'redis://:%(password)s@%(host)s:%(port)s/%(db)s' % {
'password': CONFIG.REDIS_PASSWORD if CONFIG.REDIS_PASSWORD else '', 'password': CONFIG.REDIS_PASSWORD,
'host': CONFIG.REDIS_HOST or '127.0.0.1', 'host': CONFIG.REDIS_HOST,
'port': CONFIG.REDIS_PORT or 6379, 'port': CONFIG.REDIS_PORT,
'db': CONFIG.REDIS_DB_CACHE or 4, 'db': CONFIG.REDIS_DB_CACHE,
} }
} }
} }
...@@ -454,27 +442,44 @@ COMMAND_STORAGE = { ...@@ -454,27 +442,44 @@ COMMAND_STORAGE = {
'ENGINE': 'terminal.backends.command.db', 'ENGINE': 'terminal.backends.command.db',
} }
TERMINAL_COMMAND_STORAGE = { DEFAULT_TERMINAL_COMMAND_STORAGE = {
"default": { "default": {
"TYPE": "server", "TYPE": "server",
}, },
}
TERMINAL_COMMAND_STORAGE = {
# 'ali-es': { # 'ali-es': {
# 'TYPE': 'elasticsearch', # 'TYPE': 'elasticsearch',
# 'HOSTS': ['http://elastic:changeme@localhost:9200'], # 'HOSTS': ['http://elastic:changeme@localhost:9200'],
# }, # },
} }
TERMINAL_REPLAY_STORAGE = { DEFAULT_TERMINAL_REPLAY_STORAGE = {
"default": { "default": {
"TYPE": "server", "TYPE": "server",
}, },
} }
TERMINAL_REPLAY_STORAGE = {
}
SECURITY_PASSWORD_MIN_LENGTH = 6 SECURITY_MFA_AUTH = False
SECURITY_LOGIN_LIMIT_COUNT = 7 SECURITY_LOGIN_LIMIT_COUNT = 7
SECURITY_LOGIN_LIMIT_TIME = 30 # Unit: minute SECURITY_LOGIN_LIMIT_TIME = 30 # Unit: minute
SECURITY_MAX_IDLE_TIME = 30 # Unit: minute SECURITY_MAX_IDLE_TIME = 30 # Unit: minute
SECURITY_PASSWORD_MIN_LENGTH = 6
SECURITY_PASSWORD_UPPER_CASE = False
SECURITY_PASSWORD_LOWER_CASE = False
SECURITY_PASSWORD_NUMBER = False
SECURITY_PASSWORD_SPECIAL_CHAR = False
SECURITY_PASSWORD_RULES = [
'SECURITY_PASSWORD_MIN_LENGTH',
'SECURITY_PASSWORD_UPPER_CASE',
'SECURITY_PASSWORD_LOWER_CASE',
'SECURITY_PASSWORD_NUMBER',
'SECURITY_PASSWORD_SPECIAL_CHAR'
]
# Django bootstrap3 setting, more see http://django-bootstrap3.readthedocs.io/en/latest/settings.html # Django bootstrap3 setting, more see http://django-bootstrap3.readthedocs.io/en/latest/settings.html
BOOTSTRAP3 = { BOOTSTRAP3 = {
...@@ -486,8 +491,8 @@ BOOTSTRAP3 = { ...@@ -486,8 +491,8 @@ BOOTSTRAP3 = {
'success_css_class': '', 'success_css_class': '',
} }
TOKEN_EXPIRATION = CONFIG.TOKEN_EXPIRATION or 3600 TOKEN_EXPIRATION = CONFIG.TOKEN_EXPIRATION
DISPLAY_PER_PAGE = CONFIG.DISPLAY_PER_PAGE or 25 DISPLAY_PER_PAGE = CONFIG.DISPLAY_PER_PAGE
DEFAULT_EXPIRED_YEARS = 70 DEFAULT_EXPIRED_YEARS = 70
USER_GUIDE_URL = "" USER_GUIDE_URL = ""
......
This diff is collapsed.
...@@ -8,7 +8,7 @@ msgid "" ...@@ -8,7 +8,7 @@ msgid ""
msgstr "" msgstr ""
"Project-Id-Version: PACKAGE VERSION\n" "Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n" "Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2018-08-08 14:48+0800\n" "POT-Creation-Date: 2018-11-21 19:14+0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n" "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n" "Language-Team: LANGUAGE <LL@li.org>\n"
...@@ -17,58 +17,58 @@ msgstr "" ...@@ -17,58 +17,58 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n" "Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n" "Content-Transfer-Encoding: 8bit\n"
#: static/js/jumpserver.js:158 #: static/js/jumpserver.js:168
msgid "Update is successful!" msgid "Update is successful!"
msgstr "更新成功" msgstr "更新成功"
#: static/js/jumpserver.js:160 #: static/js/jumpserver.js:170
msgid "An unknown error occurred while updating.." msgid "An unknown error occurred while updating.."
msgstr "更新时发生未知错误" msgstr "更新时发生未知错误"
#: static/js/jumpserver.js:205 static/js/jumpserver.js:247 #: static/js/jumpserver.js:236 static/js/jumpserver.js:273
#: static/js/jumpserver.js:252 #: static/js/jumpserver.js:276
msgid "Error" msgid "Error"
msgstr "错误" msgstr "错误"
#: static/js/jumpserver.js:205 #: static/js/jumpserver.js:236
msgid "Being used by the asset, please unbind the asset first." msgid "Being used by the asset, please unbind the asset first."
msgstr "正在被资产使用中,请先解除资产绑定" msgstr "正在被资产使用中,请先解除资产绑定"
#: static/js/jumpserver.js:212 static/js/jumpserver.js:260 #: static/js/jumpserver.js:242 static/js/jumpserver.js:283
msgid "Delete the success" msgid "Delete the success"
msgstr "删除成功" msgstr "删除成功"
#: static/js/jumpserver.js:219 #: static/js/jumpserver.js:248
msgid "Are you sure about deleting it?" msgid "Are you sure about deleting it?"
msgstr "你确定删除吗 ?" msgstr "你确定删除吗 ?"
#: static/js/jumpserver.js:224 static/js/jumpserver.js:273 #: static/js/jumpserver.js:252 static/js/jumpserver.js:293
msgid "Cancel" msgid "Cancel"
msgstr "取消" msgstr "取消"
#: static/js/jumpserver.js:227 static/js/jumpserver.js:276 #: static/js/jumpserver.js:254 static/js/jumpserver.js:295
msgid "Confirm" msgid "Confirm"
msgstr "确认" msgstr "确认"
#: static/js/jumpserver.js:247 #: static/js/jumpserver.js:273
msgid "" msgid ""
"The organization contains undeleted information. Please try again after " "The organization contains undeleted information. Please try again after "
"deleting" "deleting"
msgstr "组织中包含未删除信息,请删除后重试" msgstr "组织中包含未删除信息,请删除后重试"
#: static/js/jumpserver.js:252 #: static/js/jumpserver.js:276
msgid "" msgid ""
"Do not perform this operation under this organization. Try again after " "Do not perform this operation under this organization. Try again after "
"switching to another organization" "switching to another organization"
msgstr "请勿在此组织下执行此操作,切换到其他组织后重试" msgstr "请勿在此组织下执行此操作,切换到其他组织后重试"
#: static/js/jumpserver.js:267 #: static/js/jumpserver.js:289
msgid "" msgid ""
"Please ensure that the following information in the organization has been " "Please ensure that the following information in the organization has been "
"deleted" "deleted"
msgstr "请确保组织内的以下信息已删除" msgstr "请确保组织内的以下信息已删除"
#: static/js/jumpserver.js:269 #: static/js/jumpserver.js:290
msgid "" msgid ""
"User list、User group、Asset list、Domain list、Admin user、System user、" "User list、User group、Asset list、Domain list、Admin user、System user、"
"Labels、Asset permission" "Labels、Asset permission"
...@@ -76,32 +76,52 @@ msgstr "" ...@@ -76,32 +76,52 @@ msgstr ""
"用户列表、用户组、资产列表、网域列表、管理用户、系统用户、标签管理、资产授权" "用户列表、用户组、资产列表、网域列表、管理用户、系统用户、标签管理、资产授权"
"规则" "规则"
#: static/js/jumpserver.js:311 #: static/js/jumpserver.js:329
msgid "Loading ..." msgid "Loading ..."
msgstr "加载中 ..." msgstr "加载中 ..."
#: static/js/jumpserver.js:313 #: static/js/jumpserver.js:330
msgid "Search" msgid "Search"
msgstr "搜索" msgstr "搜索"
#: static/js/jumpserver.js:317 #: static/js/jumpserver.js:333
#, javascript-format #, javascript-format
msgid "Selected item %d" msgid "Selected item %d"
msgstr "选中 %d 项" msgstr "选中 %d 项"
#: static/js/jumpserver.js:322 #: static/js/jumpserver.js:337
msgid "Per page _MENU_" msgid "Per page _MENU_"
msgstr "每页 _MENU_" msgstr "每页 _MENU_"
#: static/js/jumpserver.js:324 #: static/js/jumpserver.js:338
msgid "" msgid ""
"Displays the results of items _START_ to _END_; A total of _TOTAL_ entries" "Displays the results of items _START_ to _END_; A total of _TOTAL_ entries"
msgstr "显示第 _START_ 至 _END_ 项结果; 总共 _TOTAL_ 项" msgstr "显示第 _START_ 至 _END_ 项结果; 总共 _TOTAL_ 项"
#: static/js/jumpserver.js:328 #: static/js/jumpserver.js:341
msgid "No match" msgid "No match"
msgstr "没有匹配项" msgstr "没有匹配项"
#: static/js/jumpserver.js:330 #: static/js/jumpserver.js:342
msgid "No record" msgid "No record"
msgstr "没有记录" msgstr "没有记录"
#: static/js/jumpserver.js:701
msgid "Password minimum length {N} bits"
msgstr "密码最小长度 {N} 位"
#: static/js/jumpserver.js:702
msgid "Must contain capital letters"
msgstr "必须包含大写字母"
#: static/js/jumpserver.js:703
msgid "Must contain lowercase letters"
msgstr "必须包含小写字母"
#: static/js/jumpserver.js:704
msgid "Must contain numeric characters"
msgstr "必须包含数字字符"
#: static/js/jumpserver.js:705
msgid "Must contain special characters"
msgstr "必须包含特殊字符"
...@@ -101,7 +101,7 @@ class AssetPermissionUserView(AdminUserRequiredMixin, ...@@ -101,7 +101,7 @@ class AssetPermissionUserView(AdminUserRequiredMixin,
ListView): ListView):
template_name = 'perms/asset_permission_user.html' template_name = 'perms/asset_permission_user.html'
context_object_name = 'asset_permission' context_object_name = 'asset_permission'
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE paginate_by = settings.DISPLAY_PER_PAGE
object = None object = None
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
...@@ -133,7 +133,7 @@ class AssetPermissionAssetView(AdminUserRequiredMixin, ...@@ -133,7 +133,7 @@ class AssetPermissionAssetView(AdminUserRequiredMixin,
ListView): ListView):
template_name = 'perms/asset_permission_asset.html' template_name = 'perms/asset_permission_asset.html'
context_object_name = 'asset_permission' context_object_name = 'asset_permission'
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE paginate_by = settings.DISPLAY_PER_PAGE
object = None object = None
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
......
...@@ -688,41 +688,69 @@ function setUrlParam(url, name, value) { ...@@ -688,41 +688,69 @@ function setUrlParam(url, name, value) {
return url return url
} }
// Password check rules
var rules_short_map_id = {
'min': 'id_security_password_min_length',
'upper': 'id_security_password_upper_case',
'lower': 'id_security_password_lower_case',
'number': 'id_security_password_number',
'special': 'id_security_password_special_char'
};
var rules_id_map_label = {
'id_security_password_min_length': gettext('Password minimum length {N} bits'),
'id_security_password_upper_case': gettext('Must contain capital letters'),
'id_security_password_lower_case': gettext('Must contain lowercase letters'),
'id_security_password_number': gettext('Must contain numeric characters'),
'id_security_password_special_char': gettext('Must contain special characters')
};
function getRuleLabel(rule){
var label = '';
if (rule.key === rules_short_map_id['min']){
label = rules_id_map_label[rule.key].replace('{N}', rule.value)
}
else{
label = rules_id_map_label[rule.key]
}
return label
}
// 校验密码-改变规则颜色 // 校验密码-改变规则颜色
function checkPasswordRules(password, minLength) { function checkPasswordRules(password, minLength) {
if (wordMinLength(password, minLength)) { if (wordMinLength(password, minLength)) {
$('#rule_SECURITY_PASSWORD_MIN_LENGTH').css('color', 'green') $('#'+rules_short_map_id['min']).css('color', 'green')
} }
else { else {
$('#rule_SECURITY_PASSWORD_MIN_LENGTH').css('color', '#908a8a') $('#'+rules_short_map_id['min']).css('color', '#908a8a')
} }
if (wordUpperCase(password)) { if (wordUpperCase(password)) {
$('#rule_SECURITY_PASSWORD_UPPER_CASE').css('color', 'green'); $('#'+rules_short_map_id['upper']).css('color', 'green')
} }
else { else {
$('#rule_SECURITY_PASSWORD_UPPER_CASE').css('color', '#908a8a') $('#'+rules_short_map_id['upper']).css('color', '#908a8a')
} }
if (wordLowerCase(password)) { if (wordLowerCase(password)) {
$('#rule_SECURITY_PASSWORD_LOWER_CASE').css('color', 'green') $('#'+rules_short_map_id['lower']).css('color', 'green')
} }
else { else {
$('#rule_SECURITY_PASSWORD_LOWER_CASE').css('color', '#908a8a') $('#'+rules_short_map_id['lower']).css('color', '#908a8a')
} }
if (wordNumber(password)) { if (wordNumber(password)) {
$('#rule_SECURITY_PASSWORD_NUMBER').css('color', 'green') $('#'+rules_short_map_id['number']).css('color', 'green')
} }
else { else {
$('#rule_SECURITY_PASSWORD_NUMBER').css('color', '#908a8a') $('#'+rules_short_map_id['number']).css('color', '#908a8a')
} }
if (wordSpecialChar(password)) { if (wordSpecialChar(password)) {
$('#rule_SECURITY_PASSWORD_SPECIAL_CHAR').css('color', 'green') $('#'+rules_short_map_id['special']).css('color', 'green')
} }
else { else {
$('#rule_SECURITY_PASSWORD_SPECIAL_CHAR').css('color', '#908a8a') $('#'+rules_short_map_id['special']).css('color', '#908a8a')
} }
} }
...@@ -749,11 +777,12 @@ function wordSpecialChar(word) { ...@@ -749,11 +777,12 @@ function wordSpecialChar(word) {
return word.match(/[`,~,!,@,#,\$,%,\^,&,\*,\(,\),\-,_,=,\+,\{,\},\[,\],\|,\\,;,',:,",\,,\.,<,>,\/,\?]+/) return word.match(/[`,~,!,@,#,\$,%,\^,&,\*,\(,\),\-,_,=,\+,\{,\},\[,\],\|,\\,;,',:,",\,,\.,<,>,\/,\?]+/)
} }
// 显示弹窗密码规则 // 显示弹窗密码规则
function popoverPasswordRules(password_check_rules, $el) { function popoverPasswordRules(password_check_rules, $el) {
var message = ""; var message = "";
jQuery.each(password_check_rules, function (idx, rules) { jQuery.each(password_check_rules, function (idx, rule) {
message += "<li id=" + rules.id + " style='list-style-type:none;'> <i class='fa fa-check-circle-o' style='margin-right:10px;' ></i>" + rules.label + "</li>"; message += "<li id=" + rule.key + " style='list-style-type:none;'> <i class='fa fa-check-circle-o' style='margin-right:10px;' ></i>" + getRuleLabel(rule) + "</li>";
}); });
//$('#id_password_rules').html(message); //$('#id_password_rules').html(message);
$el.html(message) $el.html(message)
......
...@@ -9,7 +9,6 @@ from django.conf import settings ...@@ -9,7 +9,6 @@ from django.conf import settings
from users.models import User from users.models import User
from orgs.mixins import OrgModelMixin from orgs.mixins import OrgModelMixin
from common.models import common_settings
from common.utils import get_command_storage_setting, get_replay_storage_setting from common.utils import get_command_storage_setting, get_replay_storage_setting
from .backends.command.models import AbstractSessionCommand from .backends.command.models import AbstractSessionCommand
...@@ -61,11 +60,11 @@ class Terminal(models.Model): ...@@ -61,11 +60,11 @@ class Terminal(models.Model):
configs = {} configs = {}
for k in dir(settings): for k in dir(settings):
if k.startswith('TERMINAL'): if k.startswith('TERMINAL'):
configs[k] = getattr(common_settings, k) configs[k] = getattr(settings, k)
configs.update(self.get_common_storage()) configs.update(self.get_common_storage())
configs.update(self.get_replay_storage()) configs.update(self.get_replay_storage())
configs.update({ configs.update({
'SECURITY_MAX_IDLE_TIME': common_settings.SECURITY_MAX_IDLE_TIME 'SECURITY_MAX_IDLE_TIME': settings.SECURITY_MAX_IDLE_TIME
}) })
return configs return configs
......
...@@ -14,7 +14,6 @@ from django.utils import timezone ...@@ -14,7 +14,6 @@ from django.utils import timezone
from django.shortcuts import reverse from django.shortcuts import reverse
from common.utils import get_signer, date_expired_default from common.utils import get_signer, date_expired_default
from common.models import common_settings
from orgs.mixins import OrgManager from orgs.mixins import OrgManager
from orgs.utils import current_org from orgs.utils import current_org
...@@ -284,7 +283,7 @@ class User(AbstractUser): ...@@ -284,7 +283,7 @@ class User(AbstractUser):
@property @property
def otp_force_enabled(self): def otp_force_enabled(self):
if common_settings.SECURITY_MFA_AUTH: if settings.SECURITY_MFA_AUTH:
return True return True
return self.otp_level == 2 return self.otp_level == 2
......
...@@ -99,7 +99,7 @@ ...@@ -99,7 +99,7 @@
container = $('#container'), container = $('#container'),
progress = $('#id_progress'), progress = $('#id_progress'),
password_check_rules = {{ password_check_rules|safe }}, password_check_rules = {{ password_check_rules|safe }},
minLength = {{ min_length }}, minLength = 6,
top = 146, left = 170, top = 146, left = 170,
i18n_fallback = { i18n_fallback = {
"veryWeak": "{% trans 'Very weak' %}", "veryWeak": "{% trans 'Very weak' %}",
...@@ -110,6 +110,12 @@ ...@@ -110,6 +110,12 @@
"veryStrong": "{% trans 'Very strong' %}" "veryStrong": "{% trans 'Very strong' %}"
}; };
jQuery.each(password_check_rules, function (idx, rules) {
if(rules.key === 'id_security_password_min_length'){
minLength = rules.value
}
});
// 初始化popover // 初始化popover
initPopover(container, progress, idPassword, el, password_check_rules, i18n_fallback); initPopover(container, progress, idPassword, el, password_check_rules, i18n_fallback);
......
...@@ -92,7 +92,7 @@ ...@@ -92,7 +92,7 @@
container = $('#container'), container = $('#container'),
progress = $('#id_progress'), progress = $('#id_progress'),
password_check_rules = {{ password_check_rules|safe }}, password_check_rules = {{ password_check_rules|safe }},
minLength = {{ min_length }}, minLength = 6,
top = idPassword.offset().top - $('.navbar').outerHeight(true) - $('.page-heading').outerHeight(true) - 10 + 34, top = idPassword.offset().top - $('.navbar').outerHeight(true) - $('.page-heading').outerHeight(true) - 10 + 34,
left = 377, left = 377,
i18n_fallback = { i18n_fallback = {
...@@ -104,6 +104,12 @@ ...@@ -104,6 +104,12 @@
"veryStrong": "{% trans 'Very strong' %}" "veryStrong": "{% trans 'Very strong' %}"
}; };
jQuery.each(password_check_rules, function (idx, rules) {
if(rules.key === 'id_security_password_min_length'){
minLength = rules.value
}
});
// 初始化popover // 初始化popover
initPopover(container, progress, idPassword, el, password_check_rules, i18n_fallback); initPopover(container, progress, idPassword, el, password_check_rules, i18n_fallback);
......
...@@ -27,7 +27,7 @@ ...@@ -27,7 +27,7 @@
container = $('#container'), container = $('#container'),
progress = $('#id_progress'), progress = $('#id_progress'),
password_check_rules = {{ password_check_rules|safe }}, password_check_rules = {{ password_check_rules|safe }},
minLength = {{ min_length }}, minLength = 6,
top = idPassword.offset().top - $('.navbar').outerHeight(true) - $('.page-heading').outerHeight(true) - 10 + 34, top = idPassword.offset().top - $('.navbar').outerHeight(true) - $('.page-heading').outerHeight(true) - 10 + 34,
left = 377, left = 377,
i18n_fallback = { i18n_fallback = {
...@@ -39,6 +39,12 @@ ...@@ -39,6 +39,12 @@
"veryStrong": "{% trans 'Very strong' %}" "veryStrong": "{% trans 'Very strong' %}"
}; };
jQuery.each(password_check_rules, function (idx, rules) {
if(rules.key === 'id_security_password_min_length'){
minLength = rules.value
}
});
// 初始化popover // 初始化popover
initPopover(container, progress, idPassword, el, password_check_rules, i18n_fallback); initPopover(container, progress, idPassword, el, password_check_rules, i18n_fallback);
......
...@@ -19,8 +19,8 @@ from django.core.cache import cache ...@@ -19,8 +19,8 @@ from django.core.cache import cache
from common.tasks import send_mail_async from common.tasks import send_mail_async
from common.utils import reverse, get_object_or_none from common.utils import reverse, get_object_or_none
from common.models import common_settings, Setting
from common.forms import SecuritySettingForm from common.forms import SecuritySettingForm
from common.models import Setting
from .models import User, LoginLog from .models import User, LoginLog
...@@ -275,56 +275,27 @@ def check_otp_code(otp_secret_key, otp_code): ...@@ -275,56 +275,27 @@ def check_otp_code(otp_secret_key, otp_code):
def get_password_check_rules(): def get_password_check_rules():
check_rules = [] check_rules = []
min_length = settings.DEFAULT_PASSWORD_MIN_LENGTH for rule in settings.SECURITY_PASSWORD_RULES:
min_name = 'SECURITY_PASSWORD_MIN_LENGTH' key = "id_{}".format(rule.lower())
base_filed = SecuritySettingForm.base_fields value = getattr(settings, rule)
password_setting = Setting.objects.filter(name__startswith='SECURITY_PASSWORD') if not value:
continue
if not password_setting: check_rules.append({'key': key, 'value': int(value)})
# 用户还没有设置过密码校验规则 return check_rules
label = base_filed.get(min_name).label
label += ' ' + str(min_length) + _('Bit')
id = 'rule_' + min_name
rules = {'id': id, 'label': label}
check_rules.append(rules)
for setting in password_setting:
if setting.cleaned_value:
id = 'rule_' + setting.name
label = base_filed.get(setting.name).label
if setting.name == min_name:
label += str(setting.cleaned_value) + _('Bit')
min_length = setting.cleaned_value
rules = {'id': id, 'label': label}
check_rules.append(rules)
return check_rules, min_length
def check_password_rules(password): def check_password_rules(password):
min_field_name = 'SECURITY_PASSWORD_MIN_LENGTH' pattern = r"^"
upper_field_name = 'SECURITY_PASSWORD_UPPER_CASE' if settings.SECURITY_PASSWORD_UPPER_CASE:
lower_field_name = 'SECURITY_PASSWORD_LOWER_CASE' pattern += '(?=.*[A-Z])'
number_field_name = 'SECURITY_PASSWORD_NUMBER' if settings.SECURITY_PASSWORD_LOWER_CASE:
special_field_name = 'SECURITY_PASSWORD_SPECIAL_CHAR' pattern += '(?=.*[a-z])'
min_length = getattr(common_settings, min_field_name) if settings.SECURITY_PASSWORD_NUMBER:
pattern += '(?=.*\d)'
password_setting = Setting.objects.filter(name__startswith='SECURITY_PASSWORD') if settings.SECURITY_PASSWORD_SPECIAL_CHAR:
if not password_setting: pattern += '(?=.*[`~!@#\$%\^&\*\(\)-=_\+\[\]\{\}\|;:\'\",\.<>\/\?])'
pattern = r"^.{" + str(min_length) + ",}$" pattern += '[a-zA-Z\d`~!@#\$%\^&\*\(\)-=_\+\[\]\{\}\|;:\'\",\.<>\/\?]'
else: pattern += '.{' + str(settings.SECURITY_PASSWORD_MIN_LENGTH-1) + ',}$'
pattern = r"^"
for setting in password_setting:
if setting.cleaned_value and setting.name == upper_field_name:
pattern += '(?=.*[A-Z])'
elif setting.cleaned_value and setting.name == lower_field_name:
pattern += '(?=.*[a-z])'
elif setting.cleaned_value and setting.name == number_field_name:
pattern += '(?=.*\d)'
elif setting.cleaned_value and setting.name == special_field_name:
pattern += '(?=.*[`~!@#\$%\^&\*\(\)-=_\+\[\]\{\}\|;:\'",\.<>\/\?])'
pattern += '[a-zA-Z\d`~!@#\$%\^&\*\(\)-=_\+\[\]\{\}\|;:\'",\.<>\/\?]'
match_obj = re.match(pattern, password) match_obj = re.match(pattern, password)
return bool(match_obj) return bool(match_obj)
...@@ -339,7 +310,7 @@ def increase_login_failed_count(username, ip): ...@@ -339,7 +310,7 @@ def increase_login_failed_count(username, ip):
count = cache.get(key_limit) count = cache.get(key_limit)
count = count + 1 if count else 1 count = count + 1 if count else 1
limit_time = common_settings.SECURITY_LOGIN_LIMIT_TIME limit_time = settings.SECURITY_LOGIN_LIMIT_TIME
cache.set(key_limit, count, int(limit_time)*60) cache.set(key_limit, count, int(limit_time)*60)
...@@ -355,8 +326,8 @@ def is_block_login(username, ip): ...@@ -355,8 +326,8 @@ def is_block_login(username, ip):
key_block = key_prefix_block.format(username) key_block = key_prefix_block.format(username)
count = cache.get(key_limit, 0) count = cache.get(key_limit, 0)
limit_count = common_settings.SECURITY_LOGIN_LIMIT_COUNT limit_count = settings.SECURITY_LOGIN_LIMIT_COUNT
limit_time = common_settings.SECURITY_LOGIN_LIMIT_TIME limit_time = settings.SECURITY_LOGIN_LIMIT_TIME
if count >= limit_count: if count >= limit_count:
cache.set(key_block, 1, int(limit_time)*60) cache.set(key_block, 1, int(limit_time)*60)
......
...@@ -17,11 +17,10 @@ from django.views.decorators.csrf import csrf_protect ...@@ -17,11 +17,10 @@ from django.views.decorators.csrf import csrf_protect
from django.views.decorators.debug import sensitive_post_parameters from django.views.decorators.debug import sensitive_post_parameters
from django.views.generic.base import TemplateView from django.views.generic.base import TemplateView
from django.views.generic.edit import FormView from django.views.generic.edit import FormView
from formtools.wizard.views import SessionWizardView
from django.conf import settings from django.conf import settings
from formtools.wizard.views import SessionWizardView
from common.utils import get_object_or_none, get_request_ip from common.utils import get_object_or_none, get_request_ip
from common.models import common_settings
from ..models import User, LoginLog from ..models import User, LoginLog
from ..utils import send_reset_password_mail, check_otp_code, \ from ..utils import send_reset_password_mail, check_otp_code, \
redirect_user_first_login_or_index, get_user_or_tmp_user, \ redirect_user_first_login_or_index, get_user_or_tmp_user, \
...@@ -269,13 +268,11 @@ class UserResetPasswordView(TemplateView): ...@@ -269,13 +268,11 @@ class UserResetPasswordView(TemplateView):
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
token = request.GET.get('token') token = request.GET.get('token')
user = User.validate_reset_token(token) user = User.validate_reset_token(token)
check_rules, min_length = get_password_check_rules()
password_rules = {'password_check_rules': check_rules, 'min_length': min_length}
kwargs.update(password_rules)
if not user: if not user:
kwargs.update({'errors': _('Token invalid or expired')}) kwargs.update({'errors': _('Token invalid or expired')})
else:
check_rules = get_password_check_rules()
kwargs.update({'password_check_rules': check_rules})
return super().get(request, *args, **kwargs) return super().get(request, *args, **kwargs)
def post(self, request, *args, **kwargs): def post(self, request, *args, **kwargs):
...@@ -326,7 +323,7 @@ class UserFirstLoginView(LoginRequiredMixin, SessionWizardView): ...@@ -326,7 +323,7 @@ class UserFirstLoginView(LoginRequiredMixin, SessionWizardView):
user.is_public_key_valid = True user.is_public_key_valid = True
user.save() user.save()
context = { context = {
'user_guide_url': common_settings.USER_GUIDE_URL 'user_guide_url': settings.USER_GUIDE_URL
} }
return render(self.request, 'users/first_login_done.html', context) return render(self.request, 'users/first_login_done.html', context)
......
...@@ -14,6 +14,7 @@ from django.contrib.auth.mixins import LoginRequiredMixin ...@@ -14,6 +14,7 @@ from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.auth import authenticate, login as auth_login from django.contrib.auth import authenticate, login as auth_login
from django.contrib.messages.views import SuccessMessageMixin from django.contrib.messages.views import SuccessMessageMixin
from django.core.cache import cache from django.core.cache import cache
from django.conf import settings
from django.http import HttpResponse, JsonResponse from django.http import HttpResponse, JsonResponse
from django.shortcuts import redirect from django.shortcuts import redirect
from django.urls import reverse_lazy, reverse from django.urls import reverse_lazy, reverse
...@@ -33,7 +34,6 @@ from django.contrib.auth import logout as auth_logout ...@@ -33,7 +34,6 @@ from django.contrib.auth import logout as auth_logout
from common.const import create_success_msg, update_success_msg from common.const import create_success_msg, update_success_msg
from common.mixins import JSONResponseMixin from common.mixins import JSONResponseMixin
from common.utils import get_logger, get_object_or_none, is_uuid, ssh_key_gen from common.utils import get_logger, get_object_or_none, is_uuid, ssh_key_gen
from common.models import Setting, common_settings
from common.permissions import AdminUserRequiredMixin from common.permissions import AdminUserRequiredMixin
from orgs.utils import current_org from orgs.utils import current_org
from .. import forms from .. import forms
...@@ -106,12 +106,11 @@ class UserUpdateView(AdminUserRequiredMixin, SuccessMessageMixin, UpdateView): ...@@ -106,12 +106,11 @@ class UserUpdateView(AdminUserRequiredMixin, SuccessMessageMixin, UpdateView):
success_message = update_success_msg success_message = update_success_msg
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
check_rules, min_length = get_password_check_rules() check_rules = get_password_check_rules()
context = { context = {
'app': _('Users'), 'app': _('Users'),
'action': _('Update user'), 'action': _('Update user'),
'password_check_rules': check_rules, 'password_check_rules': check_rules,
'min_length': min_length
} }
kwargs.update(context) kwargs.update(context)
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)
...@@ -362,7 +361,7 @@ class UserProfileView(LoginRequiredMixin, TemplateView): ...@@ -362,7 +361,7 @@ class UserProfileView(LoginRequiredMixin, TemplateView):
template_name = 'users/user_profile.html' template_name = 'users/user_profile.html'
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
mfa_setting = common_settings.SECURITY_MFA_AUTH mfa_setting = settings.SECURITY_MFA_AUTH
context = { context = {
'action': _('Profile'), 'action': _('Profile'),
'mfa_setting': mfa_setting if mfa_setting is not None else False, 'mfa_setting': mfa_setting if mfa_setting is not None else False,
...@@ -399,12 +398,11 @@ class UserPasswordUpdateView(LoginRequiredMixin, UpdateView): ...@@ -399,12 +398,11 @@ class UserPasswordUpdateView(LoginRequiredMixin, UpdateView):
return self.request.user return self.request.user
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
check_rules, min_length = get_password_check_rules() check_rules = get_password_check_rules()
context = { context = {
'app': _('Users'), 'app': _('Users'),
'action': _('Password update'), 'action': _('Password update'),
'password_check_rules': check_rules, 'password_check_rules': check_rules,
'min_length': min_length,
} }
kwargs.update(context) kwargs.update(context)
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)
......
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
""" """
jumpserver.config jumpserver.config
~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~
...@@ -13,48 +15,63 @@ BASE_DIR = os.path.dirname(os.path.abspath(__file__)) ...@@ -13,48 +15,63 @@ BASE_DIR = os.path.dirname(os.path.abspath(__file__))
class Config: class Config:
# Use it to encrypt or decrypt data """
Jumpserver Config File
Jumpserver 配置文件
Jumpserver use this config for drive django framework running,
You can set is value or set the same envirment value,
Jumpserver look for config order: file => env => default
Jumpserver使用配置来驱动Django框架的运行,
你可以在该文件中设置,或者设置同样名称的环境变量,
Jumpserver使用配置的顺序: 文件 => 环境变量 => 默认值
"""
# SECURITY WARNING: keep the secret key used in production secret! # SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = os.environ.get('SECRET_KEY') or '2vym+ky!997d5kkcc64mnz06y1mmui3lut#(^wd=%s_qj$1%x' # 加密秘钥 生产环境中请修改为随机字符串,请勿外泄
SECRET_KEY = '2vym+ky!997d5kkcc64mnz06y1mmui3lut#(^wd=%s_qj$1%x'
# Django security setting, if your disable debug model, you should setting that
ALLOWED_HOSTS = ['*']
# Development env open this, when error occur display the full process track, Production disable it # Development env open this, when error occur display the full process track, Production disable it
DEBUG = os.environ.get("DEBUG") or True # DEBUG 模式 开启DEBUG后遇到错误时可以看到更多日志
# DEBUG = True
# DEBUG, INFO, WARNING, ERROR, CRITICAL can set. See https://docs.djangoproject.com/en/1.10/topics/logging/ # DEBUG, INFO, WARNING, ERROR, CRITICAL can set. See https://docs.djangoproject.com/en/1.10/topics/logging/
LOG_LEVEL = os.environ.get("LOG_LEVEL") or 'DEBUG' # 日志级别
LOG_DIR = os.path.join(BASE_DIR, 'logs') # LOG_LEVEL = 'DEBUG'
# LOG_DIR = os.path.join(BASE_DIR, 'logs')
# Database setting, Support sqlite3, mysql, postgres .... # Database setting, Support sqlite3, mysql, postgres ....
# 数据库设置
# See https://docs.djangoproject.com/en/1.10/ref/settings/#databases # See https://docs.djangoproject.com/en/1.10/ref/settings/#databases
# SQLite setting: # SQLite setting:
DB_ENGINE = 'sqlite3' # 使用单文件sqlite数据库
DB_NAME = os.path.join(BASE_DIR, 'data', 'db.sqlite3') # DB_ENGINE = 'sqlite3'
# DB_NAME = os.path.join(BASE_DIR, 'data', 'db.sqlite3')
# MySQL or postgres setting like: # MySQL or postgres setting like:
# DB_ENGINE = os.environ.get("DB_ENGINE") or 'mysql' # 使用Mysql作为数据库
# DB_HOST = os.environ.get("DB_HOST") or '127.0.0.1' DB_ENGINE = 'mysql'
# DB_PORT = os.environ.get("DB_PORT") or 3306 DB_HOST = '127.0.0.1'
# DB_USER = os.environ.get("DB_USER") or 'jumpserver' DB_PORT = 3306
# DB_PASSWORD = os.environ.get("DB_PASSWORD") or 'weakPassword' DB_USER = 'jumpserver'
# DB_NAME = os.environ.get("DB_NAME") or 'jumpserver' DB_PASSWORD = ''
DB_NAME = 'jumpserver'
# When Django start it will bind this host and port # When Django start it will bind this host and port
# ./manage.py runserver 127.0.0.1:8080 # ./manage.py runserver 127.0.0.1:8080
# 运行时绑定端口
HTTP_BIND_HOST = '0.0.0.0' HTTP_BIND_HOST = '0.0.0.0'
HTTP_LISTEN_PORT = 8080 HTTP_LISTEN_PORT = 8080
# Use Redis as broker for celery and web socket # Use Redis as broker for celery and web socket
REDIS_HOST = os.environ.get("REDIS_HOST") or '127.0.0.1' # Redis配置
REDIS_PORT = os.environ.get("REDIS_PORT") or 6379 REDIS_HOST = '127.0.0.1'
REDIS_PASSWORD = os.environ.get("REDIS_PASSWORD") or '' REDIS_PORT = 6379
REDIS_DB_CELERY = os.environ.get('REDIS_DB') or 3 REDIS_PASSWORD = ''
REDIS_DB_CACHE = os.environ.get('REDIS_DB') or 4
# Use OpenID authorization # Use OpenID authorization
# 使用OpenID 来进行认证设置
# BASE_SITE_URL = 'http://localhost:8080' # BASE_SITE_URL = 'http://localhost:8080'
# AUTH_OPENID = False # True or False # AUTH_OPENID = False # True or False
# AUTH_OPENID_SERVER_URL = 'https://openid-auth-server.com/' # AUTH_OPENID_SERVER_URL = 'https://openid-auth-server.com/'
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment