Commit 948214ca authored by ibuler's avatar ibuler

Merge branch 'audits'

parents 09794801 df94d11f
......@@ -23,17 +23,20 @@ class TerminalRegisterView(ListCreateAPIView):
def create(self, request, *args, **kwargs):
name = request.data.get('name', '')
remote_addr = request.META.get('X-Real-IP') or request.META.get('REMOTE_ADDR')
serializer = self.serializer_class(data={'name': name, 'remote_addr': remote_addr})
remote_addr = request.META.get('X-Real-IP') or \
request.META.get('REMOTE_ADDR')
serializer = self.serializer_class(
data={'name': name, 'remote_addr': remote_addr})
if get_object_or_none(Terminal, name=name):
return Response({'msg': 'Already register, Need administrator active it'}, status=200)
return Response({'msg': 'Already register, Need '
'administrator active it'}, status=200)
if serializer.is_valid():
terminal = serializer.save()
app_user, access_key = terminal.create_related_app_user()
data = {}
data['applications'] = copy.deepcopy(serializer.data)
data['terminal'] = copy.deepcopy(serializer.data)
data['user'] = app_user.to_json()
data['access_key_id'] = access_key.id
data['access_key_secret'] = access_key.secret
......@@ -54,11 +57,11 @@ class TerminalViewSet(viewsets.ModelViewSet):
def create(self, request, *args, **kwargs):
return Response({'msg': 'Use register view except that'}, status=404)
def destroy(self, request, *args, **kwargs):
instance = self.get_object()
if instance.user is not None:
instance.user.delete()
return super(TerminalViewSet, self).destroy(request, *args, **kwargs)
# def destroy(self, request, *args, **kwargs):
# instance = self.get_object()
# if instance.user is not None:
# instance.user.delete()
# return super(TerminalViewSet, self).destroy(request, *args, **kwargs)
class TerminalHeatbeatViewSet(viewsets.ModelViewSet):
......
......@@ -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
......
......@@ -214,23 +214,9 @@ class AdminUserForm(forms.ModelForm):
class SystemUserForm(forms.ModelForm):
# Admin user assets define, let user select, save it in form not in view
assets = forms.ModelMultipleChoiceField(queryset=Asset.objects.all(),
label=_('Asset'),
required=False,
widget=forms.SelectMultiple(
attrs={'class': 'select2', 'data-placeholder': _('Select assets')})
)
asset_groups = forms.ModelMultipleChoiceField(queryset=AssetGroup.objects.all(),
label=_('Asset group'),
required=False,
widget=forms.SelectMultiple(
attrs={'class': 'select2',
'data-placeholder': _('Select asset groups')})
)
auto_generate_key = forms.BooleanField(initial=True)
auto_generate_key = forms.BooleanField(initial=True, required=False)
# Form field name can not start with `_`, so redefine it,
password = forms.CharField(widget=forms.PasswordInput, max_length=100, min_length=8, strip=True,
help_text=_('If also set private key, use that first'), required=False)
password = forms.CharField(widget=forms.PasswordInput, max_length=100, min_length=8, strip=True)
# Need use upload private key file except paste private key content
private_key_file = forms.FileField(required=False)
......@@ -242,22 +228,13 @@ class SystemUserForm(forms.ModelForm):
initial['asset_groups'] = kwargs['instance'].asset_groups.all()
super(SystemUserForm, self).__init__(*args, **kwargs)
def _save_m2m(self):
# Save assets relation with admin user
super(SystemUserForm, self)._save_m2m()
assets = self.cleaned_data['assets']
asset_groups = self.cleaned_data['asset_groups']
self.instance.assets.clear()
self.instance.assets.add(*tuple(assets))
self.instance.asset_groups.clear()
self.instance.asset_groups.add(*tuple(asset_groups))
def save(self, commit=True):
# Because we define custom field, so we need rewrite :method: `save`
system_user = super(SystemUserForm, self).save(commit=commit)
password = self.cleaned_data['password']
private_key_file = self.cleaned_data['private_key_file']
if system_user.auth_method == 'P':
if password:
system_user.password = password
print(password)
......@@ -268,11 +245,30 @@ class SystemUserForm(forms.ModelForm):
system_user.save()
return self.instance
# Todo: check valid
# def clean_private_key_file(self):
# if not self.cleaned_data['auto_generate_key']:
# if not self.cleaned_data['private_key_file']:
# raise forms.ValidationError(_('Private key required'))
# def clean_password(self):
# if self.cleaned_data['auth_method'] == 'P':
# if not self.cleaned_data['password']:
# raise forms.ValidationError(_('Password required'))
# return self.cleaned_data['password']
# def clean(self):
# password = self.cleaned_data['password']
# private_key_file = self.cleaned_data.get('private_key_file', '')
#
# if not (password or private_key_file):
# raise forms.ValidationError(_('Password and private key file must be input one'))
class Meta:
model = SystemUser
fields = [
'name', 'username', 'protocol', 'auto_generate_key', 'password', 'private_key_file', 'auth_method',
'auto_push', 'auto_update', 'sudo', 'comment', 'shell', 'home', 'uid',
'auto_push', 'sudo', 'comment', 'shell', 'home', 'uid',
]
widgets = {
'name': forms.TextInput(attrs={'placeholder': _('Name')}),
......@@ -282,7 +278,6 @@ class SystemUserForm(forms.ModelForm):
'name': '* required',
'username': '* required',
'auto_push': 'Auto push system user to asset',
'auto_update': 'Auto update system user ssh key',
}
......
......@@ -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):
......
......@@ -27,23 +27,31 @@
</div>
</div>
<div class="ibox-content">
{% if form.no_field_errors %}
<div class="alert alert-danger">
{{ form.non_field_errors }}
</div>
{% endif %}
<form enctype="multipart/form-data" method="post" class="form-horizontal" action="" >
{% csrf_token %}
<h3>{% trans 'Basic' %}</h3>
{{ form.name|bootstrap_horizontal }}
{{ form.username|bootstrap_horizontal }}
{{ form.protocol|bootstrap_horizontal }}
<h3>{% trans 'Auth' %}</h3>
{{ form.auth_method|bootstrap_horizontal }}
<div class="password-auth hidden">
{{ form.password|bootstrap_horizontal }}
</div>
<div class="public-key-auth">
<div class="form-group">
<label for="{{ form.auto_generate_key.id_for_label }}" class="col-sm-2 control-label">{% trans 'Auto generate key' %}</label>
<div class="col-sm-8">
{{ form.auto_generate_key}}
</div>
</div>
{{ form.password|bootstrap_horizontal }}
<div>
{{ form.private_key_file|bootstrap_horizontal }}
<div class="form-group">
<label for="{{ form.as_default.id_for_label }}" class="col-sm-2 control-label">{% trans 'As default' %}</label>
<div class="col-sm-8">
{{ form.as_default}}
</div>
</div>
<div class="form-group">
......@@ -52,20 +60,12 @@
{{ form.auto_push}}
</div>
</div>
<div class="form-group">
<label for="{{ form.as_update.id_for_label }}" class="col-sm-2 control-label">{% trans 'Auto update' %}</label>
<div class="col-sm-8">
{{ form.auto_update}}
</div>
</div>
{{ form.assets|bootstrap_horizontal }}
{{ form.asset_groups|bootstrap_horizontal }}
<h3>{% trans 'Other' %}</h3>
{{ form.sudo|bootstrap_horizontal }}
{{ form.comment|bootstrap_horizontal }}
{{ form.home|bootstrap_horizontal }}
{{ form.shell|bootstrap_horizontal }}
{{ form.uid|bootstrap_horizontal }}
{{ form.comment|bootstrap_horizontal }}
<div class="form-group">
<div class="col-sm-4 col-sm-offset-2">
<button class="btn btn-white" type="reset">{% trans 'Reset' %}</button>
......@@ -81,8 +81,36 @@
{% endblock %}
{% block custom_foot_js %}
<script>
var auth_method = '#'+'{{ form.auth_method.id_for_label }}';
var auto_generate_key = '#'+'{{ form.auto_generate_key.id_for_label }}';
function authMethodDisplay() {
if ($(auth_method).val() == 'P') {
$('.password-auth').removeClass('hidden');
$('.public-key-auth').addClass('hidden');
$('#'+'{{ form.password.id_for_label }}').attr('required', 'required');
} else if ($(auth_method).val() == 'K') {
$('.password-auth').addClass('hidden');
$('.public-key-auth').removeClass('hidden');
if ($(auto_generate_key).prop('checked')){
$('#'+'{{ form.private_key_file.id_for_label }}').closest('.form-group').addClass('hidden');
} else {
$('#'+'{{ form.private_key_file.id_for_label }}').closest('.form-group').removeClass('hidden');
{# $('#'+'{{ form.private_key_file.id_for_label }}').attr('required', 'required');#}
}
}
}
$(document).ready(function () {
$('.select2').select2();
authMethodDisplay();
$(auth_method).change(function () {
authMethodDisplay();
});
$(auto_generate_key).change(function () {
authMethodDisplay();
});
if ($('#'+'{{ form.protocol.id_for_label }}').val() == 'telnet') {
$('#'+'{{ form.auto_generate_key.id_for_label }}').closest('.form-group').remove();
......
......@@ -547,6 +547,10 @@ class SystemUserCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateVi
kwargs.update(context)
return super(SystemUserCreateView, self).get_context_data(**kwargs)
def form_invalid(self, form):
print(form.errors)
return super(SystemUserCreateView, self).form_invalid(form)
def get_success_message(self, cleaned_data):
success_message = _('Create system user <a href="%s">%s</a> successfully.' %
(
......
......@@ -6,6 +6,7 @@ from __future__ import absolute_import, unicode_literals
from rest_framework import generics, viewsets
from rest_framework.views import APIView, Response
from rest_framework_bulk import BulkModelViewSet
from . import models, serializers
from .hands import IsSuperUserOrAppUser, Terminal, IsAppUser
......@@ -51,13 +52,3 @@ class CommandLogViewSet(viewsets.ModelViewSet):
serializer_class = serializers.CommandLogSerializer
permission_classes = (IsSuperUserOrAppUser,)
# class CommandLogTitleApi(APIView):
# def get(self, request):
# response = [
# {"name": "command_no", "title": "ID", "type": "number"},
# {"name": "command", "title": "Title", "visible": True, "filterable": True},
# {"name": "datetime", "title": "Datetime", "type"},
# {"name": "output", "title": "Output", "filterable": True},
# ]
#
\ No newline at end of file
......@@ -17,10 +17,13 @@ class LoginLog(models.Model):
username = models.CharField(max_length=20, verbose_name=_('Username'))
name = models.CharField(max_length=20, blank=True, verbose_name=_('Name'))
login_type = models.CharField(choices=LOGIN_TYPE_CHOICE, max_length=2, verbose_name=_('Login type'))
login_type = models.CharField(choices=LOGIN_TYPE_CHOICE, max_length=2,
verbose_name=_('Login type'))
login_ip = models.GenericIPAddressField(verbose_name=_('Login ip'))
login_city = models.CharField(max_length=100, blank=True, null=True, verbose_name=_('Login city'))
user_agent = models.CharField(max_length=100, blank=True, null=True, verbose_name=_('User agent'))
login_city = models.CharField(max_length=100, blank=True, null=True,
verbose_name=_('Login city'))
user_agent = models.CharField(max_length=100, blank=True, null=True,
verbose_name=_('User agent'))
date_login = models.DateTimeField(auto_now_add=True, verbose_name=_('Date login'))
class Meta:
......@@ -66,7 +69,8 @@ class ProxyLog(models.Model):
class CommandLog(models.Model):
proxy_log = models.ForeignKey(ProxyLog, on_delete=models.CASCADE, related_name='commands')
proxy_log = models.ForeignKey(ProxyLog, on_delete=models.CASCADE,
related_name='commands')
command_no = models.IntegerField()
command = models.CharField(max_length=1000, blank=True)
output = models.TextField(blank=True)
......@@ -78,7 +82,8 @@ class CommandLog(models.Model):
@property
def output_decode(self):
try:
return base64.b64decode(self.output).replace('\n', '<br />')
return base64.b64decode(self.output).decode('utf-8') \
.replace('\n', '<br />')
except UnicodeDecodeError:
return 'UnicodeDecodeError'
......
......@@ -13,8 +13,9 @@ class ProxyLogSerializer(serializers.ModelSerializer):
class Meta:
model = models.ProxyLog
fields = ['id', 'name', 'username', 'hostname', 'ip', 'system_user', 'login_type', 'terminal',
'log_file', 'was_failed', 'is_finished', 'date_start', 'date_finished', 'time',
fields = ['id', 'name', 'username', 'hostname', 'ip', 'system_user',
'login_type', 'terminal', 'log_file', 'was_failed',
'is_finished', 'date_start', 'date_finished', 'time',
'command_length', "commands_dict"]
@staticmethod
......@@ -32,3 +33,4 @@ class ProxyLogSerializer(serializers.ModelSerializer):
class CommandLogSerializer(serializers.ModelSerializer):
class Meta:
model = models.CommandLog
fields = '__all__'
......@@ -83,7 +83,7 @@
<td>{{ command.proxy_log.system_user }}</td>
<td><a href="{% url 'audits:proxy-log-detail' pk=command.proxy_log.id %}">{{ command.proxy_log.id}}</a></td>
<td>{{ command.datetime }}</td>
<td>{{ command.output_decode |safe }}</td>
<td>{{ command.output_decode|safe }}</td>
</tr>
{% endfor %}
</tbody>
......
......@@ -52,7 +52,7 @@
<tr>
<td>{{ command.command_no }}</td>
<td>{{ command.command }}</td>
<td>{{ command.output_decode |safe }}</td>
<td>{{ command.output_decode|safe }}</td>
<td>{{ command.datetime }}</td>
</tr>
{% endfor %}
......@@ -68,30 +68,6 @@
</div>
</div>
</div>
<div class="col-sm-5" style="padding-left: 0;padding-right: 0">
<div class="ibox float-e-margins">
<div class="ibox-title">
<span style="float: left">{% trans 'Detail' %} <b>{{ user_object.name }}</b></span>
<div class="ibox-tools">
<a class="collapse-link">
<i class="fa fa-chevron-up"></i>
</a>
<a class="dropdown-toggle" data-toggle="dropdown" href="#">
<i class="fa fa-wrench"></i>
</a>
<ul class="dropdown-menu dropdown-user">
</ul>
<a class="close-link">
<i class="fa fa-times"></i>
</a>
</div>
</div>
<div class="ibox-content">
<table class="table2 table-stripped toggle-arrow-tiny" data-page-size="8">
</table>
</div>
</div>
</div>
</div>
</div>
</div>
......
......@@ -14,17 +14,16 @@ from .models import ProxyLog, CommandLog, LoginLog
from .hands import User, Asset, SystemUser, AdminUserRequiredMixin
date_now = timezone.localtime(timezone.now())
now_s = date_now.strftime('%m/%d/%Y')
seven_days_ago_s = (date_now-timezone.timedelta(7)).strftime('%m/%d/%Y')
class ProxyLogListView(AdminUserRequiredMixin, ListView):
model = ProxyLog
template_name = 'audits/proxy_log_list.html'
context_object_name = 'proxy_log_list'
def get_queryset(self):
date_now = timezone.localtime(timezone.now())
now_s = date_now.strftime('%m/%d/%Y')
seven_days_ago_s = (date_now-timezone.timedelta(7)).strftime('%m/%d/%Y')
self.queryset = super(ProxyLogListView, self).get_queryset()
self.keyword = keyword = self.request.GET.get('keyword', '')
self.username = username = self.request.GET.get('username', '')
......@@ -37,7 +36,8 @@ class ProxyLogListView(AdminUserRequiredMixin, ListView):
date_from = timezone.datetime.strptime(date_from_s, '%m/%d/%Y')
self.queryset = self.queryset.filter(date_start__gt=date_from)
if date_to_s:
date_to = timezone.datetime.strptime(date_to_s + ' 23:59:59', '%m/%d/%Y %H:%M:%S')
date_to = timezone.datetime.strptime(date_to_s + ' 23:59:59',
'%m/%d/%Y %H:%M:%S')
self.queryset = self.queryset.filter(date_start__lt=date_to)
if username:
self.queryset = self.queryset.filter(username=username)
......@@ -54,7 +54,6 @@ class ProxyLogListView(AdminUserRequiredMixin, ListView):
return self.queryset
def get_context_data(self, **kwargs):
print(self.date_to_s)
context = {
'app': _('Audits'),
'action': _('Proxy log list'),
......@@ -110,6 +109,9 @@ class CommandLogListView(AdminUserRequiredMixin, ListView):
context_object_name = 'command_list'
def get_queryset(self):
date_now = timezone.localtime(timezone.now())
now_s = date_now.strftime('%m/%d/%Y')
seven_days_ago_s = (date_now-timezone.timedelta(7)).strftime('%m/%d/%Y')
self.queryset = super(CommandLogListView, self).get_queryset()
self.keyword = keyword = self.request.GET.get('keyword', '')
self.sort = sort = self.request.GET.get('sort', '-datetime')
......@@ -161,6 +163,9 @@ class LoginLogListView(AdminUserRequiredMixin, ListView):
context_object_name = 'login_log_list'
def get_queryset(self):
date_now = timezone.localtime(timezone.now())
now_s = date_now.strftime('%m/%d/%Y')
seven_days_ago_s = (date_now - timezone.timedelta(7)).strftime('%m/%d/%Y')
self.queryset = super(LoginLogListView, self).get_queryset()
self.keyword = keyword = self.request.GET.get('keyword', '')
self.username = username = self.request.GET.get('username', '')
......
......@@ -16,5 +16,6 @@ app = Celery('jumpserver')
# Using a string here means the worker will not have to
# pickle the object when using Windows.
app.config_from_object('django.conf:settings')
app.autodiscover_tasks(lambda: [app_config.split('.')[0] for app_config in settings.INSTALLED_APPS])
app.autodiscover_tasks(lambda: [app_config.split('.')[0]
for app_config in settings.INSTALLED_APPS])
# ~*~ 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 +82,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 +107,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 +151,7 @@ class MyGrantedAssetsGroupsApi(APIView):
class MyAssetGroupAssetsApi(ListAPIView):
"""授权用户资产组下的资产列表, 非该资产组的所有资产,而是被授权的"""
permission_classes = (IsValidUser,)
serializer_class = AssetGrantedSerializer
......@@ -152,8 +163,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
......@@ -186,3 +198,23 @@ class UserGroupGrantedAssetGroupsApi(ListAPIView):
else:
queryset = []
return queryset
class ValidateUserAssetPermissionView(APIView):
permission_classes = (IsAppUser,)
@staticmethod
def get(request):
user_id = request.query_params.get('user_id', '')
asset_id = request.query_params.get('asset_id', '')
system_id = request.query_params.get('system_user_id', '')
user = get_object_or_404(User, id=user_id)
asset = get_object_or_404(Asset, id=asset_id)
system_user = get_object_or_404(SystemUser, id=system_id)
assets_granted = get_user_granted_assets(user)
if system_user in assets_granted.get(asset, []):
return Response({'msg': True}, status=200)
else:
return Response({'msg': False}, status=403)
......@@ -11,6 +11,7 @@ from .hands import User
class AssetPermissionSerializer(serializers.ModelSerializer):
class Meta:
model = AssetPermission
fields = '__all__'
class UserAssetPermissionSerializer(AssetPermissionSerializer):
......@@ -23,4 +24,3 @@ class UserAssetPermissionSerializer(AssetPermissionSerializer):
else:
return False
......@@ -7,30 +7,50 @@ from .. import api
app_name = 'perms'
router = routers.DefaultRouter()
router.register('v1/asset-permissions', api.AssetPermissionViewSet, 'asset-permission')
router.register('v1/asset-permissions',
api.AssetPermissionViewSet,
'asset-permission')
urlpatterns = [
url(r'^v1/user/my/assets/$', api.MyGrantedAssetsApi.as_view(), name='my-assets'),
url(r'^v1/user/my/asset-groups/$', api.MyGrantedAssetsGroupsApi.as_view(), name='my-asset-groups'),
url(r'^v1/user/my/asset-group/(?P<pk>[0-9]+)/assets/$', api.MyAssetGroupAssetsApi.as_view(),
# 用户可以使用自己的Token或其它认证查看自己授权的资产,资产组等
url(r'^v1/user/my/assets/$',
api.MyGrantedAssetsApi.as_view(),
name='my-assets'),
url(r'^v1/user/my/asset-groups/$',
api.MyGrantedAssetsGroupsApi.as_view(),
name='my-asset-groups'),
url(r'^v1/user/my/asset-group/(?P<pk>[0-9]+)/assets/$',
api.MyAssetGroupAssetsApi.as_view(),
name='user-my-asset-group-assets'),
# Select user permission of asset and asset group
url(r'^v1/user/(?P<pk>[0-9]+)/assets/$', api.UserGrantedAssetsApi.as_view(), name='user-assets'),
url(r'^v1/user/(?P<pk>[0-9]+)/asset-groups/$', api.UserGrantedAssetGroupsApi.as_view(),
# 查询某个用户授权的资产和资产组
url(r'^v1/user/(?P<pk>[0-9]+)/assets/$',
api.UserGrantedAssetsApi.as_view(),
name='user-assets'),
url(r'^v1/user/(?P<pk>[0-9]+)/asset-groups/$',
api.UserGrantedAssetGroupsApi.as_view(),
name='user-asset-groups'),
# Select user group permission of asset and asset group
url(r'^v1/user-group/(?P<pk>[0-9]+)/assets/$', api.UserGroupGrantedAssetsApi.as_view(), name='user-group-assets'),
url(r'^v1/user-group/(?P<pk>[0-9]+)/asset-groups/$', api.UserGroupGrantedAssetGroupsApi.as_view(),
# 查询某个用户组授权的资产和资产组
url(r'^v1/user-group/(?P<pk>[0-9]+)/assets/$',
api.UserGroupGrantedAssetsApi.as_view(),
name='user-group-assets'),
url(r'^v1/user-group/(?P<pk>[0-9]+)/asset-groups/$',
api.UserGroupGrantedAssetGroupsApi.as_view(),
name='user-group-asset-groups'),
# Revoke permission api
url(r'^v1/asset-permissions/user/revoke/', api.RevokeUserAssetPermission.as_view(),
# 回收用户或用户组授权
url(r'^v1/asset-permissions/user/revoke/$',
api.RevokeUserAssetPermission.as_view(),
name='revoke-user-asset-permission'),
url(r'^v1/asset-permissions/user-group/revoke/', api.RevokeUserGroupAssetPermission.as_view(),
url(r'^v1/asset-permissions/user-group/revoke/$',
api.RevokeUserGroupAssetPermission.as_view(),
name='revoke-user-group-asset-permission'),
# 验证用户是否有某个资产和系统用户的权限
url(r'v1/asset-permission/user/validate/$',
api.ValidateUserAssetPermissionView.as_view(),
name='validate-user-asset-permission')
]
urlpatterns += router.urls
......
......@@ -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:
......
......@@ -39,7 +39,8 @@ class AssetPermissionListView(AdminUserRequiredMixin, ListView):
self.sort = sort = self.request.GET.get('sort', '-date_created')
if keyword:
self.queryset = self.queryset.filter(Q(users__name__contains=keyword) |
self.queryset = self.queryset\
.filter(Q(users__name__contains=keyword) |
Q(users__username__contains=keyword) |
Q(user_groups__name__contains=keyword) |
Q(assets__ip__contains=keyword) |
......@@ -54,7 +55,9 @@ class AssetPermissionListView(AdminUserRequiredMixin, ListView):
return self.queryset
class AssetPermissionCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateView):
class AssetPermissionCreateView(AdminUserRequiredMixin,
SuccessMessageMixin,
CreateView):
model = AssetPermission
form_class = AssetPermissionForm
template_name = 'perms/asset_permission_create_update.html'
......@@ -69,11 +72,11 @@ class AssetPermissionCreateView(AdminUserRequiredMixin, SuccessMessageMixin, Cre
return super(AssetPermissionCreateView, self).get_context_data(**kwargs)
def get_success_message(self, cleaned_data):
success_message = _('Create asset permission <a href="%s"> %s </a> successfully.' %
(
reverse_lazy('perms:asset-permission-detail', kwargs={'pk': self.object.pk}),
self.object.name,
))
success_message = _(
'Create asset permission <a href="%s"> %s </a> '
'successfully.' % (reverse_lazy('perms:asset-permission-detail',
kwargs={'pk': self.object.pk}),
self.object.name,))
return success_message
......@@ -81,7 +84,8 @@ class AssetPermissionUpdateView(AdminUserRequiredMixin, UpdateView):
model = AssetPermission
form_class = AssetPermissionForm
template_name = 'perms/asset_permission_create_update.html'
success_message = _('Update asset permission <a href="%s"> %s </a> successfully.')
success_message = _('Update asset permission '
'<a href="%s"> %s </a> successfully.')
def get_context_data(self, **kwargs):
context = {
......@@ -92,7 +96,8 @@ class AssetPermissionUpdateView(AdminUserRequiredMixin, UpdateView):
return super(AssetPermissionUpdateView, self).get_context_data(**kwargs)
def get_success_url(self):
success_url = reverse_lazy('perms:asset-permission-detail', kwargs={'pk': self.object.pk})
success_url = reverse_lazy('perms:asset-permission-detail',
kwargs={'pk': self.object.pk})
return success_url
......@@ -105,7 +110,8 @@ class AssetPermissionDetailView(AdminUserRequiredMixin, DetailView):
context = {
'app': _('Perms'),
'action': _('Asset permission detail'),
'system_users_remain': [system_user for system_user in SystemUser.objects.all()
'system_users_remain': [
system_user for system_user in SystemUser.objects.all()
if system_user not in self.object.system_users.all()],
'system_users': self.object.system_users.all(),
}
......@@ -119,7 +125,9 @@ class AssetPermissionDeleteView(AdminUserRequiredMixin, DeleteView):
success_url = reverse_lazy('perms:asset-permission-list')
class AssetPermissionUserView(AdminUserRequiredMixin, SingleObjectMixin, ListView):
class AssetPermissionUserView(AdminUserRequiredMixin,
SingleObjectMixin,
ListView):
template_name = 'perms/asset_permission_user.html'
context_object_name = 'asset_permission'
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
......@@ -132,7 +140,9 @@ class AssetPermissionUserView(AdminUserRequiredMixin, SingleObjectMixin, ListVie
def get_queryset(self):
queryset = self.object.get_granted_users()
if self.keyword:
search_func = functools.partial(search_object_attr, value=self.keyword,
search_func = functools.partial(
search_object_attr,
value=self.keyword,
attr_list=['username', 'name', 'email'],
ignore_case=True)
queryset = filter(search_func, queryset)
......@@ -144,9 +154,12 @@ class AssetPermissionUserView(AdminUserRequiredMixin, SingleObjectMixin, ListVie
context = {
'app': _('Perms'),
'action': _('Asset permission user list'),
'users_remain': [user for user in User.objects.all() if user not in users_granted],
'users_remain': [
user for user in User.objects.all()
if user not in users_granted],
'user_groups': self.object.user_groups.all(),
'user_groups_remain': [user_group for user_group in UserGroup.objects.all()
'user_groups_remain': [
user_group for user_group in UserGroup.objects.all()
if user_group not in user_groups_granted],
'keyword': self.keyword,
}
......@@ -154,7 +167,9 @@ class AssetPermissionUserView(AdminUserRequiredMixin, SingleObjectMixin, ListVie
return super(AssetPermissionUserView, self).get_context_data(**kwargs)
class AssetPermissionAssetView(AdminUserRequiredMixin, SingleObjectMixin, ListView):
class AssetPermissionAssetView(AdminUserRequiredMixin,
SingleObjectMixin,
ListView):
template_name = 'perms/asset_permission_asset.html'
context_object_name = 'asset_permission'
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
......@@ -162,12 +177,14 @@ class AssetPermissionAssetView(AdminUserRequiredMixin, SingleObjectMixin, ListVi
def get(self, request, *args, **kwargs):
self.object = self.get_object(queryset=AssetPermission.objects.all())
self.keyword = self.request.GET.get('keyword', '')
return super(AssetPermissionAssetView, self).get(request, *args, **kwargs)
return super(AssetPermissionAssetView, self)\
.get(request, *args, **kwargs)
def get_queryset(self):
queryset = self.object.get_granted_assets()
if self.keyword:
search_func = functools.partial(search_object_attr, value=self.keyword,
search_func = functools.partial(
search_object_attr, value=self.keyword,
attr_list=['hostname', 'ip'],
ignore_case=True)
queryset = filter(search_func, queryset)
......@@ -179,9 +196,12 @@ class AssetPermissionAssetView(AdminUserRequiredMixin, SingleObjectMixin, ListVi
context = {
'app': _('Perms'),
'action': _('Asset permission asset list'),
'assets_remain': (asset for asset in Asset.objects.all() if asset not in assets_granted),
'assets_remain': [
asset for asset in Asset.objects.all()
if asset not in assets_granted],
'asset_groups': self.object.asset_groups.all(),
'asset_groups_remain': [asset_group for asset_group in AssetGroup.objects.all()
'asset_groups_remain': [
asset_group for asset_group in AssetGroup.objects.all()
if asset_group not in asset_groups_granted],
'keyword': self.keyword,
}
......
# ~*~ coding: utf-8 ~*~
#
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 django_filters.rest_framework import DjangoFilterBackend
......@@ -19,7 +13,8 @@ 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)
from . import serializers
......@@ -98,7 +93,8 @@ class UserToken(APIView):
password = request.data.get('password', '')
public_key = request.data.get('public_key', '')
user, msg = check_user_valid(username=username, email=email,
user, msg = check_user_valid(
username=username, email=email,
password=password, public_key=public_key)
else:
user = request.user
......@@ -116,24 +112,31 @@ 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)
......@@ -2,16 +2,18 @@
#
import base64
import uuid
import hashlib
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 +29,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 +57,30 @@ 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]
try:
uuid.UUID(access_key_id)
except ValueError:
raise exceptions.AuthenticationFailed('Access key id invalid')
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 +93,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 +125,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 +155,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)
......@@ -16,7 +16,8 @@ class AccessKey(models.Model):
default=uuid.uuid4, editable=False)
secret = models.UUIDField(verbose_name='AccessKeySecret',
default=uuid.uuid4, editable=False)
user = models.ForeignKey(User, verbose_name='User', related_name='access_key')
user = models.ForeignKey(User, verbose_name='User',
related_name='access_key')
def get_id(self):
return str(self.id)
......
......@@ -18,9 +18,12 @@ urlpatterns = [
url(r'^v1/token/$', api.UserToken.as_view(), name='user-token'),
url(r'^v1/profile/$', api.UserProfile.as_view(), name='user-profile'),
url(r'^v1/auth/$', api.UserAuthApi.as_view(), name='user-auth'),
url(r'^v1/users/(?P<pk>\d+)/password/reset/$', api.UserResetPasswordApi.as_view(), name='user-reset-password'),
url(r'^v1/users/(?P<pk>\d+)/public-key/reset/$', api.UserResetPKApi.as_view(), name='user-public-key-reset'),
url(r'^v1/users/(?P<pk>\d+)/public-key/update/$', api.UserUpdatePKApi.as_view(), name='user-public-key-update'),
url(r'^v1/users/(?P<pk>\d+)/password/reset/$',
api.UserResetPasswordApi.as_view(), name='user-reset-password'),
url(r'^v1/users/(?P<pk>\d+)/public-key/reset/$',
api.UserResetPKApi.as_view(), name='user-public-key-reset'),
url(r'^v1/users/(?P<pk>\d+)/public-key/update/$',
api.UserUpdatePKApi.as_view(), name='user-public-key-update'),
url(r'^v1/users/(?P<pk>\d+)/groups/$',
api.UserUpdateGroupApi.as_view(), name='user-update-group'),
url(r'^v1/user-groups/(?P<pk>\d+)/users/$',
......
......@@ -6,39 +6,72 @@ from .. import views
app_name = 'users'
urlpatterns = [
# Login view
url(r'^login$', views.UserLoginView.as_view(), name='login'),
url(r'^logout$', views.UserLogoutView.as_view(), name='logout'),
url(r'^password/forgot$', views.UserForgotPasswordView.as_view(), name='forgot-password'),
url(r'^password/forgot$', views.UserForgotPasswordView.as_view(),
name='forgot-password'),
url(r'^password/forgot/sendmail-success$',
views.UserForgotPasswordSendmailSuccessView.as_view(), name='forgot-password-sendmail-success'),
url(r'^password/reset$', views.UserResetPasswordView.as_view(), name='reset-password'),
url(r'^password/reset/success$', views.UserResetPasswordSuccessView.as_view(),
views.UserForgotPasswordSendmailSuccessView.as_view(),
name='forgot-password-sendmail-success'),
url(r'^password/reset$', views.UserResetPasswordView.as_view(),
name='reset-password'),
url(r'^password/reset/success$',
views.UserResetPasswordSuccessView.as_view(),
name='reset-password-success'),
# User view
url(r'^user$', views.UserListView.as_view(), name='user-list'),
url(r'^user/(?P<pk>[0-9]+)$', views.UserDetailView.as_view(), name='user-detail'),
url(r'^user/(?P<pk>[0-9]+)/asset-permission$', views.UserAssetPermissionView.as_view(),
url(r'^user/(?P<pk>[0-9]+)$', views.UserDetailView.as_view(),
name='user-detail'),
url(r'^user/(?P<pk>[0-9]+)/asset-permission$',
views.UserAssetPermissionView.as_view(),
name='user-asset-permission'),
url(r'^user/(?P<pk>[0-9]+)/asset-permission/create$', views.UserAssetPermissionCreateView.as_view(),
url(r'^user/(?P<pk>[0-9]+)/asset-permission/create$',
views.UserAssetPermissionCreateView.as_view(),
name='user-asset-permission-create'),
url(r'^user/(?P<pk>[0-9]+)/assets', views.UserGrantedAssetView.as_view(), name='user-granted-asset'),
url(r'^user/(?P<pk>[0-9]+)/login-history', views.UserDetailView.as_view(), name='user-login-history'),
url(r'^user/export/', views.UserExportView.as_view(), name='user-export'),
url(r'^first-login/$', views.UserFirstLoginView.as_view(), name='user-first-login'),
url(r'^user/import/$', views.BulkImportUserView.as_view(), name='user-import'),
# url(r'^user/(?P<pk>[0-9]+)/assets-perm$', views.UserDetailView.as_view(), name='user-detail'),
url(r'^user/create$', views.UserCreateView.as_view(), name='user-create'),
url(r'^user/(?P<pk>[0-9]+)/update$', views.UserUpdateView.as_view(), name='user-update'),
url(r'^user/(?P<pk>[0-9]+)/assets',
views.UserGrantedAssetView.as_view(),
name='user-granted-asset'),
url(r'^user/(?P<pk>[0-9]+)/login-history',
views.UserDetailView.as_view(),
name='user-login-history'),
url(r'^user/export/',
views.UserExportView.as_view(),
name='user-export'),
url(r'^first-login/$',
views.UserFirstLoginView.as_view(),
name='user-first-login'),
url(r'^user/import/$',
views.UserBulkImportView.as_view(),
name='user-import'),
url(r'^user/create$',
views.UserCreateView.as_view(),
name='user-create'),
url(r'^user/(?P<pk>[0-9]+)/update$',
views.UserUpdateView.as_view(),
name='user-update'),
# User group view
url(r'^user-group$', views.UserGroupListView.as_view(), name='user-group-list'),
url(r'^user-group/(?P<pk>[0-9]+)$', views.UserGroupDetailView.as_view(), name='user-group-detail'),
url(r'^user-group/create$', views.UserGroupCreateView.as_view(), name='user-group-create'),
url(r'^user-group/(?P<pk>[0-9]+)/update$', views.UserGroupUpdateView.as_view(), name='user-group-update'),
url(r'^user-group/(?P<pk>[0-9]+)/asset-permission$', views.UserGroupAssetPermissionView.as_view(),
url(r'^user-group$',
views.UserGroupListView.as_view(),
name='user-group-list'),
url(r'^user-group/(?P<pk>[0-9]+)$',
views.UserGroupDetailView.as_view(),
name='user-group-detail'),
url(r'^user-group/create$',
views.UserGroupCreateView.as_view(),
name='user-group-create'),
url(r'^user-group/(?P<pk>[0-9]+)/update$',
views.UserGroupUpdateView.as_view(),
name='user-group-update'),
url(r'^user-group/(?P<pk>[0-9]+)/asset-permission$',
views.UserGroupAssetPermissionView.as_view(),
name='user-group-asset-permission'),
url(r'^user-group/(?P<pk>[0-9]+)/asset-permission/create$', views.UserGroupAssetPermissionCreateView.as_view(),
url(r'^user-group/(?P<pk>[0-9]+)/asset-permission/create$',
views.UserGroupAssetPermissionCreateView.as_view(),
name='user-group-asset-permission-create'),
url(r'^user-group/(?P<pk>[0-9]+)/assets', views.UserGroupGrantedAssetView.as_view(),
url(r'^user-group/(?P<pk>[0-9]+)/assets',
views.UserGroupGrantedAssetView.as_view(),
name='user-group-granted-asset'),
]
# ~*~ coding: utf-8 ~*~
from .login import *
from .user import *
from .group import *
# ~*~ 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)
# ~*~ 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, '
'please 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
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment