diff --git a/apps/applications/serializers.py b/apps/applications/serializers.py
index 31d67418d..56f05e67c 100644
--- a/apps/applications/serializers.py
+++ b/apps/applications/serializers.py
@@ -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
diff --git a/apps/assets/serializers.py b/apps/assets/serializers.py
index 87fec5767..a9c9c41a8 100644
--- a/apps/assets/serializers.py
+++ b/apps/assets/serializers.py
@@ -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):
diff --git a/apps/audits/models.py b/apps/audits/models.py
index 7e8cbeb03..a6b1c1133 100644
--- a/apps/audits/models.py
+++ b/apps/audits/models.py
@@ -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', '
')
+ return base64.b64decode(self.output).decode('utf-8') \
+ .replace('\n', '
')
except UnicodeDecodeError:
return 'UnicodeDecodeError'
diff --git a/apps/audits/serializers.py b/apps/audits/serializers.py
index 4d02ef5b4..7212c0d97 100644
--- a/apps/audits/serializers.py
+++ b/apps/audits/serializers.py
@@ -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__'
diff --git a/apps/audits/templates/audits/command_log_list.html b/apps/audits/templates/audits/command_log_list.html
index 03b786f3f..5e0cf0752 100644
--- a/apps/audits/templates/audits/command_log_list.html
+++ b/apps/audits/templates/audits/command_log_list.html
@@ -83,7 +83,7 @@
{{ command.proxy_log.system_user }} |
{{ command.proxy_log.id}} |
{{ command.datetime }} |
- {{ command.output_decode |safe }} |
+ {{ command.output_decode|safe }} |
{% endfor %}
diff --git a/apps/audits/templates/audits/proxy_log_detail.html b/apps/audits/templates/audits/proxy_log_detail.html
index 8d2724586..05b06071c 100644
--- a/apps/audits/templates/audits/proxy_log_detail.html
+++ b/apps/audits/templates/audits/proxy_log_detail.html
@@ -52,7 +52,7 @@
{{ command.command_no }} |
{{ command.command }} |
- {{ command.output_decode |safe }} |
+ {{ command.output_decode|safe }} |
{{ command.datetime }} |
{% endfor %}
@@ -68,30 +68,6 @@
-
-
-
-
{% trans 'Detail' %} {{ user_object.name }}
-
-
-
-
-
diff --git a/apps/audits/views.py b/apps/audits/views.py
index 61fb1ff97..0c0c7cf55 100644
--- a/apps/audits/views.py
+++ b/apps/audits/views.py
@@ -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', '')
diff --git a/apps/common/celery.py b/apps/common/celery.py
index f4ea048e5..51de45349 100644
--- a/apps/common/celery.py
+++ b/apps/common/celery.py
@@ -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])
diff --git a/apps/perms/api.py b/apps/perms/api.py
index 7b50c4a9d..ce0193b89 100644
--- a/apps/perms/api.py
+++ b/apps/perms/api.py
@@ -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
\ No newline at end of file
+ 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)
+
+
diff --git a/apps/perms/utils.py b/apps/perms/utils.py
index 585b65808..f01ae1779 100644
--- a/apps/perms/utils.py
+++ b/apps/perms/utils.py
@@ -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:
diff --git a/apps/users/api.py b/apps/users/api.py
index a6c2c24b5..393a5f15e 100644
--- a/apps/users/api.py
+++ b/apps/users/api.py
@@ -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)
diff --git a/apps/users/authentication.py b/apps/users/authentication.py
index db58b0ed4..203a07e5c 100644
--- a/apps/users/authentication.py
+++ b/apps/users/authentication.py
@@ -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
\ No newline at end of file
+ reason = CSRFCheck().process_view(request, None, (), {})
+ if reason:
+ raise exceptions.AuthenticationFailed(reason)
diff --git a/apps/users/models/authentication.py b/apps/users/models/authentication.py
index a0351e466..917bc1114 100644
--- a/apps/users/models/authentication.py
+++ b/apps/users/models/authentication.py
@@ -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)
diff --git a/apps/users/urls/api_urls.py b/apps/users/urls/api_urls.py
index 212837aff..eaae47556 100644
--- a/apps/users/urls/api_urls.py
+++ b/apps/users/urls/api_urls.py
@@ -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\d+)/password/reset/$', api.UserResetPasswordApi.as_view(), name='user-reset-password'),
- url(r'^v1/users/(?P\d+)/public-key/reset/$', api.UserResetPKApi.as_view(), name='user-public-key-reset'),
- url(r'^v1/users/(?P\d+)/public-key/update/$', api.UserUpdatePKApi.as_view(), name='user-public-key-update'),
+ url(r'^v1/users/(?P\d+)/password/reset/$',
+ api.UserResetPasswordApi.as_view(), name='user-reset-password'),
+ url(r'^v1/users/(?P\d+)/public-key/reset/$',
+ api.UserResetPKApi.as_view(), name='user-public-key-reset'),
+ url(r'^v1/users/(?P\d+)/public-key/update/$',
+ api.UserUpdatePKApi.as_view(), name='user-public-key-update'),
url(r'^v1/users/(?P\d+)/groups/$',
api.UserUpdateGroupApi.as_view(), name='user-update-group'),
url(r'^v1/user-groups/(?P\d+)/users/$',
diff --git a/apps/users/urls/views_urls.py b/apps/users/urls/views_urls.py
index 4741fde60..58c531765 100644
--- a/apps/users/urls/views_urls.py
+++ b/apps/users/urls/views_urls.py
@@ -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[0-9]+)$', views.UserDetailView.as_view(), name='user-detail'),
- url(r'^user/(?P[0-9]+)/asset-permission$', views.UserAssetPermissionView.as_view(),
+ url(r'^user/(?P[0-9]+)$', views.UserDetailView.as_view(),
+ name='user-detail'),
+ url(r'^user/(?P[0-9]+)/asset-permission$',
+ views.UserAssetPermissionView.as_view(),
name='user-asset-permission'),
- url(r'^user/(?P[0-9]+)/asset-permission/create$', views.UserAssetPermissionCreateView.as_view(),
+ url(r'^user/(?P[0-9]+)/asset-permission/create$',
+ views.UserAssetPermissionCreateView.as_view(),
name='user-asset-permission-create'),
- url(r'^user/(?P[0-9]+)/assets', views.UserGrantedAssetView.as_view(), name='user-granted-asset'),
- url(r'^user/(?P[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[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[0-9]+)/update$', views.UserUpdateView.as_view(), name='user-update'),
+ url(r'^user/(?P[0-9]+)/assets',
+ views.UserGrantedAssetView.as_view(),
+ name='user-granted-asset'),
+ url(r'^user/(?P[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[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[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[0-9]+)/update$', views.UserGroupUpdateView.as_view(), name='user-group-update'),
- url(r'^user-group/(?P[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[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[0-9]+)/update$',
+ views.UserGroupUpdateView.as_view(),
+ name='user-group-update'),
+ url(r'^user-group/(?P[0-9]+)/asset-permission$',
+ views.UserGroupAssetPermissionView.as_view(),
name='user-group-asset-permission'),
- url(r'^user-group/(?P[0-9]+)/asset-permission/create$', views.UserGroupAssetPermissionCreateView.as_view(),
+ url(r'^user-group/(?P[0-9]+)/asset-permission/create$',
+ views.UserGroupAssetPermissionCreateView.as_view(),
name='user-group-asset-permission-create'),
- url(r'^user-group/(?P[0-9]+)/assets', views.UserGroupGrantedAssetView.as_view(),
+ url(r'^user-group/(?P[0-9]+)/assets',
+ views.UserGroupGrantedAssetView.as_view(),
name='user-group-granted-asset'),
]
diff --git a/apps/users/views.py b/apps/users/views.py
deleted file mode 100644
index c568a4b90..000000000
--- a/apps/users/views.py
+++ /dev/null
@@ -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 %s 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})
-
diff --git a/apps/users/views/__init__.py b/apps/users/views/__init__.py
new file mode 100644
index 000000000..b178ac710
--- /dev/null
+++ b/apps/users/views/__init__.py
@@ -0,0 +1,5 @@
+# ~*~ coding: utf-8 ~*~
+
+from .login import *
+from .user import *
+from .group import *
diff --git a/apps/users/views/group.py b/apps/users/views/group.py
new file mode 100644
index 000000000..48232b72a
--- /dev/null
+++ b/apps/users/views/group.py
@@ -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)
diff --git a/apps/users/views/login.py b/apps/users/views/login.py
new file mode 100644
index 000000000..b81d06f1b
--- /dev/null
+++ b/apps/users/views/login.py
@@ -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
diff --git a/apps/users/views/user.py b/apps/users/views/user.py
new file mode 100644
index 000000000..8a2df396e
--- /dev/null
+++ b/apps/users/views/user.py
@@ -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 %s 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)
\ No newline at end of file