超级管理员可创建超级审计员并可设置审计员为组织审计员 (#3141)

* [Update] 超级管理员可创建超级审计员并可设置审计员为组织审计员

* [Update] 修改小问题

* [Update] 修改普通用户角色可以是组织审计员

* [Update] 更改组织审计员切换组织问题

* [Update] 修改小问题

* [Update] 普通用户是组织审计员的页面左侧栏显示

* [Update] 修改删除权限问题和组织显示问题

* [Update] 优化逻辑

* [Update] 优化类名

* [Update] 修改小问题

* [Update] 优化逻辑

* [Update] 优化切换到某一个组织逻辑

* [Update] 修改用户详情页的 删除/更新 按钮是否可点击

* [Update] 优化代码

* [Update] 组织管理列表增加审计员显示

* [Update] 优化代码细节

* [Update] 优化权限类逻辑

* [Update] 优化导航菜单控制

* [Update] 优化页面控制逻辑

* [Update] 修改变量名错误问题

* [Update] 修改页面上的小问题

* [Update] 审计员或组织审计员能够更新个人部分信息

* [Update] 用户名为admin的用户不能被删除

* [Update] 不同用户在不同组织下扮演不同角色的权限不同,为了避免切换组织时出现403,重定向到index

* [Update] 一个用户在同一个组织既是管理员又是审计员,隐藏个人信息模块,仅当是审计员,在当前组织显示个人信息模块

* [Update] 修改方法命名

* [Update] 优化代码细节

* [Update] 修改命令执行列表方法

* [Update] 优化用户之间操作的权限逻辑;添加 UserModel 的 property 属性;修改 Organization 的 related name 名称;

* [Update] 修改OrgProcessor Anonymous问题

* [Update] 修改用户序列类校验组织和转换raw密码的逻辑
pull/3224/head
八千流 2019-09-12 18:56:26 +08:00 committed by 老广
parent 5f23c358da
commit a2376d3afd
34 changed files with 611 additions and 434 deletions

View File

@ -3,7 +3,7 @@
from rest_framework import viewsets
from common.permissions import IsOrgAdminOrAppUser, IsAuditor
from common.permissions import IsOrgAdminOrAppUser, IsOrgAuditor
from .models import FTPLog
from .serializers import FTPLogSerializer
@ -11,4 +11,4 @@ from .serializers import FTPLogSerializer
class FTPLogViewSet(viewsets.ModelViewSet):
queryset = FTPLog.objects.all()
serializer_class = FTPLogSerializer
permission_classes = (IsOrgAdminOrAppUser | IsAuditor,)
permission_classes = (IsOrgAdminOrAppUser | IsOrgAuditor,)

View File

@ -18,8 +18,9 @@ from django.db.models import Q
from audits.utils import get_excel_response, write_content_to_excel
from common.mixins import DatetimeSearchMixin
from common.permissions import PermissionsMixin, IsOrgAdmin, IsAuditor, IsValidUser
from common.permissions import (
PermissionsMixin, IsOrgAdmin, IsValidUser, IsOrgAuditor
)
from orgs.utils import current_org
from ops.views import CommandExecutionListView as UserCommandExecutionListView
from .models import FTPLog, OperateLog, PasswordChangeLog, UserLoginLog
@ -47,7 +48,7 @@ class FTPLogListView(PermissionsMixin, DatetimeSearchMixin, ListView):
paginate_by = settings.DISPLAY_PER_PAGE
user = asset = system_user = filename = ''
date_from = date_to = None
permission_classes = [IsOrgAdmin | IsAuditor]
permission_classes = [IsOrgAdmin | IsOrgAuditor]
def get_queryset(self):
self.queryset = super().get_queryset()
@ -96,7 +97,7 @@ class OperateLogListView(PermissionsMixin, DatetimeSearchMixin, ListView):
user = action = resource_type = ''
date_from = date_to = None
actions_dict = dict(OperateLog.ACTION_CHOICES)
permission_classes = [IsOrgAdmin | IsAuditor]
permission_classes = [IsOrgAdmin | IsOrgAuditor]
def get_queryset(self):
self.queryset = super().get_queryset()
@ -119,7 +120,7 @@ class OperateLogListView(PermissionsMixin, DatetimeSearchMixin, ListView):
def get_context_data(self, **kwargs):
context = {
'user_list': current_org.get_org_users(),
'user_list': current_org.get_org_members(),
'actions': self.actions_dict,
'resource_type_list': get_resource_type_list(),
'date_from': self.date_from,
@ -139,10 +140,10 @@ class PasswordChangeLogList(PermissionsMixin, DatetimeSearchMixin, ListView):
paginate_by = settings.DISPLAY_PER_PAGE
user = ''
date_from = date_to = None
permission_classes = [IsOrgAdmin | IsAuditor]
permission_classes = [IsOrgAdmin | IsOrgAuditor]
def get_queryset(self):
users = current_org.get_org_users()
users = current_org.get_org_members()
self.queryset = super().get_queryset().filter(
user__in=[user.__str__() for user in users]
)
@ -159,7 +160,7 @@ class PasswordChangeLogList(PermissionsMixin, DatetimeSearchMixin, ListView):
def get_context_data(self, **kwargs):
context = {
'user_list': current_org.get_org_users(),
'user_list': current_org.get_org_members(),
'date_from': self.date_from,
'date_to': self.date_to,
'user': self.user,
@ -176,18 +177,18 @@ class LoginLogListView(PermissionsMixin, DatetimeSearchMixin, ListView):
paginate_by = settings.DISPLAY_PER_PAGE
user = keyword = ""
date_to = date_from = None
permission_classes = [IsOrgAdmin | IsAuditor]
permission_classes = [IsOrgAdmin | IsOrgAuditor]
@staticmethod
def get_org_users():
users = current_org.get_org_users().values_list('username', flat=True)
def get_org_members():
users = current_org.get_org_members().values_list('username', flat=True)
return users
def get_queryset(self):
if current_org.is_default():
queryset = super().get_queryset()
else:
users = self.get_org_users()
users = self.get_org_members()
queryset = super().get_queryset().filter(username__in=users)
self.user = self.request.GET.get('user', '')
@ -214,7 +215,7 @@ class LoginLogListView(PermissionsMixin, DatetimeSearchMixin, ListView):
'date_to': self.date_to,
'user': self.user,
'keyword': self.keyword,
'user_list': self.get_org_users(),
'user_list': self.get_org_members(),
}
kwargs.update(context)
return super().get_context_data(**kwargs)
@ -223,6 +224,10 @@ class LoginLogListView(PermissionsMixin, DatetimeSearchMixin, ListView):
class CommandExecutionListView(UserCommandExecutionListView):
user_id = None
def get_user_list(self):
users = current_org.get_org_members(exclude=('Auditor',))
return users
def get_queryset(self):
queryset = self._get_queryset()
self.user_id = self.request.GET.get('user')
@ -233,10 +238,6 @@ class CommandExecutionListView(UserCommandExecutionListView):
queryset = queryset.filter(user__in=org_users)
return queryset
def get_user_list(self):
users = current_org.get_org_users()
return users
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context.update({

View File

@ -4,8 +4,6 @@ import time
from rest_framework import permissions
from django.contrib.auth.mixins import UserPassesTestMixin
from django.shortcuts import redirect
from django.http.response import HttpResponseForbidden
from django.conf import settings
from orgs.utils import current_org
@ -27,12 +25,6 @@ class IsAppUser(IsValidUser):
and request.user.is_app
class IsAuditor(IsValidUser):
def has_permission(self, request, view):
return super(IsAuditor, self).has_permission(request, view) \
and request.user.is_auditor
class IsSuperUser(IsValidUser):
def has_permission(self, request, view):
return super(IsSuperUser, self).has_permission(request, view) \
@ -45,6 +37,20 @@ class IsSuperUserOrAppUser(IsSuperUser):
or request.user.is_app
class IsSuperAuditor(IsValidUser):
def has_permission(self, request, view):
return super(IsSuperAuditor, self).has_permission(request, view) \
and request.user.is_super_auditor
class IsOrgAuditor(IsValidUser):
def has_permission(self, request, view):
if not current_org:
return False
return super(IsOrgAuditor, self).has_permission(request, view) \
and current_org.can_audit_by(request.user)
class IsOrgAdmin(IsValidUser):
"""Allows access only to superuser"""
@ -81,43 +87,6 @@ class IsCurrentUserOrReadOnly(permissions.BasePermission):
return obj == request.user
class LoginRequiredMixin(UserPassesTestMixin):
def test_func(self):
if self.request.user.is_authenticated:
return True
else:
return False
class AdminUserRequiredMixin(UserPassesTestMixin):
def test_func(self):
if not self.request.user.is_authenticated:
return False
elif not current_org.can_admin_by(self.request.user):
self.raise_exception = True
return False
return True
def dispatch(self, request, *args, **kwargs):
if not request.user.is_authenticated:
return super().dispatch(request, *args, **kwargs)
if not current_org:
return redirect('orgs:switch-a-org')
if not current_org.can_admin_by(request.user):
if request.user.is_org_admin:
return redirect('orgs:switch-a-org')
return HttpResponseForbidden()
return super().dispatch(request, *args, **kwargs)
class SuperUserRequiredMixin(UserPassesTestMixin):
def test_func(self):
if self.request.user.is_authenticated and self.request.user.is_superuser:
return True
class WithBootstrapToken(permissions.BasePermission):
def has_permission(self, request, view):
authorization = request.META.get('HTTP_AUTHORIZATION', '')
@ -159,14 +128,61 @@ class NeedMFAVerify(permissions.BasePermission):
return False
class CanUpdateDeleteSuperUser(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
if request.method in ['GET', 'OPTIONS']:
return True
elif request.method == 'DELETE' and str(request.user.id) == str(obj.id):
class CanUpdateDeleteUser(permissions.BasePermission):
@staticmethod
def has_delete_object_permission(request, view, obj):
if not request.user.can_admin_current_org:
return False
elif request.user.is_superuser:
# 超级管理员 / 组织管理员
if str(request.user.id) == str(obj.id):
return False
# 超级管理员
if request.user.is_superuser:
if obj.is_superuser and obj.username in ['admin']:
return False
return True
if hasattr(obj, 'is_superuser') and obj.is_superuser:
# 组织管理员
if obj.is_superuser:
return False
if obj.is_super_auditor:
return False
if obj.is_org_admin:
return False
if len(obj.audit_orgs) > 1:
return False
if len(obj.user_orgs) > 1:
return False
return True
@staticmethod
def has_update_object_permission(request, view, obj):
if not request.user.can_admin_current_org:
return False
# 超级管理员 / 组织管理员
if str(request.user.id) == str(obj.id):
return True
# 超级管理员
if request.user.is_superuser:
return True
# 组织管理员
if obj.is_superuser:
return False
if obj.is_super_auditor:
return False
if obj.is_org_admin:
return False
if len(obj.audit_orgs) > 1:
return False
if len(obj.user_orgs) > 1:
return False
return True
def has_object_permission(self, request, view, obj):
if not request.user.can_admin_current_org:
return False
if request.method in ['DELETE']:
return self.has_delete_object_permission(request, view, obj)
if request.method in ['PUT', 'PATCH']:
return self.has_update_object_permission(request, view, obj)
return True

View File

@ -34,17 +34,13 @@ class IndexView(PermissionsMixin, TemplateView):
def dispatch(self, request, *args, **kwargs):
if not request.user.is_authenticated:
return self.handle_no_permission()
if request.user.is_auditor:
return super(IndexView, self).dispatch(request, *args, **kwargs)
if not request.user.is_org_admin:
if request.user.is_common_user:
return redirect('assets:user-asset-list')
if not current_org or not current_org.can_admin_by(request.user):
return redirect('orgs:switch-a-org')
return super(IndexView, self).dispatch(request, *args, **kwargs)
@staticmethod
def get_user_count():
return current_org.get_org_users().count()
return current_org.get_org_members().count()
@staticmethod
def get_asset_count():
@ -99,7 +95,7 @@ class IndexView(PermissionsMixin, TemplateView):
return self.session_month.values('user').distinct().count()
def get_month_inactive_user_total(self):
count = current_org.get_org_users().count() - self.get_month_active_user_total()
count = current_org.get_org_members().count() - self.get_month_active_user_total()
if count < 0:
count = 0
return count
@ -115,7 +111,7 @@ class IndexView(PermissionsMixin, TemplateView):
@staticmethod
def get_user_disabled_total():
return current_org.get_org_users().filter(is_active=False).count()
return current_org.get_org_members().filter(is_active=False).count()
@staticmethod
def get_asset_disabled_total():

View File

@ -2,7 +2,7 @@
#
from django.views.generic import TemplateView
from common.permissions import PermissionsMixin, IsOrgAdmin, IsAuditor
from common.permissions import PermissionsMixin, IsOrgAdmin, IsOrgAuditor
__all__ = ['CeleryTaskLogView']
@ -10,7 +10,7 @@ __all__ = ['CeleryTaskLogView']
class CeleryTaskLogView(PermissionsMixin, TemplateView):
template_name = 'ops/celery_task_log.html'
permission_classes = [IsOrgAdmin | IsAuditor]
permission_classes = [IsOrgAdmin | IsOrgAuditor]
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)

View File

@ -6,7 +6,7 @@ from django.conf import settings
from django.views.generic import ListView, TemplateView
from common.permissions import (
PermissionsMixin, IsOrgAdmin, IsAuditor, IsValidUser
PermissionsMixin, IsOrgAdmin, IsValidUser, IsOrgAuditor
)
from common.mixins import DatetimeSearchMixin
from ..models import CommandExecution
@ -25,7 +25,7 @@ class CommandExecutionListView(PermissionsMixin, DatetimeSearchMixin, ListView):
ordering = ('-date_created',)
context_object_name = 'task_list'
keyword = ''
permission_classes = [IsOrgAdmin | IsAuditor]
permission_classes = [IsOrgAdmin | IsOrgAuditor]
def _get_queryset(self):
self.keyword = self.request.GET.get('keyword', '')

View File

@ -33,7 +33,7 @@ class OrgViewSet(BulkModelViewSet):
def get_data_from_model(self, model):
if model == User:
data = model.objects.filter(orgs__id=self.org.id)
data = model.objects.filter(related_user_orgs__id=self.org.id)
else:
data = model.objects.filter(org_id=self.org.id)
return data

View File

@ -7,9 +7,11 @@ from .models import Organization
def org_processor(request):
context = {
'ADMIN_ORGS': Organization.get_user_admin_orgs(request.user),
# 'ADMIN_ORGS': request.user.admin_orgs,
# 'AUDIT_ORGS': request.user.audit_orgs,
'ADMIN_OR_AUDIT_ORGS': Organization.get_user_admin_or_audit_orgs(request.user),
'CURRENT_ORG': get_org_from_request(request),
'HAS_ORG_PERM': current_org.can_admin_by(request.user),
# 'HAS_ORG_PERM': current_org.can_admin_by(request.user),
}
return context

View File

@ -13,14 +13,23 @@ class OrgMiddleware:
def set_permed_org_if_need(request):
if request.path.startswith('/api'):
return
if not (request.user.is_authenticated and request.user.is_org_admin):
if not request.user.is_authenticated:
return
if request.user.is_common_user:
return
org = get_org_from_request(request)
if org.can_admin_by(request.user):
return
admin_orgs = Organization.get_user_admin_orgs(request.user)
if org.can_audit_by(request.user):
return
admin_orgs = request.user.admin_orgs
if admin_orgs:
request.session['oid'] = str(admin_orgs[0].id)
return
audit_orgs = request.user.audit_orgs
if audit_orgs:
request.session['oid'] = str(audit_orgs[0].id)
return
def __call__(self, request):
self.set_permed_org_if_need(request)

View File

@ -9,8 +9,9 @@ from common.utils import is_uuid
class Organization(models.Model):
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
name = models.CharField(max_length=128, unique=True, verbose_name=_("Name"))
users = models.ManyToManyField('users.User', related_name='orgs', blank=True)
admins = models.ManyToManyField('users.User', related_name='admin_orgs', blank=True)
users = models.ManyToManyField('users.User', related_name='related_user_orgs', blank=True)
admins = models.ManyToManyField('users.User', related_name='related_admin_orgs', blank=True)
auditors = models.ManyToManyField('users.User', related_name='related_audit_orgs', blank=True)
created_by = models.CharField(max_length=32, null=True, blank=True, verbose_name=_('Created by'))
date_created = models.DateTimeField(auto_now_add=True, null=True, blank=True, verbose_name=_('Date created'))
comment = models.TextField(max_length=128, default='', blank=True, verbose_name=_('Comment'))
@ -70,25 +71,46 @@ class Organization(models.Model):
org = cls.default() if default else None
return org
def get_org_users(self, include_app=False):
def get_org_users(self):
from users.models import User
if self.is_real():
users = self.users.all()
else:
users = User.objects.all()
if not include_app:
users = users.exclude(role=User.ROLE_APP)
return users
return self.users.all()
return User.objects.filter(role=User.ROLE_USER)
def get_org_admins(self):
from users.models import User
if self.is_real():
return self.admins.all()
return []
return User.objects.filter(role=User.ROLE_ADMIN)
def get_org_auditors(self):
from users.models import User
if self.is_real():
return self.auditors.all()
return User.objects.filter(role=User.ROLE_AUDITOR)
def get_org_members(self, exclude=()):
from users.models import User
members = User.objects.none()
if 'Admin' not in exclude:
members |= self.get_org_admins()
if 'User' not in exclude:
members |= self.get_org_users()
if 'Auditor' not in exclude:
members |= self.get_org_auditors()
return members.exclude(role=User.ROLE_APP).distinct()
def can_admin_by(self, user):
if user.is_superuser:
return True
if user in list(self.get_org_admins()):
if self.get_org_admins().filter(id=user.id):
return True
return False
def can_audit_by(self, user):
if user.is_super_auditor:
return True
if self.get_org_auditors().filter(id=user.id):
return True
return False
@ -100,13 +122,40 @@ class Organization(models.Model):
admin_orgs = []
if user.is_anonymous:
return admin_orgs
elif user.is_superuser or user.is_auditor:
elif user.is_superuser:
admin_orgs = list(cls.objects.all())
admin_orgs.append(cls.default())
elif user.is_org_admin:
admin_orgs = user.admin_orgs.all()
admin_orgs = user.related_admin_orgs.all()
return admin_orgs
@classmethod
def get_user_user_orgs(self, user):
user_orgs = []
if user.is_anonymous:
return user_orgs
user_orgs = user.related_user_orgs.all()
return user_orgs
@classmethod
def get_user_audit_orgs(cls, user):
audit_orgs = []
if user.is_anonymous:
return audit_orgs
elif user.is_super_auditor:
audit_orgs = list(cls.objects.all())
audit_orgs.append(cls.default())
elif user.is_org_auditor:
audit_orgs = user.related_audit_orgs.all()
return audit_orgs
@classmethod
def get_user_admin_or_audit_orgs(self, user):
admin_orgs = self.get_user_admin_orgs(user)
audit_orgs = self.get_user_audit_orgs(user)
orgs = set(admin_orgs) | set(audit_orgs)
return orgs
@classmethod
def default(cls):
return cls(id=cls.DEFAULT_ID, name=cls.DEFAULT_NAME)

View File

@ -21,6 +21,7 @@ class OrgSerializer(ModelSerializer):
class OrgReadSerializer(ModelSerializer):
admins = serializers.SlugRelatedField(slug_field='name', many=True, read_only=True)
auditors = serializers.SlugRelatedField(slug_field='name', many=True, read_only=True)
users = serializers.SlugRelatedField(slug_field='name', many=True, read_only=True)
user_groups = serializers.SerializerMethodField()
assets = serializers.SerializerMethodField()

View File

@ -5,6 +5,7 @@ from django.views.generic import DetailView, View
from .models import Organization
from common.utils import UUID_PATTERN
from orgs.utils import current_org
class SwitchOrgView(DetailView):
@ -22,17 +23,28 @@ class SwitchOrgView(DetailView):
return redirect(reverse('index'))
if UUID_PATTERN.search(referer):
return redirect(reverse('index'))
# 组织管理员切换到组织审计员时(403)
if not self.object.get_org_admins().filter(id=request.user.id):
return redirect(reverse('index'))
return redirect(referer)
class SwitchToAOrgView(View):
def get(self, request, *args, **kwargs):
admin_orgs = Organization.get_user_admin_orgs(request.user)
if not admin_orgs:
if request.user.is_common_user:
return HttpResponseForbidden()
admin_orgs = request.user.admin_orgs
audit_orgs = request.user.audit_orgs
default_org = Organization.default()
if default_org in admin_orgs:
redirect_org = default_org
else:
redirect_org = admin_orgs[0]
return redirect(reverse('orgs:org-switch', kwargs={'pk': redirect_org.id}))
if admin_orgs:
if default_org in admin_orgs:
redirect_org = default_org
else:
redirect_org = admin_orgs[0]
return redirect(reverse('orgs:org-switch', kwargs={'pk': redirect_org.id}))
if audit_orgs:
if default_org in audit_orgs:
redirect_org = default_org
else:
redirect_org = audit_orgs[0]
return redirect(reverse('orgs:org-switch', kwargs={'pk': redirect_org.id}))

View File

@ -39,7 +39,7 @@ class AssetPermissionForm(OrgModelForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
users_field = self.fields.get('users')
users_field.queryset = current_org.get_org_users()
users_field.queryset = current_org.get_org_members(exclude=('Auditor',))
if self.data:
return

View File

@ -19,7 +19,7 @@ class RemoteAppPermissionCreateUpdateForm(OrgModelForm):
super().__init__(*args, **kwargs)
users_field = self.fields.get('users')
if hasattr(users_field, 'queryset'):
users_field.queryset = current_org.get_org_users()
users_field.queryset = current_org.get_org_members(exclude=('Auditor',))
class Meta:
model = RemoteAppPermission

View File

@ -131,16 +131,15 @@ class AssetPermissionUserView(PermissionsMixin,
return queryset
def get_context_data(self, **kwargs):
user_remain = current_org.get_org_members(exclude=('Auditor',)).exclude(
assetpermission=self.object)
user_groups_remain = UserGroup.objects.exclude(
assetpermission=self.object)
context = {
'app': _('Perms'),
'action': _('Asset permission user list'),
'users_remain': current_org.get_org_users().exclude(
assetpermission=self.object
),
'user_groups_remain': UserGroup.objects.exclude(
assetpermission=self.object
)
'users_remain': user_remain,
'user_groups_remain': user_groups_remain,
}
kwargs.update(context)
return super().get_context_data(**kwargs)

View File

@ -107,15 +107,15 @@ class RemoteAppPermissionUserView(PermissionsMixin,
return queryset
def get_context_data(self, **kwargs):
user_remain = current_org.get_org_members(exclude=('Auditor',)).exclude(
remoteapppermission=self.object)
user_groups_remain = UserGroup.objects.exclude(
remoteapppermission=self.object)
context = {
'app': _('Perms'),
'action': _('RemoteApp permission user list'),
'users_remain': current_org.get_org_users().exclude(
remoteapppermission=self.object
),
'user_groups_remain': UserGroup.objects.exclude(
remoteapppermission=self.object
)
'users_remain': user_remain,
'user_groups_remain': user_groups_remain,
}
kwargs.update(context)
return super().get_context_data(**kwargs)

View File

@ -68,7 +68,7 @@
</a>
<ul class="dropdown-menu animated fadeInRight m-t-xs profile-dropdown">
<li><a href="{% url 'users:user-profile' %}"><i class="fa fa-cogs"> </i><span> {% trans 'Profile' %}</span></a></li>
{% if request.user.is_org_admin %}
{% if request.user.can_admin_or_audit_current_org %}
{% if request.COOKIES.IN_ADMIN_PAGE == 'No' %}
<li><a id="switch_admin"><i class="fa fa-exchange"></i><span> {% trans 'Admin page' %}</span></a></li>
{% else %}
@ -107,3 +107,23 @@
<div class="col-sm-2">
</div>
</div>
<script>
$(document).ready(function () {
})
.on('click', '#switch_admin', function () {
var cookieName = "IN_ADMIN_PAGE";
setTimeout(function () {
delCookie(cookieName);
setCookie(cookieName, "Yes");
window.location = "/"
}, 100)
})
.on('click', '#switch_user', function () {
var cookieName = "IN_ADMIN_PAGE";
setTimeout(function () {
delCookie(cookieName);
setCookie(cookieName, "No");
window.location = "{% url 'assets:user-asset-list' %}"
}, 100);
})
</script>

View File

@ -2,12 +2,10 @@
<div class="sidebar-collapse">
<ul class="nav" id="side-menu">
{% include '_user_profile.html' %}
{% if request.user.is_org_admin and request.COOKIES.IN_ADMIN_PAGE != "No" %}
{% include '_nav.html' %}
{% elif request.user.is_auditor %}
{% include '_nav_audits.html' %}
{% else %}
{% if request.user.is_common_user or request.COOKIES.IN_ADMIN_PAGE == 'No' %}
{% include '_nav_user.html' %}
{% else %}
{% include '_nav.html' %}
{% endif %}
</ul>
</div>

View File

@ -1,127 +1,172 @@
{% load i18n %}
<li id="index">
<a href="{% url 'index' %}">
<i class="fa fa-dashboard" style="width: 14px"></i> <span class="nav-label">{% trans 'Dashboard' %}</span>
<span class="label label-info pull-right"></span>
</a>
</li>
<li id="users">
<a href="#">
<i class="fa fa-group" style="width: 14px"></i> <span class="nav-label">{% trans 'Users' %}</span><span class="fa arrow"></span>
</a>
<ul class="nav nav-second-level active">
<li id="user"><a href="{% url 'users:user-list' %}">{% trans 'User list' %}</a></li>
<li id="user-group"><a href="{% url 'users:user-group-list' %}">{% trans 'User group' %}</a></li>
</ul>
</li>
<li id="assets">
<a>
<i class="fa fa-inbox" style="width: 14px"></i> <span class="nav-label">{% trans 'Assets' %}</span><span class="fa arrow"></span>
</a>
<ul class="nav nav-second-level">
<li id="asset"><a href="{% url 'assets:asset-list' %}">{% trans 'Asset list' %}</a></li>
<li id="domain"><a href="{% url 'assets:domain-list' %}">{% trans 'Domain list' %}</a></li>
<li id="admin-user"><a href="{% url 'assets:admin-user-list' %}">{% trans 'Admin user' %}</a></li>
<li id="system-user"><a href="{% url 'assets:system-user-list' %}">{% trans 'System user' %}</a></li>
<li id="label"><a href="{% url 'assets:label-list' %}">{% trans 'Labels' %}</a></li>
<li id="cmd-filter"><a href="{% url 'assets:cmd-filter-list' %}">{% trans 'Command filters' %}</a></li>
</ul>
</li>
{% if LICENSE_VALID %}
<li id="applications">
<a>
<i class="fa fa-th" style="width: 14px"></i> <span class="nav-label">{% trans 'Applications' %}</span><span class="fa arrow"></span>
</a>
<ul class="nav nav-second-level">
<li id="remote-app"><a href="{% url 'applications:remote-app-list' %}">{% trans 'RemoteApp' %}</a></li>
</ul>
</li>
{# Index #}
{% if request.user.can_admin_or_audit_current_org %}
<li id="index">
<a href="{% url 'index' %}">
<i class="fa fa-dashboard" style="width: 14px"></i> <span class="nav-label">{% trans 'Dashboard' %}</span>
<span class="label label-info pull-right"></span>
</a>
</li>
{% endif %}
<li id="perms">
<a href="#"><i class="fa fa-edit" style="width: 14px"></i> <span class="nav-label">{% trans 'Perms' %}</span><span class="fa arrow"></span></a>
<ul class="nav nav-second-level">
<li id="asset-permission">
<a href="{% url 'perms:asset-permission-list' %}">{% trans 'Asset permission' %}</a>
</li>
{% if LICENSE_VALID %}
<li id="remote-app-permission">
<a href="{% url 'perms:remote-app-permission-list' %}">{% trans 'RemoteApp' %}</a>
</li>
{% endif %}
</ul>
</li>
<li id="terminal">
<a>
<i class="fa fa-rocket" style="width: 14px"></i> <span class="nav-label">{% trans 'Sessions' %}</span><span class="fa arrow"></span>
</a>
<ul class="nav nav-second-level">
<li id="session-online"><a href="{% url 'terminal:session-online-list' %}">{% trans 'Session online' %}</a></li>
<li id="session-offline"><a href="{% url 'terminal:session-offline-list' %}">{% trans 'Session offline' %}</a></li>
<li id="command"><a href="{% url 'terminal:command-list' %}">{% trans 'Commands' %}</a></li>
<li>
<a href="{% url 'terminal:web-terminal' %}" target="_blank">
<span class="nav-label">{% trans 'Web terminal' %}</span>
</a>
</li>
<li>
<a href="{% url 'terminal:web-sftp' %}" target="_blank">
<span class="nav-label">{% trans 'File manager' %}</span>
</a>
</li>
{% if request.user.is_superuser %}
<li id="terminal"><a href="{% url 'terminal:terminal-list' %}">{% trans 'Terminal' %}</a></li>
{% endif %}
</ul>
</li>
<li id="ops">
<a>
<i class="fa fa-coffee" style="width: 14px"></i> <span class="nav-label">{% trans 'Job Center' %}</span><span class="fa arrow"></span>
</a>
<ul class="nav nav-second-level">
<li id="task"><a href="{% url 'ops:task-list' %}">{% trans 'Task list' %}</a></li>
<li id="command-execution"><a href="{% url 'ops:command-execution-start' %}">{% trans 'Batch command' %}</a></li>
</ul>
</li>
<li id="audits">
<a>
<i class="fa fa-history" style="width: 14px"></i> <span class="nav-label">{% trans 'Audits' %}</span><span class="fa arrow"></span>
</a>
<ul class="nav nav-second-level">
<li id="login-log"><a href="{% url 'audits:login-log-list' %}">{% trans 'Login log' %}</a></li>
<li id="ftp-log"><a href="{% url 'audits:ftp-log-list' %}">{% trans 'FTP log' %}</a></li>
<li id="operate-log"><a href="{% url 'audits:operate-log-list' %}">{% trans 'Operate log' %}</a></li>
<li id="password-change-log"><a href="{% url 'audits:password-change-log-list' %}">{% trans 'Password change log' %}</a></li>
<li id="command-execution-log"><a href="{% url 'audits:command-execution-log-list' %}">{% trans 'Batch command' %}</a></li>
</ul>
</li>
{% if XPACK_PLUGINS %}
<li id="xpack">
<a>
<i class="fa fa-sitemap" style="width: 14px"></i> <span class="nav-label">{% trans 'XPack' %}</span><span class="fa arrow"></span>
</a>
<ul class="nav nav-second-level">
{% for plugin in XPACK_PLUGINS %}
{% ifequal plugin.name 'cloud'%}
<li id="{{ plugin.name }}">
<a href="#"><span class="nav-label">{% trans plugin.verbose_name %}</span><span class="fa arrow"></span></a>
<ul class="nav nav-third-level">
<li id="account"><a href="{% url 'xpack:cloud:account-list' %}">{% trans 'Account list' %}</a></li>
<li id="sync-instance-task"><a href="{% url 'xpack:cloud:sync-instance-task-list' %}">{% trans 'Sync instance' %}</a></li>
</ul>
</li>
{% else %}
<li id="{{ plugin.name }}"><a href="{{ plugin.endpoint }}">{% trans plugin.verbose_name %}</a></li>
{% endifequal %}
{% endfor %}
</ul>
</li>
{# Users #}
{% if request.user.can_admin_current_org %}
<li id="users">
<a href="#">
<i class="fa fa-group" style="width: 14px"></i> <span class="nav-label">{% trans 'Users' %}</span><span class="fa arrow"></span>
</a>
<ul class="nav nav-second-level active">
<li id="user"><a href="{% url 'users:user-list' %}">{% trans 'User list' %}</a></li>
<li id="user-group"><a href="{% url 'users:user-group-list' %}">{% trans 'User group' %}</a></li>
</ul>
</li>
{% endif %}
{# User info #}
{% if not request.user.can_admin_current_org and request.user.can_audit_current_org %}
<li id="users">
<a href="{% url 'users:user-profile' %}">
<i class="fa fa-user" style="width: 14px"></i> <span class="nav-label">{% trans 'Profile' %}</span><span class="label label-info pull-right"></span>
</a>
</li>
{% endif %}
{# Assets #}
{% if request.user.can_admin_current_org %}
<li id="assets">
<a>
<i class="fa fa-inbox" style="width: 14px"></i> <span class="nav-label">{% trans 'Assets' %}</span><span class="fa arrow"></span>
</a>
<ul class="nav nav-second-level">
<li id="asset"><a href="{% url 'assets:asset-list' %}">{% trans 'Asset list' %}</a></li>
<li id="domain"><a href="{% url 'assets:domain-list' %}">{% trans 'Domain list' %}</a></li>
<li id="admin-user"><a href="{% url 'assets:admin-user-list' %}">{% trans 'Admin user' %}</a></li>
<li id="system-user"><a href="{% url 'assets:system-user-list' %}">{% trans 'System user' %}</a></li>
<li id="label"><a href="{% url 'assets:label-list' %}">{% trans 'Labels' %}</a></li>
<li id="cmd-filter"><a href="{% url 'assets:cmd-filter-list' %}">{% trans 'Command filters' %}</a></li>
</ul>
</li>
{% endif %}
{# Applications #}
{% if request.user.can_admin_current_org and LICENSE_VALID %}
<li id="applications">
<a>
<i class="fa fa-th" style="width: 14px"></i> <span class="nav-label">{% trans 'Applications' %}</span><span class="fa arrow"></span>
</a>
<ul class="nav nav-second-level">
<li id="remote-app"><a href="{% url 'applications:remote-app-list' %}">{% trans 'RemoteApp' %}</a></li>
</ul>
</li>
{% endif %}
{# Perms #}
{% if request.user.can_admin_current_org %}
<li id="perms">
<a href="#"><i class="fa fa-edit" style="width: 14px"></i> <span class="nav-label">{% trans 'Perms' %}</span><span class="fa arrow"></span></a>
<ul class="nav nav-second-level">
<li id="asset-permission">
<a href="{% url 'perms:asset-permission-list' %}">{% trans 'Asset permission' %}</a>
</li>
{% if LICENSE_VALID %}
<li id="remote-app-permission">
<a href="{% url 'perms:remote-app-permission-list' %}">{% trans 'RemoteApp' %}</a>
</li>
{% endif %}
</ul>
</li>
{% endif %}
{# Terminal #}
{% if request.user.can_admin_or_audit_current_org %}
<li id="terminal">
<a>
<i class="fa fa-rocket" style="width: 14px"></i> <span class="nav-label">{% trans 'Sessions' %}</span><span class="fa arrow"></span>
</a>
<ul class="nav nav-second-level">
<li id="session-online"><a href="{% url 'terminal:session-online-list' %}">{% trans 'Session online' %}</a></li>
<li id="session-offline"><a href="{% url 'terminal:session-offline-list' %}">{% trans 'Session offline' %}</a></li>
<li id="command"><a href="{% url 'terminal:command-list' %}">{% trans 'Commands' %}</a></li>
{% if request.user.can_admin_current_org %}
<li><a href="{% url 'terminal:web-terminal' %}" target="_blank"><span class="nav-label">{% trans 'Web terminal' %}</span></a></li>
<li><a href="{% url 'terminal:web-sftp' %}" target="_blank"><span class="nav-label">{% trans 'File manager' %}</span></a></li>
{% endif %}
{% if request.user.is_superuser %}
<li id="terminal"><a href="{% url 'terminal:terminal-list' %}">{% trans 'Terminal' %}</a></li>
{% endif %}
</ul>
</li>
{% endif %}
{# Ops #}
{% if request.user.can_admin_current_org %}
<li id="ops">
<a>
<i class="fa fa-coffee" style="width: 14px"></i> <span class="nav-label">{% trans 'Job Center' %}</span><span class="fa arrow"></span>
</a>
<ul class="nav nav-second-level">
<li id="task"><a href="{% url 'ops:task-list' %}">{% trans 'Task list' %}</a></li>
<li id="command-execution"><a href="{% url 'ops:command-execution-start' %}">{% trans 'Batch command' %}</a></li>
</ul>
</li>
{% endif %}
{# Audits #}
{% if request.user.can_admin_or_audit_current_org %}
<li id="audits">
<a>
<i class="fa fa-history" style="width: 14px"></i> <span class="nav-label">{% trans 'Audits' %}</span><span class="fa arrow"></span>
</a>
<ul class="nav nav-second-level">
<li id="login-log"><a href="{% url 'audits:login-log-list' %}">{% trans 'Login log' %}</a></li>
<li id="ftp-log"><a href="{% url 'audits:ftp-log-list' %}">{% trans 'FTP log' %}</a></li>
<li id="operate-log"><a href="{% url 'audits:operate-log-list' %}">{% trans 'Operate log' %}</a></li>
<li id="password-change-log"><a href="{% url 'audits:password-change-log-list' %}">{% trans 'Password change log' %}</a></li>
<li id="command-execution-log"><a href="{% url 'audits:command-execution-log-list' %}">{% trans 'Batch command' %}</a></li>
</ul>
</li>
{% endif %}
{# X-Pack #}
{% if request.user.can_admin_current_org and XPACK_PLUGINS %}
<li id="xpack">
<a>
<i class="fa fa-sitemap" style="width: 14px"></i> <span class="nav-label">{% trans 'XPack' %}</span><span class="fa arrow"></span>
</a>
<ul class="nav nav-second-level">
{% for plugin in XPACK_PLUGINS %}
{% ifequal plugin.name 'cloud'%}
<li id="{{ plugin.name }}">
<a href="#"><span class="nav-label">{% trans plugin.verbose_name %}</span><span class="fa arrow"></span></a>
<ul class="nav nav-third-level">
<li id="account"><a href="{% url 'xpack:cloud:account-list' %}">{% trans 'Account list' %}</a></li>
<li id="sync-instance-task"><a href="{% url 'xpack:cloud:sync-instance-task-list' %}">{% trans 'Sync instance' %}</a></li>
</ul>
</li>
{% else %}
<li id="{{ plugin.name }}"><a href="{{ plugin.endpoint }}">{% trans plugin.verbose_name %}</a></li>
{% endifequal %}
{% endfor %}
</ul>
</li>
{% endif %}
{# Settings #}
{% if request.user.is_superuser %}
<li id="settings">
<a href="{% url 'settings:basic-setting' %}">
<i class="fa fa-gears"></i> <span class="nav-label">{% trans 'Settings' %}</span><span class="label label-info pull-right"></span>
</a>
</li>
<li id="settings">
<a href="{% url 'settings:basic-setting' %}">
<i class="fa fa-gears"></i> <span class="nav-label">{% trans 'Settings' %}</span><span class="label label-info pull-right"></span>
</a>
</li>
{% endif %}
<script>

View File

@ -1,31 +0,0 @@
{% load i18n %}
<li id="index">
<a href="{% url 'index' %}">
<i class="fa fa-dashboard" style="width: 14px"></i> <span class="nav-label">{% trans 'Dashboard' %}</span>
<span class="label label-info pull-right"></span>
</a>
</li>
<li id="terminal">
<a>
<i class="fa fa-rocket" style="width: 14px"></i> <span class="nav-label">{% trans 'Sessions' %}</span><span class="fa arrow"></span>
</a>
<ul class="nav nav-second-level">
<li id="session-online"><a href="{% url 'terminal:session-online-list' %}">{% trans 'Session online' %}</a></li>
<li id="session-offline"><a href="{% url 'terminal:session-offline-list' %}">{% trans 'Session offline' %}</a></li>
<li id="command"><a href="{% url 'terminal:command-list' %}">{% trans 'Commands' %}</a></li>
</ul>
</li>
<li id="audits">
<a>
<i class="fa fa-history" style="width: 14px"></i> <span class="nav-label">{% trans 'Audits' %}</span><span class="fa arrow"></span>
</a>
<ul class="nav nav-second-level">
<li id="login-log"><a href="{% url 'audits:login-log-list' %}">{% trans 'Login log' %}</a></li>
<li id="ftp-log"><a href="{% url 'audits:ftp-log-list' %}">{% trans 'FTP log' %}</a></li>
<li id="operate-log"><a href="{% url 'audits:operate-log-list' %}">{% trans 'Operate log' %}</a></li>
<li id="password-change-log"><a href="{% url 'audits:password-change-log-list' %}">{% trans 'Password change log' %}</a></li>
<li id="command-execution-log"><a href="{% url 'audits:command-execution-log-list' %}">{% trans 'Batch command' %}</a></li>
</ul>
</li>

View File

@ -9,49 +9,29 @@
<div class="logo-element">
<img alt="image" height="40" src="{{ LOGO_URL }}"/>
</div>
{% if ADMIN_ORGS and request.COOKIES.IN_ADMIN_PAGE != 'No' %}
{% if ADMIN_ORGS|length > 1 or not CURRENT_ORG.is_default %}
<div>
<a class="dropdown-toggle" data-toggle="dropdown" aria-expanded="false" style="display: block; background-color: transparent; color: #8095a8; padding: 14px 20px 14px 25px">
<i class="fa fa-bookmark" style="width: 14px; "></i>
<span class="nav-label" style="padding-left: 7px">
{{ CURRENT_ORG.name }}
</span>
<span class="fa fa-sort-desc pull-right"></span>
</a>
<ul class="dropdown-menu" style="min-width: 220px">
{% for org in ADMIN_ORGS %}
<li>
<a class="org-dropdown" href="{% url 'orgs:org-switch' pk=org.id %}" data-id="{{ org.id }}">
{{ org.name }}
{% if org.id == CURRENT_ORG.id %}
<span class="fa fa-circle" style="padding-top: 5px; color: #1ab394"></span>
{% endif %}
</a>
</li>
{% endfor %}
</ul>
</div>
{% if ADMIN_OR_AUDIT_ORGS and request.COOKIES.IN_ADMIN_PAGE != 'No' %}
{% if ADMIN_OR_AUDIT_ORGS|length > 1 or not CURRENT_ORG.is_default %}
<div>
<a class="dropdown-toggle" data-toggle="dropdown" aria-expanded="false" style="display: block; background-color: transparent; color: #8095a8; padding: 14px 20px 14px 25px">
<i class="fa fa-bookmark" style="width: 14px; "></i>
<span class="nav-label" style="padding-left: 7px">
{{ CURRENT_ORG.name }}
</span>
<span class="fa fa-sort-desc pull-right"></span>
</a>
<ul class="dropdown-menu" style="min-width: 220px">
{% for org in ADMIN_OR_AUDIT_ORGS %}
<li>
<a class="org-dropdown" href="{% url 'orgs:org-switch' pk=org.id %}" data-id="{{ org.id }}">
{{ org.name }}
{% if org.id == CURRENT_ORG.id %}
<span class="fa fa-circle" style="padding-top: 5px; color: #1ab394"></span>
{% endif %}
</a>
</li>
{% endfor %}
</ul>
</div>
{% endif %}
{% endif %}
</li>
<script>
$(document).ready(function () {
})
.on('click', '#switch_admin', function () {
var cookieName = "IN_ADMIN_PAGE";
setTimeout(function () {
delCookie(cookieName);
setCookie(cookieName, "Yes");
window.location = "/"
}, 100)
})
.on('click', '#switch_user', function () {
var cookieName = "IN_ADMIN_PAGE";
setTimeout(function () {
delCookie(cookieName);
setCookie(cookieName, "No");
window.location = "{% url 'assets:user-asset-list' %}"
}, 100);
})
</script>

View File

@ -9,7 +9,7 @@ from rest_framework.response import Response
from django.template import loader
from common.permissions import IsOrgAdminOrAppUser, IsAuditor
from common.permissions import IsOrgAdminOrAppUser, IsOrgAuditor
from common.utils import get_logger
from ..backends import (
get_command_storage, get_multi_command_storage,
@ -22,7 +22,7 @@ __all__ = ['CommandViewSet', 'CommandExportApi']
class CommandQueryMixin:
command_store = get_command_storage()
permission_classes = [IsOrgAdminOrAppUser | IsAuditor]
permission_classes = [IsOrgAdminOrAppUser | IsOrgAuditor]
filter_fields = [
"asset", "system_user", "user", "session",
]

View File

@ -12,7 +12,7 @@ from rest_framework.generics import GenericAPIView
import jms_storage
from common.utils import is_uuid, get_logger
from common.permissions import IsOrgAdminOrAppUser, IsAuditor
from common.permissions import IsOrgAdminOrAppUser, IsOrgAuditor
from common.filters import DatetimeRangeFilter
from orgs.mixins.api import OrgBulkModelViewSet
from ..hands import SystemUser
@ -27,7 +27,7 @@ logger = get_logger(__name__)
class SessionViewSet(OrgBulkModelViewSet):
queryset = Session.objects.all()
serializer_class = serializers.SessionSerializer
permission_classes = (IsOrgAdminOrAppUser | IsAuditor, )
permission_classes = (IsOrgAdminOrAppUser | IsOrgAuditor, )
filter_fields = [
"user", "asset", "system_user", "remote_addr",
"protocol", "terminal", "is_finished",
@ -62,7 +62,7 @@ class SessionViewSet(OrgBulkModelViewSet):
class SessionReplayViewSet(viewsets.ViewSet):
serializer_class = serializers.ReplaySerializer
permission_classes = (IsOrgAdminOrAppUser | IsAuditor,)
permission_classes = (IsOrgAdminOrAppUser | IsOrgAuditor,)
session = None
def create(self, request, *args, **kwargs):

View File

@ -5,14 +5,14 @@ from django.views.generic import TemplateView
from django.utils.translation import ugettext as _
from django.utils import timezone
from common.permissions import PermissionsMixin, IsOrgAdmin, IsAuditor
from common.permissions import PermissionsMixin, IsOrgAdmin, IsOrgAuditor
__all__ = ['CommandListView']
class CommandListView(PermissionsMixin, TemplateView):
template_name = "terminal/command_list.html"
permission_classes = [IsOrgAdmin | IsAuditor]
permission_classes = [IsOrgAdmin | IsOrgAuditor]
default_days_ago = 5
def get_context_data(self, **kwargs):

View File

@ -7,7 +7,7 @@ from django.utils.translation import ugettext as _
from django.utils import timezone
from django.conf import settings
from common.permissions import PermissionsMixin, IsOrgAdmin, IsAuditor
from common.permissions import PermissionsMixin, IsOrgAdmin, IsOrgAuditor
from common.mixins import DatetimeSearchMixin
from ..models import Session, Command, Terminal
from ..backends import get_multi_command_storage
@ -24,7 +24,7 @@ class SessionListView(PermissionsMixin, TemplateView):
model = Session
template_name = 'terminal/session_list.html'
date_from = date_to = None
permission_classes = [IsOrgAdmin | IsAuditor]
permission_classes = [IsOrgAdmin | IsOrgAuditor]
default_days_ago = 5
def get_context_data(self, **kwargs):
@ -63,7 +63,7 @@ class SessionDetailView(SingleObjectMixin, PermissionsMixin, ListView):
template_name = 'terminal/session_detail.html'
model = Session
object = None
permission_classes = [IsOrgAdmin | IsAuditor]
permission_classes = [IsOrgAdmin | IsOrgAuditor]
def get(self, request, *args, **kwargs):
self.object = self.get_object(queryset=self.model.objects.all())

View File

@ -8,12 +8,11 @@ from django.utils.translation import ugettext as _
from rest_framework import generics
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated
from rest_framework.serializers import ValidationError
from rest_framework_bulk import BulkModelViewSet
from common.permissions import (
IsOrgAdmin, IsCurrentUserOrReadOnly, IsOrgAdminOrAppUser,
CanUpdateDeleteSuperUser,
CanUpdateDeleteUser,
)
from common.mixins import IDInCacheFilterMixin
from common.utils import get_logger
@ -36,7 +35,7 @@ class UserViewSet(IDInCacheFilterMixin, BulkModelViewSet):
search_fields = filter_fields
queryset = User.objects.exclude(role=User.ROLE_APP)
serializer_class = serializers.UserSerializer
permission_classes = (IsOrgAdmin, CanUpdateDeleteSuperUser)
permission_classes = (IsOrgAdmin, CanUpdateDeleteUser)
def send_created_signal(self, users):
if not isinstance(users, list):
@ -53,7 +52,7 @@ class UserViewSet(IDInCacheFilterMixin, BulkModelViewSet):
self.send_created_signal(users)
def get_queryset(self):
queryset = current_org.get_org_users().prefetch_related('groups')
queryset = current_org.get_org_members().prefetch_related('groups')
return queryset
def get_permissions(self):
@ -61,32 +60,17 @@ class UserViewSet(IDInCacheFilterMixin, BulkModelViewSet):
self.permission_classes = (IsOrgAdminOrAppUser,)
return super().get_permissions()
def _deny_permission(self, instance):
"""
check current user has permission to handle instance
(update, destroy, bulk_update, bulk destroy)
"""
if instance.is_superuser and not self.request.user.is_superuser:
return True
return False
def _bulk_deny_permission(self, instances):
deny_instances = [i for i in instances if self._deny_permission(i)]
if len(deny_instances) > 0:
return True
else:
return False
def allow_bulk_destroy(self, qs, filtered):
return False
def perform_bulk_update(self, serializer):
users_ids = [d.get("id") or d.get("pk") for d in serializer.validated_data]
users = User.objects.filter(id__in=users_ids)
deny_instances = [str(i.id) for i in users if self._deny_permission(i)]
if deny_instances:
msg = "{} can't be update".format(deny_instances)
raise ValidationError({"id": msg})
# TODO: 需要测试
users_ids = [
d.get("id") or d.get("pk") for d in serializer.validated_data
]
users = current_org.get_org_members().filter(id__in=users_ids)
for user in users:
self.check_object_permissions(self.request, user)
return super().perform_bulk_update(serializer)

View File

@ -335,7 +335,7 @@ class UserGroupForm(OrgModelForm):
return
users_field = self.fields.get('users')
if hasattr(users_field, 'queryset'):
users_field.queryset = current_org.get_org_users()
users_field.queryset = current_org.get_org_members(exclude=('Auditor',))
def save(self, commit=True):
group = super().save(commit=commit)

View File

@ -5,7 +5,6 @@ import uuid
import base64
import string
import random
from collections import OrderedDict
from django.conf import settings
from django.contrib.auth.hashers import make_password
@ -16,6 +15,7 @@ from django.utils.translation import ugettext_lazy as _
from django.utils import timezone
from django.shortcuts import reverse
from orgs.utils import current_org
from common.utils import get_signer, date_expired_default, get_logger
from common import fields
@ -132,7 +132,16 @@ class RoleMixin:
@property
def role_display(self):
return self.get_role_display()
if not current_org.is_real():
return self.get_role_display()
roles = []
if self in current_org.get_org_admins():
roles.append(str(_('Org admin')))
if self in current_org.get_org_auditors():
roles.append(str(_('Org auditor')))
if self in current_org.get_org_users():
roles.append(str(_('User')))
return " | ".join(roles)
@property
def is_superuser(self):
@ -149,26 +158,14 @@ class RoleMixin:
self.role = 'User'
@property
def admin_orgs(self):
from orgs.models import Organization
return Organization.get_user_admin_orgs(self)
@property
def is_org_admin(self):
if self.is_superuser or self.admin_orgs.exists():
return True
else:
return False
@property
def is_auditor(self):
def is_super_auditor(self):
return self.role == 'Auditor'
@property
def is_common_user(self):
if self.is_org_admin:
return False
if self.is_auditor:
if self.is_org_auditor:
return False
if self.is_app:
return False
@ -178,6 +175,52 @@ class RoleMixin:
def is_app(self):
return self.role == 'App'
@property
def user_orgs(self):
from orgs.models import Organization
return Organization.get_user_user_orgs(self)
@property
def admin_orgs(self):
from orgs.models import Organization
return Organization.get_user_admin_orgs(self)
@property
def audit_orgs(self):
from orgs.models import Organization
return Organization.get_user_audit_orgs(self)
@property
def admin_or_audit_orgs(self):
from orgs.models import Organization
return Organization.get_user_admin_or_audit_orgs(self)
@property
def is_org_admin(self):
if self.is_superuser or self.related_admin_orgs.exists():
return True
else:
return False
@property
def is_org_auditor(self):
if self.is_super_auditor or self.related_audit_orgs.exists():
return True
else:
return False
@property
def can_admin_current_org(self):
return current_org.can_admin_by(self)
@property
def can_audit_current_org(self):
return current_org.can_audit_by(self)
@property
def can_admin_or_audit_current_org(self):
return self.can_admin_current_org or self.can_audit_current_org
@property
def is_staff(self):
if self.is_authenticated and self.is_valid:

View File

@ -1,5 +1,6 @@
# -*- coding: utf-8 -*-
#
import copy
from django.utils.translation import ugettext_lazy as _
from rest_framework import serializers
@ -8,6 +9,7 @@ from common.utils import validate_ssh_public_key
from common.mixins import BulkSerializerMixin
from common.fields import StringManyToManyField
from common.serializers import AdaptedBulkListSerializer
from common.permissions import CanUpdateDeleteUser
from orgs.mixins.serializers import BulkOrgResourceModelSerializer
from ..models import User, UserGroup
@ -22,6 +24,9 @@ __all__ = [
class UserSerializer(BulkSerializerMixin, serializers.ModelSerializer):
can_update = serializers.SerializerMethodField()
can_delete = serializers.SerializerMethodField()
class Meta:
model = User
list_serializer_class = AdaptedBulkListSerializer
@ -32,6 +37,7 @@ class UserSerializer(BulkSerializerMixin, serializers.ModelSerializer):
'comment', 'source', 'source_display', 'is_valid', 'is_expired',
'is_active', 'created_by', 'is_first_login',
'date_password_last_updated', 'date_expired', 'avatar_url',
'can_update', 'can_delete',
]
extra_kwargs = {
'password': {'write_only': True, 'required': False, 'allow_null': True, 'allow_blank': True},
@ -43,10 +49,22 @@ class UserSerializer(BulkSerializerMixin, serializers.ModelSerializer):
'is_valid': {'label': _('Is valid')},
'is_expired': {'label': _('Is expired')},
'avatar_url': {'label': _('Avatar url')},
'created_by': {'read_only': True, 'allow_blank': True},
'source': {'read_only': True},
'created_by': {'read_only': True, 'allow_blank': True},
'can_update': {'read_only': True},
'can_delete': {'read_only': True},
}
def get_can_update(self, obj):
return CanUpdateDeleteUser.has_update_object_permission(
self.context['request'], self.context['view'], obj
)
def get_can_delete(self, obj):
return CanUpdateDeleteUser.has_delete_object_permission(
self.context['request'], self.context['view'], obj
)
def validate_role(self, value):
request = self.context.get('request')
if not request.user.is_superuser and value != User.ROLE_USER:
@ -67,20 +85,24 @@ class UserSerializer(BulkSerializerMixin, serializers.ModelSerializer):
raise serializers.ValidationError(msg)
return password
def validate_groups(self, groups):
role = self.initial_data.get('role')
if self.instance:
role = role or self.instance.role
if role == User.ROLE_AUDITOR:
return []
return groups
@staticmethod
def change_password_to_raw(validated_data):
password = validated_data.pop('password', None)
def change_password_to_raw(attrs):
password = attrs.pop('password', None)
if password:
validated_data['password_raw'] = password
return validated_data
attrs['password_raw'] = password
return attrs
def create(self, validated_data):
validated_data = self.change_password_to_raw(validated_data)
return super().create(validated_data)
def update(self, instance, validated_data):
validated_data = self.change_password_to_raw(validated_data)
return super().update(instance, validated_data)
def validate(self, attrs):
attrs = self.change_password_to_raw(attrs)
return attrs
class UserPKUpdateSerializer(serializers.ModelSerializer):
@ -119,6 +141,13 @@ class UserGroupSerializer(BulkOrgResourceModelSerializer):
'created_by': {'label': _('Created by'), 'read_only': True}
}
def validate_users(self, users):
for user in users:
if user.is_super_auditor:
msg = _('Auditors cannot be join in the group')
raise serializers.ValidationError(msg)
return users
class UserGroupListSerializer(UserGroupSerializer):
users = StringManyToManyField(many=True, read_only=True)
@ -140,4 +169,4 @@ class ChangeUserPasswordSerializer(serializers.ModelSerializer):
class ResetOTPSerializer(serializers.Serializer):
msg = serializers.CharField(read_only=True)
msg = serializers.CharField(read_only=True)

View File

@ -61,6 +61,17 @@
<link rel="stylesheet" type="text/css" href={% static "css/plugins/daterangepicker/daterangepicker.css" %} />
<script>
var role_id = '#' + '{{ form.role.id_for_label }}';
var groups_id = '#' + '{{ form.groups.id_for_label }}';
function fieldDisplay(){
var role = $(role_id).val();
if (role === 'Auditor'){
$(groups_id).closest('.form-group').addClass('hidden');
}
else {
$(groups_id).closest('.form-group').removeClass('hidden');
}}
var dateOptions = {
singleDatePicker: true,
showDropdowns: true,
@ -76,7 +87,10 @@
$('#id_date_expired').daterangepicker(dateOptions);
var mfa_radio = $('#id_otp_level');
mfa_radio.addClass("form-inline");
mfa_radio.children().css("margin-right","15px")
mfa_radio.children().css("margin-right","15px");
fieldDisplay()
}).on('change', role_id, function(){
fieldDisplay();
})
</script>
{% endblock %}

View File

@ -22,11 +22,11 @@
<a href="{% url 'users:user-granted-asset' pk=user_object.id %}" class="text-center"><i class="fa fa-cubes"></i> {% trans 'Asset granted' %}</a>
</li>
<li class="pull-right">
<a class="btn btn-outline {% if user_object.is_superuser and not request.user.is_superuser %} disabled {% else %} btn-default {% endif %}" href="{% url 'users:user-update' pk=user_object.id %}"><i class="fa fa-edit"></i>{% trans 'Update' %}</a>
<a class="btn btn-outline {% if can_update %} btn-default {% else %} disabled {% endif %}" href="{% url 'users:user-update' pk=user_object.id %}"><i class="fa fa-edit"></i>{% trans 'Update' %}</a>
</li>
<li class="pull-right">
<a class="btn btn-outline {% if request.user == user_object or user_object.username == "admin" or user_object.is_superuser and not request.user.is_superuser %} disabled {% else %} btn-danger btn-delete-user {% endif %}">
<a class="btn btn-outline {% if can_delete %} btn-danger btn-delete-user {% else %} disabled {% endif %}">
<i class="fa fa-trash-o"></i>{% trans 'Delete' %}
</a>
</li>
@ -85,7 +85,7 @@
{% endif %}
<tr>
<td>{% trans 'Role' %}:</td>
<td><b>{{ user_object.get_role_display }}</b></td>
<td><b>{{ user_object.role_display }}</b></td>
</tr>
<tr>
<td>{% trans 'MFA certification' %}:</td>
@ -212,44 +212,46 @@
</div>
</div>
<div class="panel panel-info">
<div class="panel-heading">
<i class="fa fa-info-circle"></i> {% trans 'User group' %}
</div>
<div class="panel-body">
<table class="table group_edit">
<tbody>
<form>
<tr>
<td colspan="2" class="no-borders">
<select data-placeholder="{% trans 'Join user groups' %}" id="groups_selected" class="select2" style="width: 100%" multiple="" tabindex="4">
{% for group in groups %}
<option value="{{ group.id }}" id="opt_{{ group.id }}" >{{ group.name }}</option>
{% endfor %}
</select>
</td>
</tr>
<tr>
<td colspan="2" class="no-borders">
<button type="button" class="btn btn-info btn-small" id="btn_join_group">{% trans 'Join' %}</button>
</td>
</tr>
</form>
{% if user_object.is_current_org_admin %}
<div class="panel panel-info">
<div class="panel-heading">
<i class="fa fa-info-circle"></i> {% trans 'User group' %}
</div>
<div class="panel-body">
<table class="table group_edit">
<tbody>
<form>
<tr>
<td colspan="2" class="no-borders">
<select data-placeholder="{% trans 'Join user groups' %}" id="groups_selected" class="select2" style="width: 100%" multiple="" tabindex="4">
{% for group in groups %}
<option value="{{ group.id }}" id="opt_{{ group.id }}" >{{ group.name }}</option>
{% endfor %}
</select>
</td>
</tr>
<tr>
<td colspan="2" class="no-borders">
<button type="button" class="btn btn-info btn-small" id="btn_join_group">{% trans 'Join' %}</button>
</td>
</tr>
</form>
{% for group in user_object.groups.all %}
<tr>
<td >
<b class="bdg_group" data-gid={{ group.id }}>{{ group.name }}</b>
</td>
<td>
<button class="btn btn-danger pull-right btn-xs btn_leave_group" type="button"><i class="fa fa-minus"></i></button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
{% for group in user_object.groups.all %}
<tr>
<td >
<b class="bdg_group" data-gid={{ group.id }}>{{ group.name }}</b>
</td>
<td>
<button class="btn btn-danger pull-right btn-xs btn_leave_group" type="button"><i class="fa fa-minus"></i></button>
</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>
</div>
{% endif %}
</div>
</div>
</div>

View File

@ -99,7 +99,7 @@ function initTable() {
{targets: 7, createdCell: function (td, cellData, rowData) {
var name = htmlEscape(rowData.name);
var update_btn = "";
if (rowData.role === 'Admin' && ('{{ request.user.role }}' !== 'Admin')) {
if (rowData.can_update === false){
update_btn = '<a class="btn btn-xs disabled btn-info">{% trans "Update" %}</a>';
}
else{
@ -107,7 +107,7 @@ function initTable() {
}
var del_btn = "";
if (rowData.id === 1 || rowData.username === "admin" || rowData.username === "{{ request.user.username }}" || (rowData.role === 'Admin' && ('{{ request.user.role }}' !== 'Admin'))) {
if (rowData.can_delete === false){
del_btn = '<a class="btn btn-xs btn-danger m-l-xs" disabled>{% trans "Delete" %}</a>'
.replace('{{ DEFAULT_PK }}', cellData)
.replace('99991938', name);

View File

@ -76,7 +76,8 @@ class UserGroupDetailView(PermissionsMixin, DetailView):
permission_classes = [IsOrgAdmin]
def get_context_data(self, **kwargs):
users = current_org.get_org_users().exclude(id__in=self.object.users.all())
users = current_org.get_org_members(exclude=('Auditor',)).exclude(
groups=self.object)
context = {
'app': _('Users'),
'action': _('User group detail'),

View File

@ -27,6 +27,7 @@ from common.utils import get_logger, ssh_key_gen
from common.permissions import (
PermissionsMixin, IsOrgAdmin, IsValidUser,
UserCanUpdatePassword, UserCanUpdateSSHKey,
CanUpdateDeleteUser,
)
from orgs.utils import current_org
from .. import forms
@ -86,7 +87,7 @@ class UserCreateView(PermissionsMixin, SuccessMessageMixin, CreateView):
user.created_by = self.request.user.username or 'System'
user.save()
if current_org and current_org.is_real():
user.orgs.add(current_org.id)
user.related_user_orgs.add(current_org.id)
post_user_create.send(self.__class__, user=user)
return super().form_valid(form)
@ -189,13 +190,19 @@ class UserDetailView(PermissionsMixin, DetailView):
'action': _('User detail'),
'groups': groups,
'unblock': is_need_unblock(key_block),
'can_update': CanUpdateDeleteUser.has_update_object_permission(
self.request, self, user
),
'can_delete': CanUpdateDeleteUser.has_delete_object_permission(
self.request, self, user
),
}
kwargs.update(context)
return super().get_context_data(**kwargs)
def get_queryset(self):
queryset = super().get_queryset()
org_users = current_org.get_org_users().values_list('id', flat=True)
org_users = current_org.get_org_members().values_list('id', flat=True)
queryset = queryset.filter(id__in=org_users)
return queryset