mirror of https://github.com/jumpserver/jumpserver
[Change] 拆分user view为多个view
parent
87bbb6afde
commit
25cb47d2f5
|
@ -14,8 +14,9 @@ class TerminalSerializer(serializers.ModelSerializer):
|
|||
|
||||
class Meta:
|
||||
model = Terminal
|
||||
fields = ['id', 'name', 'remote_addr', 'type', 'url', 'comment', 'is_accepted',
|
||||
'is_active', 'get_type_display', 'proxy_online', 'is_alive']
|
||||
fields = ['id', 'name', 'remote_addr', 'type', 'url', 'comment',
|
||||
'is_accepted', 'is_active', 'get_type_display',
|
||||
'proxy_online', 'is_alive']
|
||||
|
||||
@staticmethod
|
||||
def get_proxy_online(obj):
|
||||
|
@ -31,6 +32,7 @@ class TerminalSerializer(serializers.ModelSerializer):
|
|||
|
||||
|
||||
class TerminalHeatbeatSerializer(serializers.ModelSerializer):
|
||||
date_start = serializers.DateTimeField
|
||||
class Meta:
|
||||
model = TerminalHeatbeat
|
||||
|
||||
|
|
|
@ -99,6 +99,12 @@ class SystemUserSerializer(serializers.ModelSerializer):
|
|||
return fields
|
||||
|
||||
|
||||
class AssetSystemUserSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = SystemUser
|
||||
fields = ('id', 'name', 'username', 'protocol', 'auth_method', 'comment')
|
||||
|
||||
|
||||
class SystemUserUpdateAssetsSerializer(serializers.ModelSerializer):
|
||||
assets = serializers.PrimaryKeyRelatedField(many=True, queryset=Asset.objects.all())
|
||||
|
||||
|
@ -145,13 +151,13 @@ class AssetSerializer(BulkSerializerMixin, serializers.ModelSerializer):
|
|||
|
||||
|
||||
class AssetGrantedSerializer(serializers.ModelSerializer):
|
||||
system_users = SystemUserSerializer(many=True, read_only=True)
|
||||
system_users_granted = AssetSystemUserSerializer(many=True, read_only=True)
|
||||
is_inherited = serializers.SerializerMethodField()
|
||||
system_users_join = serializers.SerializerMethodField()
|
||||
|
||||
class Meta(object):
|
||||
model = Asset
|
||||
fields = ("id", "hostname", "ip", "port", "system_users", "is_inherited",
|
||||
fields = ("id", "hostname", "ip", "port", "system_users_granted", "is_inherited",
|
||||
"is_active", "system_users_join", "comment")
|
||||
|
||||
@staticmethod
|
||||
|
@ -163,7 +169,7 @@ class AssetGrantedSerializer(serializers.ModelSerializer):
|
|||
|
||||
@staticmethod
|
||||
def get_system_users_join(obj):
|
||||
return ', '.join([system_user.username for system_user in obj.system_users.all()])
|
||||
return ', '.join([system_user.username for system_user in obj.system_users_granted])
|
||||
|
||||
|
||||
class IDCSerializer(BulkSerializerMixin, serializers.ModelSerializer):
|
||||
|
|
|
@ -17,10 +17,13 @@ class LoginLog(models.Model):
|
|||
|
||||
username = models.CharField(max_length=20, verbose_name=_('Username'))
|
||||
name = models.CharField(max_length=20, blank=True, verbose_name=_('Name'))
|
||||
login_type = models.CharField(choices=LOGIN_TYPE_CHOICE, max_length=2, verbose_name=_('Login type'))
|
||||
login_type = models.CharField(choices=LOGIN_TYPE_CHOICE, max_length=2,
|
||||
verbose_name=_('Login type'))
|
||||
login_ip = models.GenericIPAddressField(verbose_name=_('Login ip'))
|
||||
login_city = models.CharField(max_length=100, blank=True, null=True, verbose_name=_('Login city'))
|
||||
user_agent = models.CharField(max_length=100, blank=True, null=True, verbose_name=_('User agent'))
|
||||
login_city = models.CharField(max_length=100, blank=True, null=True,
|
||||
verbose_name=_('Login city'))
|
||||
user_agent = models.CharField(max_length=100, blank=True, null=True,
|
||||
verbose_name=_('User agent'))
|
||||
date_login = models.DateTimeField(auto_now_add=True, verbose_name=_('Date login'))
|
||||
|
||||
class Meta:
|
||||
|
@ -66,7 +69,8 @@ class ProxyLog(models.Model):
|
|||
|
||||
|
||||
class CommandLog(models.Model):
|
||||
proxy_log = models.ForeignKey(ProxyLog, on_delete=models.CASCADE, related_name='commands')
|
||||
proxy_log = models.ForeignKey(ProxyLog, on_delete=models.CASCADE,
|
||||
related_name='commands')
|
||||
command_no = models.IntegerField()
|
||||
command = models.CharField(max_length=1000, blank=True)
|
||||
output = models.TextField(blank=True)
|
||||
|
@ -78,7 +82,8 @@ class CommandLog(models.Model):
|
|||
@property
|
||||
def output_decode(self):
|
||||
try:
|
||||
return base64.b64decode(self.output).replace('\n', '<br />')
|
||||
return base64.b64decode(self.output).decode('utf-8') \
|
||||
.replace('\n', '<br />')
|
||||
except UnicodeDecodeError:
|
||||
return 'UnicodeDecodeError'
|
||||
|
||||
|
|
|
@ -13,8 +13,9 @@ class ProxyLogSerializer(serializers.ModelSerializer):
|
|||
|
||||
class Meta:
|
||||
model = models.ProxyLog
|
||||
fields = ['id', 'name', 'username', 'hostname', 'ip', 'system_user', 'login_type', 'terminal',
|
||||
'log_file', 'was_failed', 'is_finished', 'date_start', 'date_finished', 'time',
|
||||
fields = ['id', 'name', 'username', 'hostname', 'ip', 'system_user',
|
||||
'login_type', 'terminal', 'log_file', 'was_failed',
|
||||
'is_finished', 'date_start', 'date_finished', 'time',
|
||||
'command_length', "commands_dict"]
|
||||
|
||||
@staticmethod
|
||||
|
@ -32,3 +33,4 @@ class ProxyLogSerializer(serializers.ModelSerializer):
|
|||
class CommandLogSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = models.CommandLog
|
||||
fields = '__all__'
|
||||
|
|
|
@ -83,7 +83,7 @@
|
|||
<td>{{ command.proxy_log.system_user }}</td>
|
||||
<td><a href="{% url 'audits:proxy-log-detail' pk=command.proxy_log.id %}">{{ command.proxy_log.id}}</a></td>
|
||||
<td>{{ command.datetime }}</td>
|
||||
<td>{{ command.output_decode |safe }}</td>
|
||||
<td>{{ command.output_decode|safe }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
|
|
|
@ -52,7 +52,7 @@
|
|||
<tr>
|
||||
<td>{{ command.command_no }}</td>
|
||||
<td>{{ command.command }}</td>
|
||||
<td>{{ command.output_decode |safe }}</td>
|
||||
<td>{{ command.output_decode|safe }}</td>
|
||||
<td>{{ command.datetime }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
|
@ -68,30 +68,6 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-sm-5" style="padding-left: 0;padding-right: 0">
|
||||
<div class="ibox float-e-margins">
|
||||
<div class="ibox-title">
|
||||
<span style="float: left">{% trans 'Detail' %} <b>{{ user_object.name }}</b></span>
|
||||
<div class="ibox-tools">
|
||||
<a class="collapse-link">
|
||||
<i class="fa fa-chevron-up"></i>
|
||||
</a>
|
||||
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
|
||||
<i class="fa fa-wrench"></i>
|
||||
</a>
|
||||
<ul class="dropdown-menu dropdown-user">
|
||||
</ul>
|
||||
<a class="close-link">
|
||||
<i class="fa fa-times"></i>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ibox-content">
|
||||
<table class="table2 table-stripped toggle-arrow-tiny" data-page-size="8">
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -14,17 +14,16 @@ from .models import ProxyLog, CommandLog, LoginLog
|
|||
from .hands import User, Asset, SystemUser, AdminUserRequiredMixin
|
||||
|
||||
|
||||
date_now = timezone.localtime(timezone.now())
|
||||
now_s = date_now.strftime('%m/%d/%Y')
|
||||
seven_days_ago_s = (date_now-timezone.timedelta(7)).strftime('%m/%d/%Y')
|
||||
|
||||
|
||||
class ProxyLogListView(AdminUserRequiredMixin, ListView):
|
||||
model = ProxyLog
|
||||
template_name = 'audits/proxy_log_list.html'
|
||||
context_object_name = 'proxy_log_list'
|
||||
|
||||
def get_queryset(self):
|
||||
date_now = timezone.localtime(timezone.now())
|
||||
now_s = date_now.strftime('%m/%d/%Y')
|
||||
seven_days_ago_s = (date_now-timezone.timedelta(7)).strftime('%m/%d/%Y')
|
||||
|
||||
self.queryset = super(ProxyLogListView, self).get_queryset()
|
||||
self.keyword = keyword = self.request.GET.get('keyword', '')
|
||||
self.username = username = self.request.GET.get('username', '')
|
||||
|
@ -37,7 +36,8 @@ class ProxyLogListView(AdminUserRequiredMixin, ListView):
|
|||
date_from = timezone.datetime.strptime(date_from_s, '%m/%d/%Y')
|
||||
self.queryset = self.queryset.filter(date_start__gt=date_from)
|
||||
if date_to_s:
|
||||
date_to = timezone.datetime.strptime(date_to_s + ' 23:59:59', '%m/%d/%Y %H:%M:%S')
|
||||
date_to = timezone.datetime.strptime(date_to_s + ' 23:59:59',
|
||||
'%m/%d/%Y %H:%M:%S')
|
||||
self.queryset = self.queryset.filter(date_start__lt=date_to)
|
||||
if username:
|
||||
self.queryset = self.queryset.filter(username=username)
|
||||
|
@ -54,7 +54,6 @@ class ProxyLogListView(AdminUserRequiredMixin, ListView):
|
|||
return self.queryset
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
print(self.date_to_s)
|
||||
context = {
|
||||
'app': _('Audits'),
|
||||
'action': _('Proxy log list'),
|
||||
|
@ -110,6 +109,9 @@ class CommandLogListView(AdminUserRequiredMixin, ListView):
|
|||
context_object_name = 'command_list'
|
||||
|
||||
def get_queryset(self):
|
||||
date_now = timezone.localtime(timezone.now())
|
||||
now_s = date_now.strftime('%m/%d/%Y')
|
||||
seven_days_ago_s = (date_now-timezone.timedelta(7)).strftime('%m/%d/%Y')
|
||||
self.queryset = super(CommandLogListView, self).get_queryset()
|
||||
self.keyword = keyword = self.request.GET.get('keyword', '')
|
||||
self.sort = sort = self.request.GET.get('sort', '-datetime')
|
||||
|
@ -161,6 +163,9 @@ class LoginLogListView(AdminUserRequiredMixin, ListView):
|
|||
context_object_name = 'login_log_list'
|
||||
|
||||
def get_queryset(self):
|
||||
date_now = timezone.localtime(timezone.now())
|
||||
now_s = date_now.strftime('%m/%d/%Y')
|
||||
seven_days_ago_s = (date_now - timezone.timedelta(7)).strftime('%m/%d/%Y')
|
||||
self.queryset = super(LoginLogListView, self).get_queryset()
|
||||
self.keyword = keyword = self.request.GET.get('keyword', '')
|
||||
self.username = username = self.request.GET.get('username', '')
|
||||
|
|
|
@ -16,5 +16,6 @@ app = Celery('jumpserver')
|
|||
# Using a string here means the worker will not have to
|
||||
# pickle the object when using Windows.
|
||||
app.config_from_object('django.conf:settings')
|
||||
app.autodiscover_tasks(lambda: [app_config.split('.')[0] for app_config in settings.INSTALLED_APPS])
|
||||
app.autodiscover_tasks(lambda: [app_config.split('.')[0]
|
||||
for app_config in settings.INSTALLED_APPS])
|
||||
|
||||
|
|
|
@ -1,16 +1,19 @@
|
|||
# ~*~ coding: utf-8 ~*~
|
||||
#
|
||||
|
||||
from django.shortcuts import get_object_or_404
|
||||
from rest_framework.views import APIView, Response
|
||||
from rest_framework.decorators import api_view
|
||||
from rest_framework.generics import ListAPIView, get_object_or_404
|
||||
from rest_framework import viewsets
|
||||
from users.permissions import IsValidUser, IsSuperUser
|
||||
from users.permissions import IsValidUser, IsSuperUser, IsAppUser
|
||||
from common.utils import get_object_or_none
|
||||
from .utils import get_user_granted_assets, get_user_granted_asset_groups, get_user_asset_permissions, \
|
||||
get_user_group_asset_permissions, get_user_group_granted_assets, get_user_group_granted_asset_groups
|
||||
from .utils import get_user_granted_assets, get_user_granted_asset_groups, \
|
||||
get_user_asset_permissions, get_user_group_asset_permissions, \
|
||||
get_user_group_granted_assets, get_user_group_granted_asset_groups
|
||||
from .models import AssetPermission
|
||||
from .hands import AssetGrantedSerializer, User, UserGroup, AssetGroup, Asset, AssetGroup, AssetGroupSerializer
|
||||
from .hands import AssetGrantedSerializer, User, UserGroup, AssetGroup, Asset, \
|
||||
AssetGroup, AssetGroupSerializer, SystemUser
|
||||
from . import serializers
|
||||
|
||||
|
||||
|
@ -80,11 +83,12 @@ class UserGrantedAssetsApi(ListAPIView):
|
|||
def get_queryset(self):
|
||||
user_id = self.kwargs.get('pk', '')
|
||||
|
||||
queryset = []
|
||||
if user_id:
|
||||
user = get_object_or_404(User, id=user_id)
|
||||
queryset = get_user_granted_assets(user)
|
||||
else:
|
||||
queryset = []
|
||||
for k, v in get_user_granted_assets(user).items():
|
||||
k.system_users_granted = v
|
||||
queryset.append(k)
|
||||
return queryset
|
||||
|
||||
|
||||
|
@ -104,19 +108,26 @@ class UserGrantedAssetGroupsApi(ListAPIView):
|
|||
|
||||
|
||||
class MyGrantedAssetsApi(ListAPIView):
|
||||
"""授权给用户的资产列表
|
||||
[{'hostname': 'x','ip': 'x', ..,
|
||||
'system_users_granted': [{'name': 'x', .}, ...]
|
||||
"""
|
||||
permission_classes = (IsValidUser,)
|
||||
serializer_class = AssetGrantedSerializer
|
||||
|
||||
def get_queryset(self):
|
||||
queryset = []
|
||||
user = self.request.user
|
||||
if user:
|
||||
queryset = get_user_granted_assets(user)
|
||||
else:
|
||||
queryset = []
|
||||
for asset, system_users in get_user_granted_assets(user).items():
|
||||
asset.system_users_granted = system_users
|
||||
queryset.append(asset)
|
||||
return queryset
|
||||
|
||||
|
||||
class MyGrantedAssetsGroupsApi(APIView):
|
||||
"""授权给用户的资产组列表, 非直接通过授权规则授权的资产组列表, 而是授权资产的所有
|
||||
资产组之和"""
|
||||
permission_classes = (IsValidUser,)
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
|
@ -141,6 +152,7 @@ class MyGrantedAssetsGroupsApi(APIView):
|
|||
|
||||
|
||||
class MyAssetGroupAssetsApi(ListAPIView):
|
||||
"""授权用户资产组下的资产列表, 非该资产组的所有资产,而是被授权的"""
|
||||
permission_classes = (IsValidUser,)
|
||||
serializer_class = AssetGrantedSerializer
|
||||
|
||||
|
@ -152,8 +164,9 @@ class MyAssetGroupAssetsApi(ListAPIView):
|
|||
|
||||
if user and asset_group:
|
||||
assets = get_user_granted_assets(user)
|
||||
for asset in assets:
|
||||
if asset_group in asset.groups.all():
|
||||
for asset in asset_group.assets.all():
|
||||
if asset in assets:
|
||||
asset.system_users_granted = assets[asset]
|
||||
queryset.append(asset)
|
||||
return queryset
|
||||
|
||||
|
@ -185,4 +198,24 @@ class UserGroupGrantedAssetGroupsApi(ListAPIView):
|
|||
queryset = get_user_group_granted_asset_groups(user_group)
|
||||
else:
|
||||
queryset = []
|
||||
return queryset
|
||||
return queryset
|
||||
|
||||
|
||||
class CheckUserAssetSystemPermission(APIView):
|
||||
permission_classes = (IsAppUser,)
|
||||
|
||||
def get(self, request):
|
||||
user_id = request.params.get('user_id', '')
|
||||
asset_id = request.params.get('asset_id', '')
|
||||
system_id = request.params.get('system_id', '')
|
||||
|
||||
user = get_object_or_none(User, id=user_id)
|
||||
asset = get_object_or_none(Asset, id=asset_id)
|
||||
system_user = get_object_or_none(SystemUser, id=system_id)
|
||||
|
||||
if not (user and asset and system_user):
|
||||
return Response(status=403)
|
||||
|
||||
assets_granted = get_user_granted_assets(user)
|
||||
|
||||
|
||||
|
|
|
@ -37,6 +37,8 @@ def get_user_group_granted_assets(user_group):
|
|||
if not asset_permission.is_valid:
|
||||
continue
|
||||
for asset in asset_permission.get_granted_assets():
|
||||
if not asset.is_active:
|
||||
continue
|
||||
if asset in assets:
|
||||
assets[asset] |= set(asset_permission.system_users.all())
|
||||
else:
|
||||
|
@ -127,6 +129,8 @@ def get_user_granted_assets_direct(user):
|
|||
if not asset_permission.is_valid:
|
||||
continue
|
||||
for asset in asset_permission.get_granted_assets():
|
||||
if not asset.is_active:
|
||||
continue
|
||||
if asset in assets:
|
||||
assets[asset] |= set(asset_permission.system_users.all())
|
||||
else:
|
||||
|
@ -147,12 +151,13 @@ def get_user_granted_assets_inherit_from_user_groups(user):
|
|||
for user_group in user_groups:
|
||||
assets_inherited = get_user_group_granted_assets(user_group)
|
||||
for asset in assets_inherited:
|
||||
if not asset.is_active:
|
||||
continue
|
||||
if asset in assets:
|
||||
assets[asset] |= assets_inherited[asset]
|
||||
else:
|
||||
setattr(asset, 'inherited', True)
|
||||
assets[asset] = assets_inherited[asset]
|
||||
|
||||
return assets
|
||||
|
||||
|
||||
|
@ -167,6 +172,8 @@ def get_user_granted_assets(user):
|
|||
assets = assets_inherited
|
||||
|
||||
for asset in assets_direct:
|
||||
if not asset.is_active:
|
||||
continue
|
||||
if asset in assets:
|
||||
assets[asset] |= assets_direct[asset]
|
||||
else:
|
||||
|
|
|
@ -3,23 +3,22 @@
|
|||
|
||||
import base64
|
||||
|
||||
from django.core.cache import cache
|
||||
from django.conf import settings
|
||||
from rest_framework import generics, viewsets
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework.decorators import api_view
|
||||
from rest_framework.permissions import AllowAny
|
||||
from rest_framework.authentication import SessionAuthentication
|
||||
from rest_framework_bulk import BulkModelViewSet
|
||||
from rest_framework.authentication import CSRFCheck
|
||||
# from django_filters.rest_framework import DjangoFilterBackend
|
||||
from django.conf import settings
|
||||
|
||||
from common.mixins import IDInFilterMixin
|
||||
from common.utils import get_logger
|
||||
from .utils import check_user_valid, generate_token
|
||||
from .models import User, UserGroup
|
||||
from .hands import write_login_log_async
|
||||
from .permissions import IsSuperUser, IsAppUser, IsValidUser, IsSuperUserOrAppUser
|
||||
from .permissions import (
|
||||
IsSuperUser, IsAppUser, IsValidUser, IsSuperUserOrAppUser)
|
||||
from . import serializers
|
||||
|
||||
|
||||
|
@ -98,8 +97,9 @@ class UserToken(APIView):
|
|||
password = request.data.get('password', '')
|
||||
public_key = request.data.get('public_key', '')
|
||||
|
||||
user, msg = check_user_valid(username=username, email=email,
|
||||
password=password, public_key=public_key)
|
||||
user, msg = check_user_valid(
|
||||
username=username, email=email,
|
||||
password=password, public_key=public_key)
|
||||
else:
|
||||
user = request.user
|
||||
msg = None
|
||||
|
@ -116,24 +116,29 @@ class UserProfile(APIView):
|
|||
def get(self, request):
|
||||
return Response(request.user.to_json())
|
||||
|
||||
def post(self, request):
|
||||
return Response(request.user.to_json())
|
||||
|
||||
|
||||
class UserAuthApi(APIView):
|
||||
permission_classes = (AllowAny,)
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
def post(self, request):
|
||||
username = request.data.get('username', '')
|
||||
password = request.data.get('password', '')
|
||||
public_key = request.data.get('public_key', '')
|
||||
login_type = request.data.get('login_type', '')
|
||||
login_ip = request.data.get('remote_addr', None) or request.META.get('REMOTE_ADDR', '')
|
||||
login_ip = request.data.get('remote_addr', None)
|
||||
user_agent = request.data.get('HTTP_USER_AGENT', '')
|
||||
|
||||
user, msg = check_user_valid(username=username, password=password, public_key=public_key)
|
||||
user, msg = check_user_valid(username=username, password=password,
|
||||
public_key=public_key)
|
||||
|
||||
if user:
|
||||
token = generate_token(request, user)
|
||||
write_login_log_async.delay(user.username, name=user.name, user_agent=user_agent,
|
||||
login_ip=login_ip, login_type=login_type)
|
||||
write_login_log_async.delay(user.username, name=user.name,
|
||||
user_agent=user_agent, login_ip=login_ip,
|
||||
login_type=login_type)
|
||||
return Response({'token': token, 'user': user.to_json()})
|
||||
else:
|
||||
return Response({'msg': msg}, status=401)
|
||||
|
|
|
@ -8,10 +8,11 @@ import time
|
|||
from django.core.cache import cache
|
||||
from django.conf import settings
|
||||
from django.utils.translation import ugettext as _
|
||||
from rest_framework import authentication, exceptions, permissions
|
||||
from django.utils.six import text_type
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from rest_framework import HTTP_HEADER_ENCODING
|
||||
from rest_framework import authentication, exceptions, permissions
|
||||
from rest_framework.authentication import CSRFCheck
|
||||
|
||||
from common.utils import get_object_or_none, make_signature, http_to_unixtime
|
||||
from .utils import refresh_token
|
||||
|
@ -27,6 +28,21 @@ def get_request_date_header(request):
|
|||
|
||||
|
||||
class AccessKeyAuthentication(authentication.BaseAuthentication):
|
||||
"""App使用Access key进行签名认证, 目前签名算法比较简单,
|
||||
app注册或者手动建立后,会生成 access_key_id 和 access_key_secret,
|
||||
然后使用 如下算法生成签名:
|
||||
Signature = md5(access_key_secret + '\n' + Date)
|
||||
example: Signature = md5('d32d2b8b-9a10-4b8d-85bb-1a66976f6fdc' + '\n' +
|
||||
'Thu, 12 Jan 2017 08:19:41 GMT')
|
||||
请求时设置请求header
|
||||
header['Authorization'] = 'Sign access_key_id:Signature' 如:
|
||||
header['Authorization'] =
|
||||
'Sign d32d2b8b-9a10-4b8d-85bb-1a66976f6fdc:OKOlmdxgYPZ9+SddnUUDbQ=='
|
||||
|
||||
验证时根据相同算法进行验证, 取到access_key_id对应的access_key_id, 从request
|
||||
headers取到Date, 然后进行md5, 判断得到的结果是否相同, 如果是认证通过, 否则 认证
|
||||
失败
|
||||
"""
|
||||
keyword = 'Sign'
|
||||
model = AccessKey
|
||||
|
||||
|
@ -40,22 +56,26 @@ class AccessKeyAuthentication(authentication.BaseAuthentication):
|
|||
msg = _('Invalid signature header. No credentials provided.')
|
||||
raise exceptions.AuthenticationFailed(msg)
|
||||
elif len(auth) > 2:
|
||||
msg = _('Invalid signature header. Signature string should not contain spaces.')
|
||||
msg = _('Invalid signature header. Signature '
|
||||
'string should not contain spaces.')
|
||||
raise exceptions.AuthenticationFailed(msg)
|
||||
|
||||
try:
|
||||
sign = auth[1].decode().split(':')
|
||||
if len(sign) != 2:
|
||||
msg = _('Invalid signature header. Format like AccessKeyId:Signature')
|
||||
msg = _('Invalid signature header. '
|
||||
'Format like AccessKeyId:Signature')
|
||||
raise exceptions.AuthenticationFailed(msg)
|
||||
except UnicodeError:
|
||||
msg = _('Invalid signature header. Signature string should not contain invalid characters.')
|
||||
msg = _('Invalid signature header. '
|
||||
'Signature string should not contain invalid characters.')
|
||||
raise exceptions.AuthenticationFailed(msg)
|
||||
|
||||
access_key_id = sign[0]
|
||||
request_signature = sign[1]
|
||||
|
||||
return self.authenticate_credentials(request, access_key_id, request_signature)
|
||||
return self.authenticate_credentials(
|
||||
request, access_key_id, request_signature)
|
||||
|
||||
@staticmethod
|
||||
def authenticate_credentials(request, access_key_id, request_signature):
|
||||
|
@ -68,14 +88,17 @@ class AccessKeyAuthentication(authentication.BaseAuthentication):
|
|||
try:
|
||||
request_unix_time = http_to_unixtime(request_date)
|
||||
except ValueError:
|
||||
raise exceptions.AuthenticationFailed(_('HTTP header: Date not provide or not %a, %d %b %Y %H:%M:%S GMT'))
|
||||
raise exceptions.AuthenticationFailed(
|
||||
_('HTTP header: Date not provide '
|
||||
'or not %a, %d %b %Y %H:%M:%S GMT'))
|
||||
|
||||
if int(time.time()) - request_unix_time > 15*60:
|
||||
raise exceptions.AuthenticationFailed(_('Expired, more than 15 minutes'))
|
||||
if int(time.time()) - request_unix_time > 15 * 60:
|
||||
raise exceptions.AuthenticationFailed(
|
||||
_('Expired, more than 15 minutes'))
|
||||
|
||||
signature = make_signature(access_key_secret, request_date)
|
||||
if not signature == request_signature:
|
||||
raise exceptions.AuthenticationFailed(_('Invalid signature. %s: %s' % (signature, request_signature)))
|
||||
raise exceptions.AuthenticationFailed(_('Invalid signature.'))
|
||||
|
||||
if not access_key.user.is_active:
|
||||
raise exceptions.AuthenticationFailed(_('User disabled.'))
|
||||
|
@ -97,13 +120,15 @@ class AccessTokenAuthentication(authentication.BaseAuthentication):
|
|||
msg = _('Invalid token header. No credentials provided.')
|
||||
raise exceptions.AuthenticationFailed(msg)
|
||||
elif len(auth) > 2:
|
||||
msg = _('Invalid token header. Sign string should not contain spaces.')
|
||||
msg = _('Invalid token header. Sign string '
|
||||
'should not contain spaces.')
|
||||
raise exceptions.AuthenticationFailed(msg)
|
||||
|
||||
try:
|
||||
token = auth[1].decode()
|
||||
except UnicodeError:
|
||||
msg = _('Invalid token header. Sign string should not contain invalid characters.')
|
||||
msg = _('Invalid token header. Sign string '
|
||||
'should not contain invalid characters.')
|
||||
raise exceptions.AuthenticationFailed(msg)
|
||||
return self.authenticate_credentials(token)
|
||||
|
||||
|
@ -125,4 +150,6 @@ class PrivateTokenAuthentication(authentication.TokenAuthentication):
|
|||
|
||||
class SessionAuthentication(authentication.SessionAuthentication):
|
||||
def enforce_csrf(self, request):
|
||||
return None
|
||||
reason = CSRFCheck().process_view(request, None, (), {})
|
||||
if reason:
|
||||
raise exceptions.AuthenticationFailed(reason)
|
||||
|
|
|
@ -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', related_name='access_key')
|
||||
user = models.ForeignKey(User, verbose_name='User',
|
||||
related_name='access_key')
|
||||
|
||||
def get_id(self):
|
||||
return str(self.id)
|
||||
|
|
|
@ -18,9 +18,12 @@ urlpatterns = [
|
|||
url(r'^v1/token/$', api.UserToken.as_view(), name='user-token'),
|
||||
url(r'^v1/profile/$', api.UserProfile.as_view(), name='user-profile'),
|
||||
url(r'^v1/auth/$', api.UserAuthApi.as_view(), name='user-auth'),
|
||||
url(r'^v1/users/(?P<pk>\d+)/password/reset/$', api.UserResetPasswordApi.as_view(), name='user-reset-password'),
|
||||
url(r'^v1/users/(?P<pk>\d+)/public-key/reset/$', api.UserResetPKApi.as_view(), name='user-public-key-reset'),
|
||||
url(r'^v1/users/(?P<pk>\d+)/public-key/update/$', api.UserUpdatePKApi.as_view(), name='user-public-key-update'),
|
||||
url(r'^v1/users/(?P<pk>\d+)/password/reset/$',
|
||||
api.UserResetPasswordApi.as_view(), name='user-reset-password'),
|
||||
url(r'^v1/users/(?P<pk>\d+)/public-key/reset/$',
|
||||
api.UserResetPKApi.as_view(), name='user-public-key-reset'),
|
||||
url(r'^v1/users/(?P<pk>\d+)/public-key/update/$',
|
||||
api.UserUpdatePKApi.as_view(), name='user-public-key-update'),
|
||||
url(r'^v1/users/(?P<pk>\d+)/groups/$',
|
||||
api.UserUpdateGroupApi.as_view(), name='user-update-group'),
|
||||
url(r'^v1/user-groups/(?P<pk>\d+)/users/$',
|
||||
|
|
|
@ -6,39 +6,72 @@ from .. import views
|
|||
app_name = 'users'
|
||||
|
||||
urlpatterns = [
|
||||
# Login view
|
||||
url(r'^login$', views.UserLoginView.as_view(), name='login'),
|
||||
url(r'^logout$', views.UserLogoutView.as_view(), name='logout'),
|
||||
url(r'^password/forgot$', views.UserForgotPasswordView.as_view(), name='forgot-password'),
|
||||
url(r'^password/forgot$', views.UserForgotPasswordView.as_view(),
|
||||
name='forgot-password'),
|
||||
url(r'^password/forgot/sendmail-success$',
|
||||
views.UserForgotPasswordSendmailSuccessView.as_view(), name='forgot-password-sendmail-success'),
|
||||
url(r'^password/reset$', views.UserResetPasswordView.as_view(), name='reset-password'),
|
||||
url(r'^password/reset/success$', views.UserResetPasswordSuccessView.as_view(),
|
||||
views.UserForgotPasswordSendmailSuccessView.as_view(),
|
||||
name='forgot-password-sendmail-success'),
|
||||
url(r'^password/reset$', views.UserResetPasswordView.as_view(),
|
||||
name='reset-password'),
|
||||
url(r'^password/reset/success$',
|
||||
views.UserResetPasswordSuccessView.as_view(),
|
||||
name='reset-password-success'),
|
||||
|
||||
# User view
|
||||
url(r'^user$', views.UserListView.as_view(), name='user-list'),
|
||||
url(r'^user/(?P<pk>[0-9]+)$', views.UserDetailView.as_view(), name='user-detail'),
|
||||
url(r'^user/(?P<pk>[0-9]+)/asset-permission$', views.UserAssetPermissionView.as_view(),
|
||||
url(r'^user/(?P<pk>[0-9]+)$', views.UserDetailView.as_view(),
|
||||
name='user-detail'),
|
||||
url(r'^user/(?P<pk>[0-9]+)/asset-permission$',
|
||||
views.UserAssetPermissionView.as_view(),
|
||||
name='user-asset-permission'),
|
||||
url(r'^user/(?P<pk>[0-9]+)/asset-permission/create$', views.UserAssetPermissionCreateView.as_view(),
|
||||
url(r'^user/(?P<pk>[0-9]+)/asset-permission/create$',
|
||||
views.UserAssetPermissionCreateView.as_view(),
|
||||
name='user-asset-permission-create'),
|
||||
url(r'^user/(?P<pk>[0-9]+)/assets', views.UserGrantedAssetView.as_view(), name='user-granted-asset'),
|
||||
url(r'^user/(?P<pk>[0-9]+)/login-history', views.UserDetailView.as_view(), name='user-login-history'),
|
||||
url(r'^user/export/', views.UserExportView.as_view(), name='user-export'),
|
||||
url(r'^first-login/$', views.UserFirstLoginView.as_view(), name='user-first-login'),
|
||||
url(r'^user/import/$', views.BulkImportUserView.as_view(), name='user-import'),
|
||||
# url(r'^user/(?P<pk>[0-9]+)/assets-perm$', views.UserDetailView.as_view(), name='user-detail'),
|
||||
url(r'^user/create$', views.UserCreateView.as_view(), name='user-create'),
|
||||
url(r'^user/(?P<pk>[0-9]+)/update$', views.UserUpdateView.as_view(), name='user-update'),
|
||||
url(r'^user/(?P<pk>[0-9]+)/assets',
|
||||
views.UserGrantedAssetView.as_view(),
|
||||
name='user-granted-asset'),
|
||||
url(r'^user/(?P<pk>[0-9]+)/login-history',
|
||||
views.UserDetailView.as_view(),
|
||||
name='user-login-history'),
|
||||
url(r'^user/export/',
|
||||
views.UserExportView.as_view(),
|
||||
name='user-export'),
|
||||
url(r'^first-login/$',
|
||||
views.UserFirstLoginView.as_view(),
|
||||
name='user-first-login'),
|
||||
url(r'^user/import/$',
|
||||
views.UserBulkImportView.as_view(),
|
||||
name='user-import'),
|
||||
url(r'^user/create$',
|
||||
views.UserCreateView.as_view(),
|
||||
name='user-create'),
|
||||
url(r'^user/(?P<pk>[0-9]+)/update$',
|
||||
views.UserUpdateView.as_view(),
|
||||
name='user-update'),
|
||||
|
||||
# User group view
|
||||
url(r'^user-group$', views.UserGroupListView.as_view(), name='user-group-list'),
|
||||
url(r'^user-group/(?P<pk>[0-9]+)$', views.UserGroupDetailView.as_view(), name='user-group-detail'),
|
||||
url(r'^user-group/create$', views.UserGroupCreateView.as_view(), name='user-group-create'),
|
||||
url(r'^user-group/(?P<pk>[0-9]+)/update$', views.UserGroupUpdateView.as_view(), name='user-group-update'),
|
||||
url(r'^user-group/(?P<pk>[0-9]+)/asset-permission$', views.UserGroupAssetPermissionView.as_view(),
|
||||
url(r'^user-group$',
|
||||
views.UserGroupListView.as_view(),
|
||||
name='user-group-list'),
|
||||
url(r'^user-group/(?P<pk>[0-9]+)$',
|
||||
views.UserGroupDetailView.as_view(),
|
||||
name='user-group-detail'),
|
||||
url(r'^user-group/create$',
|
||||
views.UserGroupCreateView.as_view(),
|
||||
name='user-group-create'),
|
||||
url(r'^user-group/(?P<pk>[0-9]+)/update$',
|
||||
views.UserGroupUpdateView.as_view(),
|
||||
name='user-group-update'),
|
||||
url(r'^user-group/(?P<pk>[0-9]+)/asset-permission$',
|
||||
views.UserGroupAssetPermissionView.as_view(),
|
||||
name='user-group-asset-permission'),
|
||||
url(r'^user-group/(?P<pk>[0-9]+)/asset-permission/create$', views.UserGroupAssetPermissionCreateView.as_view(),
|
||||
url(r'^user-group/(?P<pk>[0-9]+)/asset-permission/create$',
|
||||
views.UserGroupAssetPermissionCreateView.as_view(),
|
||||
name='user-group-asset-permission-create'),
|
||||
url(r'^user-group/(?P<pk>[0-9]+)/assets', views.UserGroupGrantedAssetView.as_view(),
|
||||
url(r'^user-group/(?P<pk>[0-9]+)/assets',
|
||||
views.UserGroupGrantedAssetView.as_view(),
|
||||
name='user-group-granted-asset'),
|
||||
]
|
||||
|
|
|
@ -1,593 +0,0 @@
|
|||
# ~*~ coding: utf-8 ~*~
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import json
|
||||
import uuid
|
||||
|
||||
from openpyxl import Workbook
|
||||
from openpyxl.writer.excel import save_virtual_workbook
|
||||
from openpyxl import load_workbook
|
||||
from django import forms
|
||||
from django.utils import timezone
|
||||
from django.core.cache import cache
|
||||
from django.db import IntegrityError
|
||||
from django.contrib.auth import login as auth_login, logout as auth_logout
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.contrib.messages.views import SuccessMessageMixin
|
||||
from django.core.files.storage import default_storage
|
||||
from django.http import HttpResponseRedirect, HttpResponse, JsonResponse
|
||||
from django.shortcuts import reverse, redirect
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.urls import reverse_lazy
|
||||
from django.views import View
|
||||
from django.views.decorators.cache import never_cache
|
||||
from django.views.decorators.csrf import csrf_protect, csrf_exempt
|
||||
from django.views.decorators.debug import sensitive_post_parameters
|
||||
from django.views.generic.base import TemplateView
|
||||
from django.views.generic.list import ListView
|
||||
from django.views.generic.edit import CreateView, DeleteView, UpdateView, FormView, SingleObjectMixin, FormMixin
|
||||
from django.views.generic.detail import DetailView
|
||||
from formtools.wizard.views import SessionWizardView
|
||||
|
||||
from common.mixins import JSONResponseMixin
|
||||
from common.utils import get_object_or_none, get_logger
|
||||
from perms.models import AssetPermission
|
||||
from .models import User, UserGroup
|
||||
from .utils import AdminUserRequiredMixin, user_add_success_next, send_reset_password_mail
|
||||
from .hands import write_login_log_async
|
||||
from . import forms
|
||||
|
||||
logger = get_logger(__name__)
|
||||
|
||||
|
||||
@method_decorator(sensitive_post_parameters(), name='dispatch')
|
||||
@method_decorator(csrf_protect, name='dispatch')
|
||||
@method_decorator(never_cache, name='dispatch')
|
||||
class UserLoginView(FormView):
|
||||
template_name = 'users/login.html'
|
||||
form_class = forms.UserLoginForm
|
||||
redirect_field_name = 'next'
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
if request.user.is_staff:
|
||||
return redirect(self.get_success_url())
|
||||
return super(UserLoginView, self).get(request, *args, **kwargs)
|
||||
|
||||
def form_valid(self, form):
|
||||
auth_login(self.request, form.get_user())
|
||||
login_ip = self.request.META.get('REMOTE_ADDR', '')
|
||||
user_agent = self.request.META.get('HTTP_USER_AGENT', '')
|
||||
write_login_log_async.delay(self.request.user.username, self.request.user.name,
|
||||
login_type='W', login_ip=login_ip, user_agent=user_agent)
|
||||
return redirect(self.get_success_url())
|
||||
|
||||
def get_success_url(self):
|
||||
if self.request.user.is_first_login:
|
||||
return reverse('users:user-first-login')
|
||||
|
||||
return self.request.POST.get(
|
||||
self.redirect_field_name,
|
||||
self.request.GET.get(self.redirect_field_name, reverse('index')))
|
||||
|
||||
|
||||
@method_decorator(never_cache, name='dispatch')
|
||||
class UserLogoutView(TemplateView):
|
||||
template_name = 'flash_message_standalone.html'
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
auth_logout(request)
|
||||
return super(UserLogoutView, self).get(request)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = {
|
||||
'title': _('Logout success'),
|
||||
'messages': _('Logout success, return login page'),
|
||||
'redirect_url': reverse('users:login'),
|
||||
'auto_redirect': True,
|
||||
}
|
||||
kwargs.update(context)
|
||||
return super(UserLogoutView, self).get_context_data(**kwargs)
|
||||
|
||||
|
||||
class UserListView(AdminUserRequiredMixin, TemplateView):
|
||||
template_name = 'users/user_list.html'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(UserListView, self).get_context_data(**kwargs)
|
||||
context.update({
|
||||
'app': _('Users'),
|
||||
'action': _('User list'),
|
||||
'groups': UserGroup.objects.all()
|
||||
})
|
||||
return context
|
||||
|
||||
|
||||
class UserCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateView):
|
||||
model = User
|
||||
form_class = forms.UserCreateUpdateForm
|
||||
template_name = 'users/user_create.html'
|
||||
success_url = reverse_lazy('users:user-list')
|
||||
success_message = _('Create user <a href="%s">%s</a> successfully.')
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(UserCreateView, self).get_context_data(**kwargs)
|
||||
context.update({'app': _('Users'), 'action': _('Create user')})
|
||||
return context
|
||||
|
||||
def form_valid(self, form):
|
||||
user = form.save(commit=False)
|
||||
user.created_by = self.request.user.username or 'System'
|
||||
user.save()
|
||||
user_add_success_next(user)
|
||||
return super(UserCreateView, self).form_valid(form)
|
||||
|
||||
def get_success_message(self, cleaned_data):
|
||||
return self.success_message % (
|
||||
reverse_lazy('users:user-detail', kwargs={'pk': self.object.pk}),
|
||||
self.object.name,
|
||||
)
|
||||
|
||||
|
||||
class UserUpdateView(AdminUserRequiredMixin, UpdateView):
|
||||
model = User
|
||||
form_class = forms.UserCreateUpdateForm
|
||||
template_name = 'users/user_update.html'
|
||||
context_object_name = 'user_object'
|
||||
success_url = reverse_lazy('users:user-list')
|
||||
|
||||
def form_valid(self, form):
|
||||
username = self.object.username
|
||||
user = form.save(commit=False)
|
||||
user.username = username
|
||||
user.save()
|
||||
password = self.request.POST.get('password', '')
|
||||
if password:
|
||||
user.set_password(password)
|
||||
return super(UserUpdateView, self).form_valid(form)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(UserUpdateView, self).get_context_data(**kwargs)
|
||||
context.update({'app': _('Users'), 'action': _('Update user')})
|
||||
return context
|
||||
|
||||
|
||||
class UserDetailView(AdminUserRequiredMixin, DetailView):
|
||||
model = User
|
||||
template_name = 'users/user_detail.html'
|
||||
context_object_name = "user"
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
groups = UserGroup.objects.exclude(id__in=self.object.groups.all())
|
||||
context = {'app': _('Users'), 'action': _('User detail'), 'groups': groups}
|
||||
kwargs.update(context)
|
||||
return super(UserDetailView, self).get_context_data(**kwargs)
|
||||
|
||||
|
||||
class UserGroupListView(AdminUserRequiredMixin, TemplateView):
|
||||
template_name = 'users/user_group_list.html'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(UserGroupListView, self).get_context_data(**kwargs)
|
||||
context.update({'app': _('Users'), 'action': _('User group list')})
|
||||
return context
|
||||
|
||||
|
||||
class UserGroupCreateView(AdminUserRequiredMixin, CreateView):
|
||||
model = UserGroup
|
||||
form_class = forms.UserGroupForm
|
||||
template_name = 'users/user_group_create_update.html'
|
||||
success_url = reverse_lazy('users:user-group-list')
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(UserGroupCreateView, self).get_context_data(**kwargs)
|
||||
users = User.objects.all()
|
||||
context.update({'app': _('Users'), 'action': _('Create user group'), 'users': users})
|
||||
return context
|
||||
|
||||
def form_valid(self, form):
|
||||
user_group = form.save()
|
||||
users_id_list = self.request.POST.getlist('users', [])
|
||||
users = User.objects.filter(id__in=users_id_list)
|
||||
user_group.created_by = self.request.user.username or 'Admin'
|
||||
user_group.users.add(*users)
|
||||
user_group.save()
|
||||
return super(UserGroupCreateView, self).form_valid(form)
|
||||
|
||||
|
||||
class UserGroupUpdateView(AdminUserRequiredMixin, UpdateView):
|
||||
model = UserGroup
|
||||
form_class = forms.UserGroupForm
|
||||
template_name = 'users/user_group_create_update.html'
|
||||
success_url = reverse_lazy('users:user-group-list')
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
# self.object = self.get_object()
|
||||
context = super(UserGroupUpdateView, self).get_context_data(**kwargs)
|
||||
users = User.objects.all()
|
||||
group_users = [user.id for user in self.object.users.all()]
|
||||
context.update({
|
||||
'app': _('Users'),
|
||||
'action': _('Update User Group'),
|
||||
'users': users,
|
||||
'group_users': group_users
|
||||
})
|
||||
return context
|
||||
|
||||
def form_valid(self, form):
|
||||
user_group = form.save()
|
||||
users_id_list = self.request.POST.getlist('users', [])
|
||||
users = User.objects.filter(id__in=users_id_list)
|
||||
user_group.users.clear()
|
||||
user_group.users.add(*users)
|
||||
user_group.save()
|
||||
return super(UserGroupUpdateView, self).form_valid(form)
|
||||
|
||||
|
||||
class UserGroupDetailView(AdminUserRequiredMixin, DetailView):
|
||||
model = UserGroup
|
||||
context_object_name = 'user_group'
|
||||
template_name = 'users/user_group_detail.html'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
users = User.objects.exclude(id__in=self.object.users.all())
|
||||
context = {
|
||||
'app': _('Users'),
|
||||
'action': _('User Group Detail'),
|
||||
'users': users,
|
||||
}
|
||||
kwargs.update(context)
|
||||
return super(UserGroupDetailView, self).get_context_data(**kwargs)
|
||||
|
||||
|
||||
class UserForgotPasswordView(TemplateView):
|
||||
template_name = 'users/forgot_password.html'
|
||||
|
||||
def post(self, request):
|
||||
email = request.POST.get('email')
|
||||
user = get_object_or_none(User, email=email)
|
||||
if not user:
|
||||
return self.get(request, errors=_('Email address invalid, input again'))
|
||||
else:
|
||||
send_reset_password_mail(user)
|
||||
return HttpResponseRedirect(reverse('users:forgot-password-sendmail-success'))
|
||||
|
||||
|
||||
class UserForgotPasswordSendmailSuccessView(TemplateView):
|
||||
template_name = 'flash_message_standalone.html'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = {
|
||||
'title': _('Send reset password message'),
|
||||
'messages': _('Send reset password mail success, login your mail box and follow it '),
|
||||
'redirect_url': reverse('users:login'),
|
||||
}
|
||||
kwargs.update(context)
|
||||
return super(UserForgotPasswordSendmailSuccessView, self).get_context_data(**kwargs)
|
||||
|
||||
|
||||
class UserResetPasswordSuccessView(TemplateView):
|
||||
template_name = 'flash_message_standalone.html'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = {
|
||||
'title': _('Reset password success'),
|
||||
'messages': _('Reset password success, return to login page'),
|
||||
'redirect_url': reverse('users:login'),
|
||||
'auto_redirect': True,
|
||||
}
|
||||
kwargs.update(context)
|
||||
return super(UserResetPasswordSuccessView, self).get_context_data(**kwargs)
|
||||
|
||||
|
||||
class UserResetPasswordView(TemplateView):
|
||||
template_name = 'users/reset_password.html'
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
token = request.GET.get('token')
|
||||
user = User.validate_reset_token(token)
|
||||
|
||||
if not user:
|
||||
kwargs.update({'errors': _('Token invalid or expired')})
|
||||
return super(UserResetPasswordView, self).get(request, *args, **kwargs)
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
password = request.POST.get('password')
|
||||
password_confirm = request.POST.get('password-confirm')
|
||||
token = request.GET.get('token')
|
||||
|
||||
if password != password_confirm:
|
||||
return self.get(request, errors=_('Password not same'))
|
||||
|
||||
user = User.validate_reset_token(token)
|
||||
if not user:
|
||||
return self.get(request, errors=_('Token invalid or expired'))
|
||||
|
||||
user.reset_password(password)
|
||||
return HttpResponseRedirect(reverse('users:reset-password-success'))
|
||||
|
||||
|
||||
class UserFirstLoginView(LoginRequiredMixin, SessionWizardView):
|
||||
template_name = 'users/first_login.html'
|
||||
form_list = [forms.UserInfoForm, forms.UserKeyForm]
|
||||
file_storage = default_storage
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
if request.user.is_authenticated() and not request.user.is_first_login:
|
||||
return redirect(reverse('index'))
|
||||
return super(UserFirstLoginView, self).dispatch(request, *args, **kwargs)
|
||||
|
||||
def done(self, form_list, form_dict, **kwargs):
|
||||
user = self.request.user
|
||||
for form in form_list:
|
||||
for field in form:
|
||||
if field.value():
|
||||
setattr(user, field.name, field.value())
|
||||
if field.name == 'enable_otp':
|
||||
user.enable_otp = field.value()
|
||||
user.is_first_login = False
|
||||
user.is_public_key_valid = True
|
||||
user.save()
|
||||
return redirect(reverse('index'))
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(UserFirstLoginView, self).get_context_data(**kwargs)
|
||||
context.update({'app': _('Users'), 'action': _('First Login')})
|
||||
return context
|
||||
|
||||
def get_form_initial(self, step):
|
||||
user = self.request.user
|
||||
if step == '0':
|
||||
return {
|
||||
'name': user.name or user.username,
|
||||
'enable_otp': user.enable_otp or True,
|
||||
'wechat': user.wechat or '',
|
||||
'phone': user.phone or ''
|
||||
}
|
||||
return super(UserFirstLoginView, self).get_form_initial(step)
|
||||
|
||||
def get_form(self, step=None, data=None, files=None):
|
||||
form = super(UserFirstLoginView, self).get_form(step, data, files)
|
||||
|
||||
if step is None:
|
||||
step = self.steps.current
|
||||
|
||||
if step == '1':
|
||||
form.user = self.request.user
|
||||
return form
|
||||
|
||||
|
||||
class UserAssetPermissionView(AdminUserRequiredMixin, FormMixin, SingleObjectMixin, ListView):
|
||||
model = User
|
||||
template_name = 'users/user_asset_permission.html'
|
||||
context_object_name = 'user'
|
||||
form_class = forms.UserPrivateAssetPermissionForm
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
self.object = self.get_object(queryset=User.objects.all())
|
||||
return super(UserAssetPermissionView, self).get(request, *args, **kwargs)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = {
|
||||
'app': 'Users',
|
||||
'action': 'User asset permissions',
|
||||
}
|
||||
kwargs.update(context)
|
||||
return super(UserAssetPermissionView, self).get_context_data(**kwargs)
|
||||
|
||||
|
||||
class UserGroupAssetPermissionView(AdminUserRequiredMixin, FormMixin, SingleObjectMixin, ListView):
|
||||
model = UserGroup
|
||||
template_name = 'users/user_group_asset_permission.html'
|
||||
context_object_name = 'user_group'
|
||||
form_class = forms.UserPrivateAssetPermissionForm
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
self.object = self.get_object(queryset=UserGroup.objects.all())
|
||||
return super(UserGroupAssetPermissionView, self).get(request, *args, **kwargs)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = {
|
||||
'app': 'Users',
|
||||
'action': 'User group asset permissions',
|
||||
}
|
||||
kwargs.update(context)
|
||||
return super(UserGroupAssetPermissionView, self).get_context_data(**kwargs)
|
||||
|
||||
|
||||
class UserAssetPermissionCreateView(AdminUserRequiredMixin, CreateView):
|
||||
form_class = forms.UserPrivateAssetPermissionForm
|
||||
model = AssetPermission
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
user = self.get_object(queryset=User.objects.all())
|
||||
return redirect(reverse('users:user-asset-permission', kwargs={'pk': user.id}))
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
self.user = self.get_object(queryset=User.objects.all())
|
||||
return super(UserAssetPermissionCreateView, self).post(request, *args, **kwargs)
|
||||
|
||||
def get_form(self, form_class=None):
|
||||
form = super(UserAssetPermissionCreateView, self).get_form(form_class=form_class)
|
||||
form.user = self.user
|
||||
return form
|
||||
|
||||
def form_invalid(self, form):
|
||||
return redirect(reverse('users:user-asset-permission', kwargs={'pk': self.user.id}))
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse('users:user-asset-permission', kwargs={'pk': self.user.id})
|
||||
|
||||
|
||||
class UserGroupAssetPermissionCreateView(AdminUserRequiredMixin, CreateView):
|
||||
form_class = forms.UserGroupPrivateAssetPermissionForm
|
||||
model = AssetPermission
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
user_group = self.get_object(queryset=UserGroup.objects.all())
|
||||
return redirect(reverse('users:user-group-asset-permission', kwargs={'pk': user_group.id}))
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
self.user_group = self.get_object(queryset=UserGroup.objects.all())
|
||||
return super(UserGroupAssetPermissionCreateView, self).post(request, *args, **kwargs)
|
||||
|
||||
def get_form(self, form_class=None):
|
||||
form = super(UserGroupAssetPermissionCreateView, self).get_form(form_class=form_class)
|
||||
form.user_group = self.user_group
|
||||
return form
|
||||
|
||||
def form_invalid(self, form):
|
||||
return redirect(reverse('users:user-group-asset-permission', kwargs={'pk': self.user_group.id}))
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse('users:user-group-asset-permission', kwargs={'pk': self.user_group.id})
|
||||
|
||||
|
||||
class UserGrantedAssetView(AdminUserRequiredMixin, DetailView):
|
||||
model = User
|
||||
template_name = 'users/user_granted_asset.html'
|
||||
context_object_name = 'user'
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
self.object = self.get_object(queryset=User.objects.all())
|
||||
return super(UserGrantedAssetView, self).get(request, *args, **kwargs)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = {
|
||||
'app': 'User',
|
||||
'action': 'User granted asset',
|
||||
}
|
||||
kwargs.update(context)
|
||||
return super(UserGrantedAssetView, self).get_context_data(**kwargs)
|
||||
|
||||
|
||||
class UserGroupGrantedAssetView(AdminUserRequiredMixin, DetailView):
|
||||
model = User
|
||||
template_name = 'users/user_group_granted_asset.html'
|
||||
context_object_name = 'user_group'
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
self.object = self.get_object(queryset=UserGroup.objects.all())
|
||||
return super(UserGroupGrantedAssetView, self).get(request, *args, **kwargs)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = {
|
||||
'app': 'User',
|
||||
'action': 'User group granted asset',
|
||||
}
|
||||
kwargs.update(context)
|
||||
return super(UserGroupGrantedAssetView, self).get_context_data(**kwargs)
|
||||
|
||||
|
||||
class BulkImportUserView(AdminUserRequiredMixin, JSONResponseMixin, FormView):
|
||||
form_class = forms.FileForm
|
||||
|
||||
def form_invalid(self, form):
|
||||
try:
|
||||
error = form.errors.values()[-1][-1]
|
||||
except Exception as e:
|
||||
print e
|
||||
error = _('Invalid file.')
|
||||
data = {
|
||||
'success': False,
|
||||
'msg': error
|
||||
}
|
||||
return self.render_json_response(data)
|
||||
|
||||
def form_valid(self, form):
|
||||
try:
|
||||
wb = load_workbook(form.cleaned_data['file'])
|
||||
ws = wb.get_active_sheet()
|
||||
except Exception as e:
|
||||
print(e)
|
||||
data = {'valid': False, 'msg': 'Not a valid Excel file'}
|
||||
return self.render_json_response(data)
|
||||
|
||||
rows = ws.rows
|
||||
header_need = ["name", 'username', 'email', 'groups', "role", "phone", "wechat", "comment"]
|
||||
header = [col.value for col in next(rows)]
|
||||
print(header)
|
||||
if header != header_need:
|
||||
data = {'valid': False, 'msg': 'Must be same format as template or export file'}
|
||||
return self.render_json_response(data)
|
||||
|
||||
created = []
|
||||
updated = []
|
||||
failed = []
|
||||
for row in rows:
|
||||
user_dict = dict(zip(header, [col.value for col in row]))
|
||||
groups_name = user_dict.pop('groups')
|
||||
if groups_name:
|
||||
groups_name = groups_name.split(',')
|
||||
groups = UserGroup.objects.filter(name__in=groups_name)
|
||||
else:
|
||||
groups = None
|
||||
try:
|
||||
user = User.objects.create(**user_dict)
|
||||
user_add_success_next(user)
|
||||
created.append(user_dict['username'])
|
||||
except IntegrityError as e:
|
||||
user = User.objects.filter(username=user_dict['username'])
|
||||
if not user:
|
||||
failed.append(user_dict['username'])
|
||||
continue
|
||||
user.update(**user_dict)
|
||||
user = user[0]
|
||||
updated.append(user_dict['username'])
|
||||
except TypeError as e:
|
||||
print(e)
|
||||
failed.append(user_dict['username'])
|
||||
user = None
|
||||
|
||||
if user and groups:
|
||||
user.groups.add(*tuple(groups))
|
||||
user.save()
|
||||
|
||||
data = {
|
||||
'created': created,
|
||||
'created_info': 'Created {}'.format(len(created)),
|
||||
'updated': updated,
|
||||
'updated_info': 'Updated {}'.format(len(updated)),
|
||||
'failed': failed,
|
||||
'failed_info': 'Failed {}'.format(len(failed)),
|
||||
'valid': True,
|
||||
'msg': 'Created: {}. Updated: {}, Error: {}'.format(len(created), len(updated), len(failed))
|
||||
}
|
||||
return self.render_json_response(data)
|
||||
|
||||
|
||||
@method_decorator(csrf_exempt, name='dispatch')
|
||||
class UserExportView(View):
|
||||
def get(self, request, *args, **kwargs):
|
||||
spm = request.GET.get('spm', '')
|
||||
users_id = cache.get(spm)
|
||||
if not users_id and not isinstance(users_id, list):
|
||||
return HttpResponse('May be expired', status=404)
|
||||
|
||||
users = User.objects.filter(id__in=users_id)
|
||||
wb = Workbook()
|
||||
ws = wb.active
|
||||
ws.title = 'User'
|
||||
header = ["name", 'username', 'email', 'groups', "role", "phone", "wechat", "comment"]
|
||||
ws.append(header)
|
||||
|
||||
for user in users:
|
||||
ws.append([user.name, user.username, user.email,
|
||||
','.join([group.name for group in user.groups.all()]),
|
||||
user.role, user.phone, user.wechat, user.comment])
|
||||
|
||||
filename = 'users-{}.xlsx'.format(timezone.localtime(timezone.now()).strftime('%Y-%m-%d_%H-%M-%S'))
|
||||
response = HttpResponse(save_virtual_workbook(wb), content_type='applications/vnd.ms-excel')
|
||||
response['Content-Disposition'] = 'attachment; filename="%s"' % filename
|
||||
return response
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
try:
|
||||
users_id = json.loads(request.body).get('users_id', [])
|
||||
except ValueError:
|
||||
return HttpResponse('Json object not valid', status=400)
|
||||
spm = uuid.uuid4().get_hex()
|
||||
cache.set(spm, users_id, 300)
|
||||
url = reverse('users:user-export') + '?spm=%s' % spm
|
||||
return JsonResponse({'redirect': url})
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
# ~*~ coding: utf-8 ~*~
|
||||
|
||||
from .login import *
|
||||
from .user import *
|
||||
from .group import *
|
|
@ -0,0 +1,169 @@
|
|||
# ~*~ coding: utf-8 ~*~
|
||||
|
||||
from __future__ import unicode_literals
|
||||
from django import forms
|
||||
from django.shortcuts import reverse, redirect
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.urls import reverse_lazy
|
||||
from django.views.generic import ListView
|
||||
from django.views.generic.base import TemplateView
|
||||
from django.views.generic.edit import CreateView, UpdateView, FormMixin
|
||||
from django.views.generic.detail import DetailView, SingleObjectMixin
|
||||
|
||||
from common.utils import get_logger
|
||||
from perms.models import AssetPermission
|
||||
from ..models import User, UserGroup
|
||||
from ..utils import AdminUserRequiredMixin
|
||||
from .. import forms
|
||||
|
||||
__all__ = ['UserGroupListView', 'UserGroupCreateView', 'UserGroupDetailView',
|
||||
'UserGroupUpdateView', 'UserGroupAssetPermissionCreateView',
|
||||
'UserGroupAssetPermissionView', 'UserGroupGrantedAssetView']
|
||||
logger = get_logger(__name__)
|
||||
|
||||
|
||||
class UserGroupListView(AdminUserRequiredMixin, TemplateView):
|
||||
template_name = 'users/user_group_list.html'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(UserGroupListView, self).get_context_data(**kwargs)
|
||||
context.update({'app': _('Users'), 'action': _('User group list')})
|
||||
return context
|
||||
|
||||
|
||||
class UserGroupCreateView(AdminUserRequiredMixin, CreateView):
|
||||
model = UserGroup
|
||||
form_class = forms.UserGroupForm
|
||||
template_name = 'users/user_group_create_update.html'
|
||||
success_url = reverse_lazy('users:user-group-list')
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(UserGroupCreateView, self).get_context_data(**kwargs)
|
||||
users = User.objects.all()
|
||||
context.update({'app': _('Users'), 'action': _('Create user group'),
|
||||
'users': users})
|
||||
return context
|
||||
|
||||
def form_valid(self, form):
|
||||
user_group = form.save()
|
||||
users_id_list = self.request.POST.getlist('users', [])
|
||||
users = User.objects.filter(id__in=users_id_list)
|
||||
user_group.created_by = self.request.user.username or 'Admin'
|
||||
user_group.users.add(*users)
|
||||
user_group.save()
|
||||
return super(UserGroupCreateView, self).form_valid(form)
|
||||
|
||||
|
||||
class UserGroupUpdateView(AdminUserRequiredMixin, UpdateView):
|
||||
model = UserGroup
|
||||
form_class = forms.UserGroupForm
|
||||
template_name = 'users/user_group_create_update.html'
|
||||
success_url = reverse_lazy('users:user-group-list')
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
# self.object = self.get_object()
|
||||
context = super(UserGroupUpdateView, self).get_context_data(**kwargs)
|
||||
users = User.objects.all()
|
||||
group_users = [user.id for user in self.object.users.all()]
|
||||
context.update({
|
||||
'app': _('Users'),
|
||||
'action': _('Update User Group'),
|
||||
'users': users,
|
||||
'group_users': group_users
|
||||
})
|
||||
return context
|
||||
|
||||
def form_valid(self, form):
|
||||
user_group = form.save()
|
||||
users_id_list = self.request.POST.getlist('users', [])
|
||||
users = User.objects.filter(id__in=users_id_list)
|
||||
user_group.users.clear()
|
||||
user_group.users.add(*users)
|
||||
user_group.save()
|
||||
return super(UserGroupUpdateView, self).form_valid(form)
|
||||
|
||||
|
||||
class UserGroupDetailView(AdminUserRequiredMixin, DetailView):
|
||||
model = UserGroup
|
||||
context_object_name = 'user_group'
|
||||
template_name = 'users/user_group_detail.html'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
users = User.objects.exclude(id__in=self.object.users.all())
|
||||
context = {
|
||||
'app': _('Users'),
|
||||
'action': _('User Group Detail'),
|
||||
'users': users,
|
||||
}
|
||||
kwargs.update(context)
|
||||
return super(UserGroupDetailView, self).get_context_data(**kwargs)
|
||||
|
||||
|
||||
class UserGroupAssetPermissionView(AdminUserRequiredMixin, FormMixin,
|
||||
SingleObjectMixin, ListView):
|
||||
model = UserGroup
|
||||
template_name = 'users/user_group_asset_permission.html'
|
||||
context_object_name = 'user_group'
|
||||
form_class = forms.UserPrivateAssetPermissionForm
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
self.object = self.get_object(queryset=UserGroup.objects.all())
|
||||
return super(UserGroupAssetPermissionView, self)\
|
||||
.get(request, *args, **kwargs)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = {
|
||||
'app': 'Users',
|
||||
'action': 'User group asset permissions',
|
||||
}
|
||||
kwargs.update(context)
|
||||
return super(UserGroupAssetPermissionView, self)\
|
||||
.get_context_data(**kwargs)
|
||||
|
||||
|
||||
class UserGroupAssetPermissionCreateView(AdminUserRequiredMixin, CreateView):
|
||||
form_class = forms.UserGroupPrivateAssetPermissionForm
|
||||
model = AssetPermission
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
user_group = self.get_object(queryset=UserGroup.objects.all())
|
||||
return redirect(reverse('users:user-group-asset-permission',
|
||||
kwargs={'pk': user_group.id}))
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
self.user_group = self.get_object(queryset=UserGroup.objects.all())
|
||||
return super(UserGroupAssetPermissionCreateView, self)\
|
||||
.post(request, *args, **kwargs)
|
||||
|
||||
def get_form(self, form_class=None):
|
||||
form = super(UserGroupAssetPermissionCreateView, self)\
|
||||
.get_form(form_class=form_class)
|
||||
form.user_group = self.user_group
|
||||
return form
|
||||
|
||||
def form_invalid(self, form):
|
||||
return redirect(reverse('users:user-group-asset-permission',
|
||||
kwargs={'pk': self.user_group.id}))
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse('users:user-group-asset-permission',
|
||||
kwargs={'pk': self.user_group.id})
|
||||
|
||||
|
||||
class UserGroupGrantedAssetView(AdminUserRequiredMixin, DetailView):
|
||||
model = User
|
||||
template_name = 'users/user_group_granted_asset.html'
|
||||
context_object_name = 'user_group'
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
self.object = self.get_object(queryset=UserGroup.objects.all())
|
||||
return super(UserGroupGrantedAssetView, self)\
|
||||
.get(request, *args, **kwargs)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = {
|
||||
'app': 'User',
|
||||
'action': 'User group granted asset',
|
||||
}
|
||||
kwargs.update(context)
|
||||
return super(UserGroupGrantedAssetView, self).get_context_data(**kwargs)
|
|
@ -0,0 +1,200 @@
|
|||
# ~*~ coding: utf-8 ~*~
|
||||
|
||||
from __future__ import unicode_literals
|
||||
from django import forms
|
||||
from django.contrib.auth import login as auth_login, logout as auth_logout
|
||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||
from django.core.files.storage import default_storage
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.shortcuts import reverse, redirect
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.views.decorators.cache import never_cache
|
||||
from django.views.decorators.csrf import csrf_protect
|
||||
from django.views.decorators.debug import sensitive_post_parameters
|
||||
from django.views.generic.base import TemplateView
|
||||
from django.views.generic.edit import FormView
|
||||
from formtools.wizard.views import SessionWizardView
|
||||
|
||||
from common.utils import get_object_or_none
|
||||
from ..models import User
|
||||
from ..utils import send_reset_password_mail
|
||||
from ..hands import write_login_log_async
|
||||
from .. import forms
|
||||
|
||||
|
||||
__all__ = ['UserLoginView', 'UserLogoutView',
|
||||
'UserForgotPasswordView', 'UserForgotPasswordSendmailSuccessView',
|
||||
'UserResetPasswordView', 'UserResetPasswordSuccessView',
|
||||
'UserFirstLoginView']
|
||||
|
||||
|
||||
@method_decorator(sensitive_post_parameters(), name='dispatch')
|
||||
@method_decorator(csrf_protect, name='dispatch')
|
||||
@method_decorator(never_cache, name='dispatch')
|
||||
class UserLoginView(FormView):
|
||||
template_name = 'users/login.html'
|
||||
form_class = forms.UserLoginForm
|
||||
redirect_field_name = 'next'
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
if request.user.is_staff:
|
||||
return redirect(self.get_success_url())
|
||||
return super(UserLoginView, self).get(request, *args, **kwargs)
|
||||
|
||||
def form_valid(self, form):
|
||||
auth_login(self.request, form.get_user())
|
||||
login_ip = self.request.META.get('REMOTE_ADDR', '')
|
||||
user_agent = self.request.META.get('HTTP_USER_AGENT', '')
|
||||
write_login_log_async.delay(self.request.user.username,
|
||||
self.request.user.name,
|
||||
login_type='W', login_ip=login_ip,
|
||||
user_agent=user_agent)
|
||||
return redirect(self.get_success_url())
|
||||
|
||||
def get_success_url(self):
|
||||
if self.request.user.is_first_login:
|
||||
return reverse('users:user-first-login')
|
||||
|
||||
return self.request.POST.get(
|
||||
self.redirect_field_name,
|
||||
self.request.GET.get(self.redirect_field_name, reverse('index')))
|
||||
|
||||
|
||||
@method_decorator(never_cache, name='dispatch')
|
||||
class UserLogoutView(TemplateView):
|
||||
template_name = 'flash_message_standalone.html'
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
auth_logout(request)
|
||||
return super(UserLogoutView, self).get(request)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = {
|
||||
'title': _('Logout success'),
|
||||
'messages': _('Logout success, return login page'),
|
||||
'redirect_url': reverse('users:login'),
|
||||
'auto_redirect': True,
|
||||
}
|
||||
kwargs.update(context)
|
||||
return super(UserLogoutView, self).get_context_data(**kwargs)
|
||||
|
||||
|
||||
class UserForgotPasswordView(TemplateView):
|
||||
template_name = 'users/forgot_password.html'
|
||||
|
||||
def post(self, request):
|
||||
email = request.POST.get('email')
|
||||
user = get_object_or_none(User, email=email)
|
||||
if not user:
|
||||
return self.get(request, errors=_('Email address invalid, input again'))
|
||||
else:
|
||||
send_reset_password_mail(user)
|
||||
return HttpResponseRedirect(
|
||||
reverse('users:forgot-password-sendmail-success'))
|
||||
|
||||
|
||||
class UserForgotPasswordSendmailSuccessView(TemplateView):
|
||||
template_name = 'flash_message_standalone.html'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = {
|
||||
'title': _('Send reset password message'),
|
||||
'messages': _('Send reset password mail success, '
|
||||
'login your mail box and follow it '),
|
||||
'redirect_url': reverse('users:login'),
|
||||
}
|
||||
kwargs.update(context)
|
||||
return super(UserForgotPasswordSendmailSuccessView, self)\
|
||||
.get_context_data(**kwargs)
|
||||
|
||||
|
||||
class UserResetPasswordSuccessView(TemplateView):
|
||||
template_name = 'flash_message_standalone.html'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = {
|
||||
'title': _('Reset password success'),
|
||||
'messages': _('Reset password success, return to login page'),
|
||||
'redirect_url': reverse('users:login'),
|
||||
'auto_redirect': True,
|
||||
}
|
||||
kwargs.update(context)
|
||||
return super(UserResetPasswordSuccessView, self).get_context_data(**kwargs)
|
||||
|
||||
|
||||
class UserResetPasswordView(TemplateView):
|
||||
template_name = 'users/reset_password.html'
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
token = request.GET.get('token')
|
||||
user = User.validate_reset_token(token)
|
||||
|
||||
if not user:
|
||||
kwargs.update({'errors': _('Token invalid or expired')})
|
||||
return super(UserResetPasswordView, self).get(request, *args, **kwargs)
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
password = request.POST.get('password')
|
||||
password_confirm = request.POST.get('password-confirm')
|
||||
token = request.GET.get('token')
|
||||
|
||||
if password != password_confirm:
|
||||
return self.get(request, errors=_('Password not same'))
|
||||
|
||||
user = User.validate_reset_token(token)
|
||||
if not user:
|
||||
return self.get(request, errors=_('Token invalid or expired'))
|
||||
|
||||
user.reset_password(password)
|
||||
return HttpResponseRedirect(reverse('users:reset-password-success'))
|
||||
|
||||
|
||||
class UserFirstLoginView(LoginRequiredMixin, SessionWizardView):
|
||||
template_name = 'users/first_login.html'
|
||||
form_list = [forms.UserInfoForm, forms.UserKeyForm]
|
||||
file_storage = default_storage
|
||||
|
||||
def dispatch(self, request, *args, **kwargs):
|
||||
if request.user.is_authenticated() and not request.user.is_first_login:
|
||||
return redirect(reverse('index'))
|
||||
return super(UserFirstLoginView, self).dispatch(request, *args, **kwargs)
|
||||
|
||||
def done(self, form_list, form_dict, **kwargs):
|
||||
user = self.request.user
|
||||
for form in form_list:
|
||||
for field in form:
|
||||
if field.value():
|
||||
setattr(user, field.name, field.value())
|
||||
if field.name == 'enable_otp':
|
||||
user.enable_otp = field.value()
|
||||
user.is_first_login = False
|
||||
user.is_public_key_valid = True
|
||||
user.save()
|
||||
return redirect(reverse('index'))
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(UserFirstLoginView, self).get_context_data(**kwargs)
|
||||
context.update({'app': _('Users'), 'action': _('First Login')})
|
||||
return context
|
||||
|
||||
def get_form_initial(self, step):
|
||||
user = self.request.user
|
||||
if step == '0':
|
||||
return {
|
||||
'name': user.name or user.username,
|
||||
'enable_otp': user.enable_otp or True,
|
||||
'wechat': user.wechat or '',
|
||||
'phone': user.phone or ''
|
||||
}
|
||||
return super(UserFirstLoginView, self).get_form_initial(step)
|
||||
|
||||
def get_form(self, step=None, data=None, files=None):
|
||||
form = super(UserFirstLoginView, self).get_form(step, data, files)
|
||||
|
||||
if step is None:
|
||||
step = self.steps.current
|
||||
|
||||
if step == '1':
|
||||
form.user = self.request.user
|
||||
return form
|
|
@ -0,0 +1,300 @@
|
|||
# ~*~ coding: utf-8 ~*~
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import uuid
|
||||
import json
|
||||
|
||||
from django.shortcuts import redirect
|
||||
from openpyxl import Workbook
|
||||
from openpyxl.writer.excel import save_virtual_workbook
|
||||
from openpyxl import load_workbook
|
||||
from django import forms
|
||||
from django.core.cache import cache
|
||||
from django.http import HttpResponse, JsonResponse
|
||||
from django.contrib.messages.views import SuccessMessageMixin
|
||||
from django.urls import reverse_lazy, reverse
|
||||
from django.utils import timezone
|
||||
from django.utils.translation import ugettext as _
|
||||
from django.utils.decorators import method_decorator
|
||||
from django.views import View
|
||||
from django.views.generic import ListView
|
||||
from django.views.generic.base import TemplateView
|
||||
from django.views.generic.edit import CreateView, UpdateView, FormMixin, \
|
||||
FormView
|
||||
from django.views.generic.detail import DetailView, SingleObjectMixin
|
||||
from django.views.decorators.csrf import csrf_exempt
|
||||
|
||||
from common.mixins import JSONResponseMixin
|
||||
from common.utils import get_logger
|
||||
from perms.models import AssetPermission
|
||||
from ..models import User, UserGroup
|
||||
from ..utils import AdminUserRequiredMixin, user_add_success_next
|
||||
from .. import forms
|
||||
|
||||
__all__ = ['UserListView', 'UserCreateView', 'UserDetailView',
|
||||
'UserUpdateView', 'UserAssetPermissionCreateView',
|
||||
'UserAssetPermissionView', 'UserGrantedAssetView',
|
||||
'UserExportView', 'UserBulkImportView']
|
||||
logger = get_logger(__name__)
|
||||
|
||||
|
||||
class UserListView(AdminUserRequiredMixin, TemplateView):
|
||||
template_name = 'users/user_list.html'
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(UserListView, self).get_context_data(**kwargs)
|
||||
context.update({
|
||||
'app': _('Users'),
|
||||
'action': _('User list'),
|
||||
'groups': UserGroup.objects.all()
|
||||
})
|
||||
return context
|
||||
|
||||
|
||||
class UserCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateView):
|
||||
model = User
|
||||
form_class = forms.UserCreateUpdateForm
|
||||
template_name = 'users/user_create.html'
|
||||
success_url = reverse_lazy('users:user-list')
|
||||
success_message = _('Create user <a href="%s">%s</a> successfully.')
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(UserCreateView, self).get_context_data(**kwargs)
|
||||
context.update({'app': _('Users'), 'action': _('Create user')})
|
||||
return context
|
||||
|
||||
def form_valid(self, form):
|
||||
user = form.save(commit=False)
|
||||
user.created_by = self.request.user.username or 'System'
|
||||
user.save()
|
||||
user_add_success_next(user)
|
||||
return super(UserCreateView, self).form_valid(form)
|
||||
|
||||
def get_success_message(self, cleaned_data):
|
||||
return self.success_message % (
|
||||
reverse_lazy('users:user-detail', kwargs={'pk': self.object.pk}),
|
||||
self.object.name,
|
||||
)
|
||||
|
||||
|
||||
class UserUpdateView(AdminUserRequiredMixin, UpdateView):
|
||||
model = User
|
||||
form_class = forms.UserCreateUpdateForm
|
||||
template_name = 'users/user_update.html'
|
||||
context_object_name = 'user_object'
|
||||
success_url = reverse_lazy('users:user-list')
|
||||
|
||||
def form_valid(self, form):
|
||||
username = self.object.username
|
||||
user = form.save(commit=False)
|
||||
user.username = username
|
||||
user.save()
|
||||
password = self.request.POST.get('password', '')
|
||||
if password:
|
||||
user.set_password(password)
|
||||
return super(UserUpdateView, self).form_valid(form)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = super(UserUpdateView, self).get_context_data(**kwargs)
|
||||
context.update({'app': _('Users'), 'action': _('Update user')})
|
||||
return context
|
||||
|
||||
|
||||
class UserDetailView(AdminUserRequiredMixin, DetailView):
|
||||
model = User
|
||||
template_name = 'users/user_detail.html'
|
||||
context_object_name = "user"
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
groups = UserGroup.objects.exclude(id__in=self.object.groups.all())
|
||||
context = {
|
||||
'app': _('Users'),
|
||||
'action': _('User detail'),
|
||||
'groups': groups
|
||||
}
|
||||
kwargs.update(context)
|
||||
return super(UserDetailView, self).get_context_data(**kwargs)
|
||||
|
||||
|
||||
@method_decorator(csrf_exempt, name='dispatch')
|
||||
class UserExportView(View):
|
||||
def get(self, request, *args, **kwargs):
|
||||
spm = request.GET.get('spm', '')
|
||||
users_id = cache.get(spm)
|
||||
if not users_id and not isinstance(users_id, list):
|
||||
return HttpResponse('May be expired', status=404)
|
||||
|
||||
users = User.objects.filter(id__in=users_id)
|
||||
wb = Workbook()
|
||||
ws = wb.active
|
||||
ws.title = 'User'
|
||||
header = ["name", 'username', 'email', 'groups',
|
||||
"role", "phone", "wechat", "comment"]
|
||||
ws.append(header)
|
||||
|
||||
for user in users:
|
||||
ws.append([user.name, user.username, user.email,
|
||||
','.join([group.name for group in user.groups.all()]),
|
||||
user.role, user.phone, user.wechat, user.comment])
|
||||
|
||||
filename = 'users-{}.xlsx'.format(
|
||||
timezone.localtime(timezone.now()).strftime('%Y-%m-%d_%H-%M-%S'))
|
||||
response = HttpResponse(save_virtual_workbook(wb),
|
||||
content_type='applications/vnd.ms-excel')
|
||||
response['Content-Disposition'] = 'attachment; filename="%s"' % filename
|
||||
return response
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
try:
|
||||
users_id = json.loads(request.body).get('users_id', [])
|
||||
except ValueError:
|
||||
return HttpResponse('Json object not valid', status=400)
|
||||
spm = uuid.uuid4().get_hex()
|
||||
cache.set(spm, users_id, 300)
|
||||
url = reverse('users:user-export') + '?spm=%s' % spm
|
||||
return JsonResponse({'redirect': url})
|
||||
|
||||
|
||||
class UserBulkImportView(AdminUserRequiredMixin, JSONResponseMixin, FormView):
|
||||
form_class = forms.FileForm
|
||||
|
||||
def form_invalid(self, form):
|
||||
try:
|
||||
error = form.errors.values()[-1][-1]
|
||||
except Exception as e:
|
||||
print e
|
||||
error = _('Invalid file.')
|
||||
data = {
|
||||
'success': False,
|
||||
'msg': error
|
||||
}
|
||||
return self.render_json_response(data)
|
||||
|
||||
def form_valid(self, form):
|
||||
try:
|
||||
wb = load_workbook(form.cleaned_data['file'])
|
||||
ws = wb.get_active_sheet()
|
||||
except Exception as e:
|
||||
print(e)
|
||||
data = {'valid': False, 'msg': 'Not a valid Excel file'}
|
||||
return self.render_json_response(data)
|
||||
|
||||
rows = ws.rows
|
||||
header_need = ["name", 'username', 'email', 'groups', "role", "phone", "wechat", "comment"]
|
||||
header = [col.value for col in next(rows)]
|
||||
print(header)
|
||||
if header != header_need:
|
||||
data = {'valid': False, 'msg': 'Must be same format as template or export file'}
|
||||
return self.render_json_response(data)
|
||||
|
||||
created = []
|
||||
updated = []
|
||||
failed = []
|
||||
for row in rows:
|
||||
user_dict = dict(zip(header, [col.value for col in row]))
|
||||
groups_name = user_dict.pop('groups')
|
||||
if groups_name:
|
||||
groups_name = groups_name.split(',')
|
||||
groups = UserGroup.objects.filter(name__in=groups_name)
|
||||
else:
|
||||
groups = None
|
||||
try:
|
||||
user = User.objects.create(**user_dict)
|
||||
user_add_success_next(user)
|
||||
created.append(user_dict['username'])
|
||||
except User.IntegrityError as e:
|
||||
user = User.objects.filter(username=user_dict['username'])
|
||||
if not user:
|
||||
failed.append(user_dict['username'])
|
||||
continue
|
||||
user.update(**user_dict)
|
||||
user = user[0]
|
||||
updated.append(user_dict['username'])
|
||||
except TypeError as e:
|
||||
print(e)
|
||||
failed.append(user_dict['username'])
|
||||
user = None
|
||||
|
||||
if user and groups:
|
||||
user.groups.add(*tuple(groups))
|
||||
user.save()
|
||||
|
||||
data = {
|
||||
'created': created,
|
||||
'created_info': 'Created {}'.format(len(created)),
|
||||
'updated': updated,
|
||||
'updated_info': 'Updated {}'.format(len(updated)),
|
||||
'failed': failed,
|
||||
'failed_info': 'Failed {}'.format(len(failed)),
|
||||
'valid': True,
|
||||
'msg': 'Created: {}. Updated: {}, Error: {}'.format(
|
||||
len(created), len(updated), len(failed))
|
||||
}
|
||||
return self.render_json_response(data)
|
||||
|
||||
|
||||
class UserAssetPermissionView(AdminUserRequiredMixin, FormMixin,
|
||||
SingleObjectMixin, ListView):
|
||||
model = User
|
||||
template_name = 'users/user_asset_permission.html'
|
||||
context_object_name = 'user'
|
||||
form_class = forms.UserPrivateAssetPermissionForm
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
self.object = self.get_object(queryset=User.objects.all())
|
||||
return super(UserAssetPermissionView, self).get(request, *args, **kwargs)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = {
|
||||
'app': 'Users',
|
||||
'action': 'User asset permissions',
|
||||
}
|
||||
kwargs.update(context)
|
||||
return super(UserAssetPermissionView, self).get_context_data(**kwargs)
|
||||
|
||||
|
||||
class UserAssetPermissionCreateView(AdminUserRequiredMixin, CreateView):
|
||||
form_class = forms.UserPrivateAssetPermissionForm
|
||||
model = AssetPermission
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
user = self.get_object(queryset=User.objects.all())
|
||||
return redirect(reverse('users:user-asset-permission',
|
||||
kwargs={'pk': user.id}))
|
||||
|
||||
def post(self, request, *args, **kwargs):
|
||||
self.user = self.get_object(queryset=User.objects.all())
|
||||
return super(UserAssetPermissionCreateView, self)\
|
||||
.post(request, *args, **kwargs)
|
||||
|
||||
def get_form(self, form_class=None):
|
||||
form = super(UserAssetPermissionCreateView, self)\
|
||||
.get_form(form_class=form_class)
|
||||
form.user = self.user
|
||||
return form
|
||||
|
||||
def form_invalid(self, form):
|
||||
return redirect(reverse('users:user-asset-permission',
|
||||
kwargs={'pk': self.user.id}))
|
||||
|
||||
def get_success_url(self):
|
||||
return reverse('users:user-asset-permission',
|
||||
kwargs={'pk': self.user.id})
|
||||
|
||||
|
||||
class UserGrantedAssetView(AdminUserRequiredMixin, DetailView):
|
||||
model = User
|
||||
template_name = 'users/user_granted_asset.html'
|
||||
context_object_name = 'user'
|
||||
|
||||
def get(self, request, *args, **kwargs):
|
||||
self.object = self.get_object(queryset=User.objects.all())
|
||||
return super(UserGrantedAssetView, self).get(request, *args, **kwargs)
|
||||
|
||||
def get_context_data(self, **kwargs):
|
||||
context = {
|
||||
'app': 'User',
|
||||
'action': 'User granted asset',
|
||||
}
|
||||
kwargs.update(context)
|
||||
return super(UserGrantedAssetView, self).get_context_data(**kwargs)
|
Loading…
Reference in New Issue