mirror of
https://github.com/jumpserver/jumpserver.git
synced 2026-01-29 21:51:31 +00:00
Merge branch 'docs' of https://github.com/jumpserver/jumpserver into docs
This commit is contained in:
@@ -145,7 +145,8 @@ class UserAuthApi(APIView):
|
||||
|
||||
if not login_ip:
|
||||
x_forwarded_for = request.META.get('HTTP_X_FORWARDED_FOR', '').split(',')
|
||||
if x_forwarded_for:
|
||||
|
||||
if x_forwarded_for and x_forwarded_for[0]:
|
||||
login_ip = x_forwarded_for[0]
|
||||
else:
|
||||
login_ip = request.META.get("REMOTE_ADDR")
|
||||
|
||||
@@ -6,3 +6,6 @@ from django.apps import AppConfig
|
||||
class UsersConfig(AppConfig):
|
||||
name = 'users'
|
||||
|
||||
def ready(self):
|
||||
from . import signals_handler
|
||||
super().ready()
|
||||
|
||||
@@ -16,7 +16,8 @@ class AccessKey(models.Model):
|
||||
default=uuid.uuid4, editable=False)
|
||||
secret = models.UUIDField(verbose_name='AccessKeySecret',
|
||||
default=uuid.uuid4, editable=False)
|
||||
user = models.ForeignKey(User, verbose_name='User', on_delete=models.CASCADE, related_name='access_key')
|
||||
user = models.ForeignKey(User, verbose_name='User',
|
||||
on_delete=models.CASCADE, related_name='access_key')
|
||||
|
||||
def get_id(self):
|
||||
return str(self.id)
|
||||
|
||||
@@ -22,6 +22,7 @@ class UserGroup(NoDeleteModelMixin):
|
||||
|
||||
class Meta:
|
||||
ordering = ['name']
|
||||
verbose_name = _("User group")
|
||||
|
||||
@classmethod
|
||||
def initial(cls):
|
||||
|
||||
@@ -151,6 +151,10 @@ class User(AbstractUser):
|
||||
def save(self, *args, **kwargs):
|
||||
if not self.name:
|
||||
self.name = self.username
|
||||
if self.username == 'admin':
|
||||
self.role = 'Admin'
|
||||
self.is_active = True
|
||||
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
@property
|
||||
@@ -247,6 +251,7 @@ class User(AbstractUser):
|
||||
|
||||
class Meta:
|
||||
ordering = ['username']
|
||||
verbose_name = _("User")
|
||||
|
||||
#: Use this method initial user
|
||||
@classmethod
|
||||
|
||||
@@ -1,21 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
|
||||
from django.dispatch import Signal, receiver
|
||||
from django.db.models.signals import post_save
|
||||
|
||||
from common.utils import get_logger
|
||||
from .models import User
|
||||
|
||||
logger = get_logger(__file__)
|
||||
from django.dispatch import Signal
|
||||
|
||||
|
||||
@receiver(post_save, sender=User)
|
||||
def on_user_created(sender, instance=None, created=False, **kwargs):
|
||||
if created:
|
||||
logger.debug("Receive user `{}` create signal".format(instance.name))
|
||||
from .utils import send_user_created_mail
|
||||
logger.info(" - Sending welcome mail ...".format(instance.name))
|
||||
if instance.email:
|
||||
send_user_created_mail(instance)
|
||||
post_user_create = Signal(providing_args=('user',))
|
||||
|
||||
|
||||
20
apps/users/signals_handler.py
Normal file
20
apps/users/signals_handler.py
Normal file
@@ -0,0 +1,20 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
|
||||
from django.dispatch import receiver
|
||||
from django.db.models.signals import post_save
|
||||
|
||||
from common.utils import get_logger
|
||||
from .models import User
|
||||
|
||||
logger = get_logger(__file__)
|
||||
|
||||
|
||||
@receiver(post_save, sender=User)
|
||||
def on_user_created(sender, instance=None, created=False, **kwargs):
|
||||
if created:
|
||||
logger.debug("Receive user `{}` create signal".format(instance.name))
|
||||
from .utils import send_user_created_mail
|
||||
logger.info(" - Sending welcome mail ...".format(instance.name))
|
||||
if instance.email:
|
||||
send_user_created_mail(instance)
|
||||
@@ -51,11 +51,8 @@
|
||||
</div>
|
||||
<hr/>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
Copyright Jumpserver.org
|
||||
</div>
|
||||
<div class="col-md-6 text-right">
|
||||
<small>© 2014-2018</small>
|
||||
<div class="col-md-12">
|
||||
{% include '_copyright.html' %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -22,24 +22,27 @@
|
||||
<div class="loginColumns animated fadeInDown">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<h2 class="font-bold">欢迎使用Jumpserver开源跳板机</h2>
|
||||
<h2 class="font-bold">欢迎使用Jumpserver开源堡垒机</h2>
|
||||
<p>
|
||||
Jumpserver是一款使用Python, Django开发的开源跳板机系统, 助力互联网企业高效 用户、资产、权限、审计 管理
|
||||
全球首款完全开源的堡垒机,使用GNU GPL v2.0开源协议,是符合 4A 的专业运维审计系统。
|
||||
</p>
|
||||
<p>
|
||||
我们自五湖四海,我们对开源精神无比敬仰和崇拜,我们对完美、整洁、优雅 无止境的追求
|
||||
使用Python / Django 进行开发,遵循 Web 2.0 规范,配备了业界领先的 Web Terminal 解决方案,交互界面美观、用户体验好。
|
||||
</p>
|
||||
<p>
|
||||
专注自动化运维,努力打造 易用、稳定、安全、自动化 的跳板机, 这是我们的不懈的追求和动力
|
||||
采纳分布式架构,支持多机房跨区域部署,中心节点提供 API,各机房部署登录节点,可横向扩展、无并发访问限制。
|
||||
</p>
|
||||
<p>
|
||||
<small>永远年轻,永远热泪盈眶 stay foolish stay hungry</small>
|
||||
改变世界,从一点点开始。
|
||||
</p>
|
||||
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="ibox-content">
|
||||
<div><img src="{% static 'img/logo.png' %}" width="82" height="82"> <span class="font-bold text-center" style="font-size: 32px; font-family: inherit">{% trans 'Login' %}</span></div>
|
||||
<div>
|
||||
<img src="{% static 'img/logo.png' %}" width="60" height="60">
|
||||
<span class="font-bold text-center" style="font-size: 24px; font-family: inherit; margin-left: 20px">{% trans 'Login' %}</span>
|
||||
</div>
|
||||
<form class="m-t" role="form" method="post" action="">
|
||||
{% csrf_token %}
|
||||
{% if form.errors %}
|
||||
@@ -60,12 +63,16 @@
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary block full-width m-b">{% trans 'Login' %}</button>
|
||||
|
||||
{% if demo_mode %}
|
||||
<p class="text-muted font-bold" style="color: red">
|
||||
Demo账号: admin 密码: admin
|
||||
</p>
|
||||
{% endif %}
|
||||
|
||||
<a href="{% url 'users:forgot-password' %}">
|
||||
<small>{% trans 'Forgot password' %}?</small>
|
||||
</a>
|
||||
|
||||
<p class="text-muted text-center">
|
||||
</p>
|
||||
</form>
|
||||
<p class="m-t">
|
||||
</p>
|
||||
@@ -74,11 +81,8 @@
|
||||
</div>
|
||||
<hr/>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
Copyright 北京堆栈科技有限公司
|
||||
</div>
|
||||
<div class="col-md-6 text-right">
|
||||
<small>© 2014-2018</small>
|
||||
<div class="col-md-12">
|
||||
{% include '_copyright.html' %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -70,11 +70,8 @@
|
||||
</div>
|
||||
<hr/>
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
Copyright Jumpserver.org
|
||||
</div>
|
||||
<div class="col-md-6 text-right">
|
||||
<small>© 2014-2018</small>
|
||||
<div class="col-md-12">
|
||||
{% include '_copyright.html' %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -92,8 +92,8 @@ class UserGroupGrantedAssetView(AdminUserRequiredMixin, DetailView):
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = {
|
||||
'app': 'User',
|
||||
'action': 'User group granted asset',
|
||||
'app': _('Users'),
|
||||
'action': _('User group granted asset'),
|
||||
}
|
||||
kwargs.update(context)
|
||||
return super().get_context_data(**kwargs)
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
# ~*~ coding: utf-8 ~*~
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import os
|
||||
from django import forms
|
||||
from django.shortcuts import render
|
||||
from django.contrib.auth import login as auth_login, logout as auth_logout
|
||||
@@ -56,6 +57,7 @@ class UserLoginView(FormView):
|
||||
return HttpResponse(_("Please enable cookies and try again."))
|
||||
auth_login(self.request, form.get_user())
|
||||
x_forwarded_for = self.request.META.get('HTTP_X_FORWARDED_FOR', '').split(',')
|
||||
|
||||
if x_forwarded_for and x_forwarded_for[0]:
|
||||
login_ip = x_forwarded_for[0]
|
||||
else:
|
||||
@@ -75,6 +77,13 @@ class UserLoginView(FormView):
|
||||
self.redirect_field_name,
|
||||
self.request.GET.get(self.redirect_field_name, reverse('index')))
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = {
|
||||
'demo_mode': os.environ.get("DEMO_MODE"),
|
||||
}
|
||||
kwargs.update(context)
|
||||
return super().get_context_data(**kwargs)
|
||||
|
||||
|
||||
@method_decorator(never_cache, name='dispatch')
|
||||
class UserLogoutView(TemplateView):
|
||||
@@ -237,7 +246,7 @@ class LoginLogListView(DatetimeSearchMixin, ListView):
|
||||
if self.user:
|
||||
queryset = queryset.filter(username=self.user)
|
||||
if self.keyword:
|
||||
queryset = self.queryset.filter(
|
||||
queryset = queryset.filter(
|
||||
Q(ip__contains=self.keyword) |
|
||||
Q(city__contains=self.keyword) |
|
||||
Q(username__contains=self.keyword)
|
||||
|
||||
@@ -6,6 +6,7 @@ import json
|
||||
import uuid
|
||||
import csv
|
||||
import codecs
|
||||
import chardet
|
||||
from io import StringIO
|
||||
|
||||
from django.contrib import messages
|
||||
@@ -20,6 +21,7 @@ from django.utils.translation import ugettext as _
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.views import View
|
||||
from django.views.generic.base import TemplateView
|
||||
from django.db import transaction
|
||||
from django.views.generic.edit import (
|
||||
CreateView, UpdateView, FormMixin, FormView
|
||||
)
|
||||
@@ -33,7 +35,7 @@ from common.utils import get_logger, get_object_or_none, is_uuid
|
||||
from .. import forms
|
||||
from ..models import User, UserGroup
|
||||
from ..utils import AdminUserRequiredMixin
|
||||
from ..signals import on_user_created
|
||||
from ..signals import post_user_create
|
||||
|
||||
|
||||
__all__ = [
|
||||
@@ -212,8 +214,10 @@ class UserBulkImportView(AdminUserRequiredMixin, JSONResponseMixin, FormView):
|
||||
|
||||
# todo: need be patch, method to long
|
||||
def form_valid(self, form):
|
||||
file = form.cleaned_data['file']
|
||||
data = file.read().decode('utf-8').strip(codecs.BOM_UTF8.decode('utf-8'))
|
||||
f = form.cleaned_data['file']
|
||||
det_result = chardet.detect(f.read())
|
||||
f.seek(0) # reset file seek index
|
||||
data = f.read().decode(det_result['encoding']).strip(codecs.BOM_UTF8.decode())
|
||||
csv_file = StringIO(data)
|
||||
reader = csv.reader(csv_file)
|
||||
csv_data = [row for row in reader]
|
||||
@@ -252,15 +256,15 @@ class UserBulkImportView(AdminUserRequiredMixin, JSONResponseMixin, FormView):
|
||||
else:
|
||||
continue
|
||||
user_dict[k] = v
|
||||
|
||||
user = get_object_or_none(User, id=id_) if is_uuid(id_) else None
|
||||
user = get_object_or_none(User, id=id_) if id_ and is_uuid(id_) else None
|
||||
if not user:
|
||||
try:
|
||||
groups = user_dict.pop('groups')
|
||||
user = User.objects.create(**user_dict)
|
||||
user.groups.set(groups)
|
||||
created.append(user_dict['username'])
|
||||
on_user_created.send(self.__class__, user=user)
|
||||
with transaction.atomic():
|
||||
groups = user_dict.pop('groups')
|
||||
user = User.objects.create(**user_dict)
|
||||
user.groups.set(groups)
|
||||
created.append(user_dict['username'])
|
||||
post_user_create.send(self.__class__, user=user)
|
||||
except Exception as e:
|
||||
failed.append('%s: %s' % (user_dict['username'], str(e)))
|
||||
else:
|
||||
@@ -309,7 +313,6 @@ class UserProfileView(LoginRequiredMixin, TemplateView):
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = {
|
||||
'app': _('Users'),
|
||||
'action': _('Profile'),
|
||||
}
|
||||
kwargs.update(context)
|
||||
|
||||
Reference in New Issue
Block a user