This commit is contained in:
fit2cloud-fengyi
2018-03-06 18:30:42 +08:00
60 changed files with 686 additions and 392 deletions

View File

@@ -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")

View File

@@ -6,3 +6,6 @@ from django.apps import AppConfig
class UsersConfig(AppConfig):
name = 'users'
def ready(self):
from . import signals_handler
super().ready()

View File

@@ -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)

View File

@@ -22,6 +22,7 @@ class UserGroup(NoDeleteModelMixin):
class Meta:
ordering = ['name']
verbose_name = _("User group")
@classmethod
def initial(cls):

View File

@@ -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

View File

@@ -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',))

View 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)

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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)

View File

@@ -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)

View File

@@ -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)