Merge branch 'audits'

pull/530/head
ibuler 2017-01-20 14:00:11 +08:00
commit 948214cacb
28 changed files with 1078 additions and 826 deletions

View File

@ -23,17 +23,20 @@ class TerminalRegisterView(ListCreateAPIView):
def create(self, request, *args, **kwargs): def create(self, request, *args, **kwargs):
name = request.data.get('name', '') name = request.data.get('name', '')
remote_addr = request.META.get('X-Real-IP') or request.META.get('REMOTE_ADDR') remote_addr = request.META.get('X-Real-IP') or \
serializer = self.serializer_class(data={'name': name, 'remote_addr': remote_addr}) request.META.get('REMOTE_ADDR')
serializer = self.serializer_class(
data={'name': name, 'remote_addr': remote_addr})
if get_object_or_none(Terminal, name=name): 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(): if serializer.is_valid():
terminal = serializer.save() terminal = serializer.save()
app_user, access_key = terminal.create_related_app_user() app_user, access_key = terminal.create_related_app_user()
data = {} data = {}
data['applications'] = copy.deepcopy(serializer.data) data['terminal'] = copy.deepcopy(serializer.data)
data['user'] = app_user.to_json() data['user'] = app_user.to_json()
data['access_key_id'] = access_key.id data['access_key_id'] = access_key.id
data['access_key_secret'] = access_key.secret data['access_key_secret'] = access_key.secret
@ -54,11 +57,11 @@ class TerminalViewSet(viewsets.ModelViewSet):
def create(self, request, *args, **kwargs): def create(self, request, *args, **kwargs):
return Response({'msg': 'Use register view except that'}, status=404) return Response({'msg': 'Use register view except that'}, status=404)
def destroy(self, request, *args, **kwargs): # def destroy(self, request, *args, **kwargs):
instance = self.get_object() # instance = self.get_object()
if instance.user is not None: # if instance.user is not None:
instance.user.delete() # instance.user.delete()
return super(TerminalViewSet, self).destroy(request, *args, **kwargs) # return super(TerminalViewSet, self).destroy(request, *args, **kwargs)
class TerminalHeatbeatViewSet(viewsets.ModelViewSet): class TerminalHeatbeatViewSet(viewsets.ModelViewSet):

View File

@ -14,8 +14,9 @@ class TerminalSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = Terminal model = Terminal
fields = ['id', 'name', 'remote_addr', 'type', 'url', 'comment', 'is_accepted', fields = ['id', 'name', 'remote_addr', 'type', 'url', 'comment',
'is_active', 'get_type_display', 'proxy_online', 'is_alive'] 'is_accepted', 'is_active', 'get_type_display',
'proxy_online', 'is_alive']
@staticmethod @staticmethod
def get_proxy_online(obj): def get_proxy_online(obj):
@ -31,6 +32,7 @@ class TerminalSerializer(serializers.ModelSerializer):
class TerminalHeatbeatSerializer(serializers.ModelSerializer): class TerminalHeatbeatSerializer(serializers.ModelSerializer):
date_start = serializers.DateTimeField
class Meta: class Meta:
model = TerminalHeatbeat model = TerminalHeatbeat

View File

@ -214,23 +214,9 @@ class AdminUserForm(forms.ModelForm):
class SystemUserForm(forms.ModelForm): class SystemUserForm(forms.ModelForm):
# Admin user assets define, let user select, save it in form not in view # Admin user assets define, let user select, save it in form not in view
assets = forms.ModelMultipleChoiceField(queryset=Asset.objects.all(), auto_generate_key = forms.BooleanField(initial=True, required=False)
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)
# Form field name can not start with `_`, so redefine it, # Form field name can not start with `_`, so redefine it,
password = forms.CharField(widget=forms.PasswordInput, max_length=100, min_length=8, strip=True, 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)
# Need use upload private key file except paste private key content # Need use upload private key file except paste private key content
private_key_file = forms.FileField(required=False) private_key_file = forms.FileField(required=False)
@ -242,24 +228,15 @@ class SystemUserForm(forms.ModelForm):
initial['asset_groups'] = kwargs['instance'].asset_groups.all() initial['asset_groups'] = kwargs['instance'].asset_groups.all()
super(SystemUserForm, self).__init__(*args, **kwargs) 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): def save(self, commit=True):
# Because we define custom field, so we need rewrite :method: `save` # Because we define custom field, so we need rewrite :method: `save`
system_user = super(SystemUserForm, self).save(commit=commit) system_user = super(SystemUserForm, self).save(commit=commit)
password = self.cleaned_data['password'] password = self.cleaned_data['password']
private_key_file = self.cleaned_data['private_key_file'] private_key_file = self.cleaned_data['private_key_file']
if password: if system_user.auth_method == 'P':
system_user.password = password if password:
system_user.password = password
print(password) print(password)
# Todo: Validate private key file, and generate public key # Todo: Validate private key file, and generate public key
# Todo: Auto generate private key and public key # Todo: Auto generate private key and public key
@ -268,11 +245,30 @@ class SystemUserForm(forms.ModelForm):
system_user.save() system_user.save()
return self.instance 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: class Meta:
model = SystemUser model = SystemUser
fields = [ fields = [
'name', 'username', 'protocol', 'auto_generate_key', 'password', 'private_key_file', 'auth_method', '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 = { widgets = {
'name': forms.TextInput(attrs={'placeholder': _('Name')}), 'name': forms.TextInput(attrs={'placeholder': _('Name')}),
@ -282,7 +278,6 @@ class SystemUserForm(forms.ModelForm):
'name': '* required', 'name': '* required',
'username': '* required', 'username': '* required',
'auto_push': 'Auto push system user to asset', 'auto_push': 'Auto push system user to asset',
'auto_update': 'Auto update system user ssh key',
} }

View File

@ -99,6 +99,12 @@ class SystemUserSerializer(serializers.ModelSerializer):
return fields return fields
class AssetSystemUserSerializer(serializers.ModelSerializer):
class Meta:
model = SystemUser
fields = ('id', 'name', 'username', 'protocol', 'auth_method', 'comment')
class SystemUserUpdateAssetsSerializer(serializers.ModelSerializer): class SystemUserUpdateAssetsSerializer(serializers.ModelSerializer):
assets = serializers.PrimaryKeyRelatedField(many=True, queryset=Asset.objects.all()) assets = serializers.PrimaryKeyRelatedField(many=True, queryset=Asset.objects.all())
@ -145,13 +151,13 @@ class AssetSerializer(BulkSerializerMixin, serializers.ModelSerializer):
class AssetGrantedSerializer(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() is_inherited = serializers.SerializerMethodField()
system_users_join = serializers.SerializerMethodField() system_users_join = serializers.SerializerMethodField()
class Meta(object): class Meta(object):
model = Asset 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") "is_active", "system_users_join", "comment")
@staticmethod @staticmethod
@ -163,7 +169,7 @@ class AssetGrantedSerializer(serializers.ModelSerializer):
@staticmethod @staticmethod
def get_system_users_join(obj): 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): class IDCSerializer(BulkSerializerMixin, serializers.ModelSerializer):

View File

@ -27,23 +27,31 @@
</div> </div>
</div> </div>
<div class="ibox-content"> <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="" > <form enctype="multipart/form-data" method="post" class="form-horizontal" action="" >
{% csrf_token %} {% csrf_token %}
<h3>{% trans 'Basic' %}</h3>
{{ form.name|bootstrap_horizontal }} {{ form.name|bootstrap_horizontal }}
{{ form.username|bootstrap_horizontal }} {{ form.username|bootstrap_horizontal }}
{{ form.protocol|bootstrap_horizontal }} {{ form.protocol|bootstrap_horizontal }}
<div class="form-group"> <h3>{% trans 'Auth' %}</h3>
<label for="{{ form.auto_generate_key.id_for_label }}" class="col-sm-2 control-label">{% trans 'Auto generate key' %}</label> {{ form.auth_method|bootstrap_horizontal }}
<div class="col-sm-8"> <div class="password-auth hidden">
{{ form.auto_generate_key}} {{ form.password|bootstrap_horizontal }}
</div>
</div> </div>
{{ form.password|bootstrap_horizontal }} <div class="public-key-auth">
{{ form.private_key_file|bootstrap_horizontal }} <div class="form-group">
<div class="form-group"> <label for="{{ form.auto_generate_key.id_for_label }}" class="col-sm-2 control-label">{% trans 'Auto generate key' %}</label>
<label for="{{ form.as_default.id_for_label }}" class="col-sm-2 control-label">{% trans 'As default' %}</label> <div class="col-sm-8">
<div class="col-sm-8"> {{ form.auto_generate_key}}
{{ form.as_default}} </div>
</div>
<div>
{{ form.private_key_file|bootstrap_horizontal }}
</div> </div>
</div> </div>
<div class="form-group"> <div class="form-group">
@ -52,20 +60,12 @@
{{ form.auto_push}} {{ form.auto_push}}
</div> </div>
</div> </div>
<div class="form-group"> <h3>{% trans 'Other' %}</h3>
<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 }}
{{ form.sudo|bootstrap_horizontal }} {{ form.sudo|bootstrap_horizontal }}
{{ form.comment|bootstrap_horizontal }}
{{ form.home|bootstrap_horizontal }} {{ form.home|bootstrap_horizontal }}
{{ form.shell|bootstrap_horizontal }} {{ form.shell|bootstrap_horizontal }}
{{ form.uid|bootstrap_horizontal }} {{ form.uid|bootstrap_horizontal }}
{{ form.comment|bootstrap_horizontal }}
<div class="form-group"> <div class="form-group">
<div class="col-sm-4 col-sm-offset-2"> <div class="col-sm-4 col-sm-offset-2">
<button class="btn btn-white" type="reset">{% trans 'Reset' %}</button> <button class="btn btn-white" type="reset">{% trans 'Reset' %}</button>
@ -81,8 +81,36 @@
{% endblock %} {% endblock %}
{% block custom_foot_js %} {% block custom_foot_js %}
<script> <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 () { $(document).ready(function () {
$('.select2').select2(); $('.select2').select2();
authMethodDisplay();
$(auth_method).change(function () {
authMethodDisplay();
});
$(auto_generate_key).change(function () {
authMethodDisplay();
});
if ($('#'+'{{ form.protocol.id_for_label }}').val() == 'telnet') { if ($('#'+'{{ form.protocol.id_for_label }}').val() == 'telnet') {
$('#'+'{{ form.auto_generate_key.id_for_label }}').closest('.form-group').remove(); $('#'+'{{ form.auto_generate_key.id_for_label }}').closest('.form-group').remove();

View File

@ -547,6 +547,10 @@ class SystemUserCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateVi
kwargs.update(context) kwargs.update(context)
return super(SystemUserCreateView, self).get_context_data(**kwargs) 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): def get_success_message(self, cleaned_data):
success_message = _('Create system user <a href="%s">%s</a> successfully.' % success_message = _('Create system user <a href="%s">%s</a> successfully.' %
( (

View File

@ -6,6 +6,7 @@ from __future__ import absolute_import, unicode_literals
from rest_framework import generics, viewsets from rest_framework import generics, viewsets
from rest_framework.views import APIView, Response from rest_framework.views import APIView, Response
from rest_framework_bulk import BulkModelViewSet
from . import models, serializers from . import models, serializers
from .hands import IsSuperUserOrAppUser, Terminal, IsAppUser from .hands import IsSuperUserOrAppUser, Terminal, IsAppUser
@ -51,13 +52,3 @@ class CommandLogViewSet(viewsets.ModelViewSet):
serializer_class = serializers.CommandLogSerializer serializer_class = serializers.CommandLogSerializer
permission_classes = (IsSuperUserOrAppUser,) 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},
# ]
#

View File

@ -17,10 +17,13 @@ class LoginLog(models.Model):
username = models.CharField(max_length=20, verbose_name=_('Username')) username = models.CharField(max_length=20, verbose_name=_('Username'))
name = models.CharField(max_length=20, blank=True, verbose_name=_('Name')) 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_ip = models.GenericIPAddressField(verbose_name=_('Login ip'))
login_city = models.CharField(max_length=100, blank=True, null=True, verbose_name=_('Login city')) login_city = models.CharField(max_length=100, blank=True, null=True,
user_agent = models.CharField(max_length=100, blank=True, null=True, verbose_name=_('User agent')) 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')) date_login = models.DateTimeField(auto_now_add=True, verbose_name=_('Date login'))
class Meta: class Meta:
@ -66,7 +69,8 @@ class ProxyLog(models.Model):
class CommandLog(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_no = models.IntegerField()
command = models.CharField(max_length=1000, blank=True) command = models.CharField(max_length=1000, blank=True)
output = models.TextField(blank=True) output = models.TextField(blank=True)
@ -78,7 +82,8 @@ class CommandLog(models.Model):
@property @property
def output_decode(self): def output_decode(self):
try: try:
return base64.b64decode(self.output).replace('\n', '<br />') return base64.b64decode(self.output).decode('utf-8') \
.replace('\n', '<br />')
except UnicodeDecodeError: except UnicodeDecodeError:
return 'UnicodeDecodeError' return 'UnicodeDecodeError'

View File

@ -13,8 +13,9 @@ class ProxyLogSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = models.ProxyLog model = models.ProxyLog
fields = ['id', 'name', 'username', 'hostname', 'ip', 'system_user', 'login_type', 'terminal', fields = ['id', 'name', 'username', 'hostname', 'ip', 'system_user',
'log_file', 'was_failed', 'is_finished', 'date_start', 'date_finished', 'time', 'login_type', 'terminal', 'log_file', 'was_failed',
'is_finished', 'date_start', 'date_finished', 'time',
'command_length', "commands_dict"] 'command_length', "commands_dict"]
@staticmethod @staticmethod
@ -32,3 +33,4 @@ class ProxyLogSerializer(serializers.ModelSerializer):
class CommandLogSerializer(serializers.ModelSerializer): class CommandLogSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = models.CommandLog model = models.CommandLog
fields = '__all__'

View File

@ -83,7 +83,7 @@
<td>{{ command.proxy_log.system_user }}</td> <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><a href="{% url 'audits:proxy-log-detail' pk=command.proxy_log.id %}">{{ command.proxy_log.id}}</a></td>
<td>{{ command.datetime }}</td> <td>{{ command.datetime }}</td>
<td>{{ command.output_decode |safe }}</td> <td>{{ command.output_decode|safe }}</td>
</tr> </tr>
{% endfor %} {% endfor %}
</tbody> </tbody>

View File

@ -52,7 +52,7 @@
<tr> <tr>
<td>{{ command.command_no }}</td> <td>{{ command.command_no }}</td>
<td>{{ command.command }}</td> <td>{{ command.command }}</td>
<td>{{ command.output_decode |safe }}</td> <td>{{ command.output_decode|safe }}</td>
<td>{{ command.datetime }}</td> <td>{{ command.datetime }}</td>
</tr> </tr>
{% endfor %} {% endfor %}
@ -68,30 +68,6 @@
</div> </div>
</div> </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> </div>
</div> </div>

View File

@ -14,17 +14,16 @@ from .models import ProxyLog, CommandLog, LoginLog
from .hands import User, Asset, SystemUser, AdminUserRequiredMixin 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): class ProxyLogListView(AdminUserRequiredMixin, ListView):
model = ProxyLog model = ProxyLog
template_name = 'audits/proxy_log_list.html' template_name = 'audits/proxy_log_list.html'
context_object_name = 'proxy_log_list' context_object_name = 'proxy_log_list'
def get_queryset(self): 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.queryset = super(ProxyLogListView, self).get_queryset()
self.keyword = keyword = self.request.GET.get('keyword', '') self.keyword = keyword = self.request.GET.get('keyword', '')
self.username = username = self.request.GET.get('username', '') 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') date_from = timezone.datetime.strptime(date_from_s, '%m/%d/%Y')
self.queryset = self.queryset.filter(date_start__gt=date_from) self.queryset = self.queryset.filter(date_start__gt=date_from)
if date_to_s: 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) self.queryset = self.queryset.filter(date_start__lt=date_to)
if username: if username:
self.queryset = self.queryset.filter(username=username) self.queryset = self.queryset.filter(username=username)
@ -54,7 +54,6 @@ class ProxyLogListView(AdminUserRequiredMixin, ListView):
return self.queryset return self.queryset
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
print(self.date_to_s)
context = { context = {
'app': _('Audits'), 'app': _('Audits'),
'action': _('Proxy log list'), 'action': _('Proxy log list'),
@ -110,6 +109,9 @@ class CommandLogListView(AdminUserRequiredMixin, ListView):
context_object_name = 'command_list' context_object_name = 'command_list'
def get_queryset(self): 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.queryset = super(CommandLogListView, self).get_queryset()
self.keyword = keyword = self.request.GET.get('keyword', '') self.keyword = keyword = self.request.GET.get('keyword', '')
self.sort = sort = self.request.GET.get('sort', '-datetime') self.sort = sort = self.request.GET.get('sort', '-datetime')
@ -161,6 +163,9 @@ class LoginLogListView(AdminUserRequiredMixin, ListView):
context_object_name = 'login_log_list' context_object_name = 'login_log_list'
def get_queryset(self): 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.queryset = super(LoginLogListView, self).get_queryset()
self.keyword = keyword = self.request.GET.get('keyword', '') self.keyword = keyword = self.request.GET.get('keyword', '')
self.username = username = self.request.GET.get('username', '') self.username = username = self.request.GET.get('username', '')

View File

@ -16,5 +16,6 @@ app = Celery('jumpserver')
# Using a string here means the worker will not have to # Using a string here means the worker will not have to
# pickle the object when using Windows. # pickle the object when using Windows.
app.config_from_object('django.conf:settings') 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])

View File

@ -1,16 +1,18 @@
# ~*~ coding: utf-8 ~*~ # ~*~ coding: utf-8 ~*~
# #
from django.shortcuts import get_object_or_404
from rest_framework.views import APIView, Response 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.generics import ListAPIView, get_object_or_404
from rest_framework import viewsets 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 common.utils import get_object_or_none
from .utils import get_user_granted_assets, get_user_granted_asset_groups, get_user_asset_permissions, \ from .utils import get_user_granted_assets, get_user_granted_asset_groups, \
get_user_group_asset_permissions, get_user_group_granted_assets, get_user_group_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 .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 from . import serializers
@ -80,11 +82,12 @@ class UserGrantedAssetsApi(ListAPIView):
def get_queryset(self): def get_queryset(self):
user_id = self.kwargs.get('pk', '') user_id = self.kwargs.get('pk', '')
queryset = []
if user_id: if user_id:
user = get_object_or_404(User, id=user_id) user = get_object_or_404(User, id=user_id)
queryset = get_user_granted_assets(user) for k, v in get_user_granted_assets(user).items():
else: k.system_users_granted = v
queryset = [] queryset.append(k)
return queryset return queryset
@ -104,19 +107,26 @@ class UserGrantedAssetGroupsApi(ListAPIView):
class MyGrantedAssetsApi(ListAPIView): class MyGrantedAssetsApi(ListAPIView):
"""授权给用户的资产列表
[{'hostname': 'x','ip': 'x', ..,
'system_users_granted': [{'name': 'x', .}, ...]
"""
permission_classes = (IsValidUser,) permission_classes = (IsValidUser,)
serializer_class = AssetGrantedSerializer serializer_class = AssetGrantedSerializer
def get_queryset(self): def get_queryset(self):
queryset = []
user = self.request.user user = self.request.user
if user: if user:
queryset = get_user_granted_assets(user) for asset, system_users in get_user_granted_assets(user).items():
else: asset.system_users_granted = system_users
queryset = [] queryset.append(asset)
return queryset return queryset
class MyGrantedAssetsGroupsApi(APIView): class MyGrantedAssetsGroupsApi(APIView):
"""授权给用户的资产组列表, 非直接通过授权规则授权的资产组列表, 而是授权资产的所有
资产组之和"""
permission_classes = (IsValidUser,) permission_classes = (IsValidUser,)
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
@ -141,6 +151,7 @@ class MyGrantedAssetsGroupsApi(APIView):
class MyAssetGroupAssetsApi(ListAPIView): class MyAssetGroupAssetsApi(ListAPIView):
"""授权用户资产组下的资产列表, 非该资产组的所有资产,而是被授权的"""
permission_classes = (IsValidUser,) permission_classes = (IsValidUser,)
serializer_class = AssetGrantedSerializer serializer_class = AssetGrantedSerializer
@ -152,8 +163,9 @@ class MyAssetGroupAssetsApi(ListAPIView):
if user and asset_group: if user and asset_group:
assets = get_user_granted_assets(user) assets = get_user_granted_assets(user)
for asset in assets: for asset in asset_group.assets.all():
if asset_group in asset.groups.all(): if asset in assets:
asset.system_users_granted = assets[asset]
queryset.append(asset) queryset.append(asset)
return queryset return queryset
@ -186,3 +198,23 @@ class UserGroupGrantedAssetGroupsApi(ListAPIView):
else: else:
queryset = [] queryset = []
return 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)

View File

@ -11,6 +11,7 @@ from .hands import User
class AssetPermissionSerializer(serializers.ModelSerializer): class AssetPermissionSerializer(serializers.ModelSerializer):
class Meta: class Meta:
model = AssetPermission model = AssetPermission
fields = '__all__'
class UserAssetPermissionSerializer(AssetPermissionSerializer): class UserAssetPermissionSerializer(AssetPermissionSerializer):
@ -23,4 +24,3 @@ class UserAssetPermissionSerializer(AssetPermissionSerializer):
else: else:
return False return False

View File

@ -7,30 +7,50 @@ from .. import api
app_name = 'perms' app_name = 'perms'
router = routers.DefaultRouter() router = routers.DefaultRouter()
router.register('v1/asset-permissions', api.AssetPermissionViewSet, 'asset-permission') router.register('v1/asset-permissions',
api.AssetPermissionViewSet,
'asset-permission')
urlpatterns = [ urlpatterns = [
url(r'^v1/user/my/assets/$', api.MyGrantedAssetsApi.as_view(), name='my-assets'), # 用户可以使用自己的Token或其它认证查看自己授权的资产,资产组等
url(r'^v1/user/my/asset-groups/$', api.MyGrantedAssetsGroupsApi.as_view(), name='my-asset-groups'), url(r'^v1/user/my/assets/$',
url(r'^v1/user/my/asset-group/(?P<pk>[0-9]+)/assets/$', api.MyAssetGroupAssetsApi.as_view(), 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'), 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]+)/assets/$',
url(r'^v1/user/(?P<pk>[0-9]+)/asset-groups/$', api.UserGrantedAssetGroupsApi.as_view(), 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'), 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]+)/assets/$',
url(r'^v1/user-group/(?P<pk>[0-9]+)/asset-groups/$', api.UserGroupGrantedAssetGroupsApi.as_view(), 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'), name='user-group-asset-groups'),
# 回收用户或用户组授权
# Revoke permission api url(r'^v1/asset-permissions/user/revoke/$',
url(r'^v1/asset-permissions/user/revoke/', api.RevokeUserAssetPermission.as_view(), api.RevokeUserAssetPermission.as_view(),
name='revoke-user-asset-permission'), 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'), 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 urlpatterns += router.urls

View File

@ -37,6 +37,8 @@ def get_user_group_granted_assets(user_group):
if not asset_permission.is_valid: if not asset_permission.is_valid:
continue continue
for asset in asset_permission.get_granted_assets(): for asset in asset_permission.get_granted_assets():
if not asset.is_active:
continue
if asset in assets: if asset in assets:
assets[asset] |= set(asset_permission.system_users.all()) assets[asset] |= set(asset_permission.system_users.all())
else: else:
@ -127,6 +129,8 @@ def get_user_granted_assets_direct(user):
if not asset_permission.is_valid: if not asset_permission.is_valid:
continue continue
for asset in asset_permission.get_granted_assets(): for asset in asset_permission.get_granted_assets():
if not asset.is_active:
continue
if asset in assets: if asset in assets:
assets[asset] |= set(asset_permission.system_users.all()) assets[asset] |= set(asset_permission.system_users.all())
else: else:
@ -147,12 +151,13 @@ def get_user_granted_assets_inherit_from_user_groups(user):
for user_group in user_groups: for user_group in user_groups:
assets_inherited = get_user_group_granted_assets(user_group) assets_inherited = get_user_group_granted_assets(user_group)
for asset in assets_inherited: for asset in assets_inherited:
if not asset.is_active:
continue
if asset in assets: if asset in assets:
assets[asset] |= assets_inherited[asset] assets[asset] |= assets_inherited[asset]
else: else:
setattr(asset, 'inherited', True) setattr(asset, 'inherited', True)
assets[asset] = assets_inherited[asset] assets[asset] = assets_inherited[asset]
return assets return assets
@ -167,6 +172,8 @@ def get_user_granted_assets(user):
assets = assets_inherited assets = assets_inherited
for asset in assets_direct: for asset in assets_direct:
if not asset.is_active:
continue
if asset in assets: if asset in assets:
assets[asset] |= assets_direct[asset] assets[asset] |= assets_direct[asset]
else: else:

View File

@ -39,22 +39,25 @@ class AssetPermissionListView(AdminUserRequiredMixin, ListView):
self.sort = sort = self.request.GET.get('sort', '-date_created') self.sort = sort = self.request.GET.get('sort', '-date_created')
if keyword: if keyword:
self.queryset = self.queryset.filter(Q(users__name__contains=keyword) | self.queryset = self.queryset\
Q(users__username__contains=keyword) | .filter(Q(users__name__contains=keyword) |
Q(user_groups__name__contains=keyword) | Q(users__username__contains=keyword) |
Q(assets__ip__contains=keyword) | Q(user_groups__name__contains=keyword) |
Q(assets__hostname__contains=keyword) | Q(assets__ip__contains=keyword) |
Q(system_users__username__icontains=keyword) | Q(assets__hostname__contains=keyword) |
Q(system_users__name__icontains=keyword) | Q(system_users__username__icontains=keyword) |
Q(asset_groups__name__icontains=keyword) | Q(system_users__name__icontains=keyword) |
Q(comment__icontains=keyword) | Q(asset_groups__name__icontains=keyword) |
Q(name__icontains=keyword)).distinct() Q(comment__icontains=keyword) |
Q(name__icontains=keyword)).distinct()
if sort: if sort:
self.queryset = self.queryset.order_by(sort) self.queryset = self.queryset.order_by(sort)
return self.queryset return self.queryset
class AssetPermissionCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateView): class AssetPermissionCreateView(AdminUserRequiredMixin,
SuccessMessageMixin,
CreateView):
model = AssetPermission model = AssetPermission
form_class = AssetPermissionForm form_class = AssetPermissionForm
template_name = 'perms/asset_permission_create_update.html' 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) return super(AssetPermissionCreateView, self).get_context_data(**kwargs)
def get_success_message(self, cleaned_data): def get_success_message(self, cleaned_data):
success_message = _('Create asset permission <a href="%s"> %s </a> successfully.' % success_message = _(
( 'Create asset permission <a href="%s"> %s </a> '
reverse_lazy('perms:asset-permission-detail', kwargs={'pk': self.object.pk}), 'successfully.' % (reverse_lazy('perms:asset-permission-detail',
self.object.name, kwargs={'pk': self.object.pk}),
)) self.object.name,))
return success_message return success_message
@ -81,7 +84,8 @@ class AssetPermissionUpdateView(AdminUserRequiredMixin, UpdateView):
model = AssetPermission model = AssetPermission
form_class = AssetPermissionForm form_class = AssetPermissionForm
template_name = 'perms/asset_permission_create_update.html' 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): def get_context_data(self, **kwargs):
context = { context = {
@ -92,7 +96,8 @@ class AssetPermissionUpdateView(AdminUserRequiredMixin, UpdateView):
return super(AssetPermissionUpdateView, self).get_context_data(**kwargs) return super(AssetPermissionUpdateView, self).get_context_data(**kwargs)
def get_success_url(self): 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 return success_url
@ -105,8 +110,9 @@ class AssetPermissionDetailView(AdminUserRequiredMixin, DetailView):
context = { context = {
'app': _('Perms'), 'app': _('Perms'),
'action': _('Asset permission detail'), 'action': _('Asset permission detail'),
'system_users_remain': [system_user for system_user in SystemUser.objects.all() 'system_users_remain': [
if system_user not in self.object.system_users.all()], 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(), 'system_users': self.object.system_users.all(),
} }
kwargs.update(context) kwargs.update(context)
@ -119,7 +125,9 @@ class AssetPermissionDeleteView(AdminUserRequiredMixin, DeleteView):
success_url = reverse_lazy('perms:asset-permission-list') 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' template_name = 'perms/asset_permission_user.html'
context_object_name = 'asset_permission' context_object_name = 'asset_permission'
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
@ -132,9 +140,11 @@ class AssetPermissionUserView(AdminUserRequiredMixin, SingleObjectMixin, ListVie
def get_queryset(self): def get_queryset(self):
queryset = self.object.get_granted_users() queryset = self.object.get_granted_users()
if self.keyword: if self.keyword:
search_func = functools.partial(search_object_attr, value=self.keyword, search_func = functools.partial(
attr_list=['username', 'name', 'email'], search_object_attr,
ignore_case=True) value=self.keyword,
attr_list=['username', 'name', 'email'],
ignore_case=True)
queryset = filter(search_func, queryset) queryset = filter(search_func, queryset)
return queryset return queryset
@ -144,17 +154,22 @@ class AssetPermissionUserView(AdminUserRequiredMixin, SingleObjectMixin, ListVie
context = { context = {
'app': _('Perms'), 'app': _('Perms'),
'action': _('Asset permission user list'), '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': self.object.user_groups.all(),
'user_groups_remain': [user_group for user_group in UserGroup.objects.all() 'user_groups_remain': [
if user_group not in user_groups_granted], user_group for user_group in UserGroup.objects.all()
if user_group not in user_groups_granted],
'keyword': self.keyword, 'keyword': self.keyword,
} }
kwargs.update(context) kwargs.update(context)
return super(AssetPermissionUserView, self).get_context_data(**kwargs) 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' template_name = 'perms/asset_permission_asset.html'
context_object_name = 'asset_permission' context_object_name = 'asset_permission'
paginate_by = settings.CONFIG.DISPLAY_PER_PAGE paginate_by = settings.CONFIG.DISPLAY_PER_PAGE
@ -162,14 +177,16 @@ class AssetPermissionAssetView(AdminUserRequiredMixin, SingleObjectMixin, ListVi
def get(self, request, *args, **kwargs): def get(self, request, *args, **kwargs):
self.object = self.get_object(queryset=AssetPermission.objects.all()) self.object = self.get_object(queryset=AssetPermission.objects.all())
self.keyword = self.request.GET.get('keyword', '') 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): def get_queryset(self):
queryset = self.object.get_granted_assets() queryset = self.object.get_granted_assets()
if self.keyword: if self.keyword:
search_func = functools.partial(search_object_attr, value=self.keyword, search_func = functools.partial(
attr_list=['hostname', 'ip'], search_object_attr, value=self.keyword,
ignore_case=True) attr_list=['hostname', 'ip'],
ignore_case=True)
queryset = filter(search_func, queryset) queryset = filter(search_func, queryset)
return queryset return queryset
@ -179,10 +196,13 @@ class AssetPermissionAssetView(AdminUserRequiredMixin, SingleObjectMixin, ListVi
context = { context = {
'app': _('Perms'), 'app': _('Perms'),
'action': _('Asset permission asset list'), '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': self.object.asset_groups.all(),
'asset_groups_remain': [asset_group for asset_group in AssetGroup.objects.all() 'asset_groups_remain': [
if asset_group not in asset_groups_granted], asset_group for asset_group in AssetGroup.objects.all()
if asset_group not in asset_groups_granted],
'keyword': self.keyword, 'keyword': self.keyword,
} }
kwargs.update(context) kwargs.update(context)

View File

@ -1,16 +1,10 @@
# ~*~ coding: utf-8 ~*~ # ~*~ 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 import generics, viewsets
from rest_framework.response import Response from rest_framework.response import Response
from rest_framework.views import APIView from rest_framework.views import APIView
from rest_framework.decorators import api_view
from rest_framework.permissions import AllowAny from rest_framework.permissions import AllowAny
from rest_framework.authentication import SessionAuthentication
from rest_framework_bulk import BulkModelViewSet from rest_framework_bulk import BulkModelViewSet
# from django_filters.rest_framework import DjangoFilterBackend # 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 .utils import check_user_valid, generate_token
from .models import User, UserGroup from .models import User, UserGroup
from .hands import write_login_log_async from .hands import write_login_log_async
from .permissions import IsSuperUser, IsAppUser, IsValidUser, IsSuperUserOrAppUser from .permissions import (
IsSuperUser, IsAppUser, IsValidUser)
from . import serializers from . import serializers
@ -98,8 +93,9 @@ class UserToken(APIView):
password = request.data.get('password', '') password = request.data.get('password', '')
public_key = request.data.get('public_key', '') public_key = request.data.get('public_key', '')
user, msg = check_user_valid(username=username, email=email, user, msg = check_user_valid(
password=password, public_key=public_key) username=username, email=email,
password=password, public_key=public_key)
else: else:
user = request.user user = request.user
msg = None msg = None
@ -116,24 +112,31 @@ class UserProfile(APIView):
def get(self, request): def get(self, request):
return Response(request.user.to_json()) return Response(request.user.to_json())
def post(self, request):
return Response(request.user.to_json())
class UserAuthApi(APIView): class UserAuthApi(APIView):
permission_classes = (AllowAny,) permission_classes = (AllowAny,)
def post(self, request, *args, **kwargs): def post(self, request):
username = request.data.get('username', '') username = request.data.get('username', '')
password = request.data.get('password', '') password = request.data.get('password', '')
public_key = request.data.get('public_key', '') public_key = request.data.get('public_key', '')
login_type = request.data.get('login_type', '') 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_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: if user:
token = generate_token(request, user) token = generate_token(request, user)
write_login_log_async.delay(user.username, name=user.name, user_agent=user_agent, write_login_log_async.delay(
login_ip=login_ip, login_type=login_type) 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()}) return Response({'token': token, 'user': user.to_json()})
else: else:
return Response({'msg': msg}, status=401) return Response({'msg': msg}, status=401)

View File

@ -2,16 +2,18 @@
# #
import base64 import base64
import uuid
import hashlib import hashlib
import time import time
from django.core.cache import cache from django.core.cache import cache
from django.conf import settings from django.conf import settings
from django.utils.translation import ugettext as _ from django.utils.translation import ugettext as _
from rest_framework import authentication, exceptions, permissions
from django.utils.six import text_type from django.utils.six import text_type
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from rest_framework import HTTP_HEADER_ENCODING 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 common.utils import get_object_or_none, make_signature, http_to_unixtime
from .utils import refresh_token from .utils import refresh_token
@ -27,6 +29,21 @@ def get_request_date_header(request):
class AccessKeyAuthentication(authentication.BaseAuthentication): 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' keyword = 'Sign'
model = AccessKey model = AccessKey
@ -40,22 +57,30 @@ class AccessKeyAuthentication(authentication.BaseAuthentication):
msg = _('Invalid signature header. No credentials provided.') msg = _('Invalid signature header. No credentials provided.')
raise exceptions.AuthenticationFailed(msg) raise exceptions.AuthenticationFailed(msg)
elif len(auth) > 2: 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) raise exceptions.AuthenticationFailed(msg)
try: try:
sign = auth[1].decode().split(':') sign = auth[1].decode().split(':')
if len(sign) != 2: if len(sign) != 2:
msg = _('Invalid signature header. Format like AccessKeyId:Signature') msg = _('Invalid signature header. '
'Format like AccessKeyId:Signature')
raise exceptions.AuthenticationFailed(msg) raise exceptions.AuthenticationFailed(msg)
except UnicodeError: 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) raise exceptions.AuthenticationFailed(msg)
access_key_id = sign[0] access_key_id = sign[0]
try:
uuid.UUID(access_key_id)
except ValueError:
raise exceptions.AuthenticationFailed('Access key id invalid')
request_signature = sign[1] 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 @staticmethod
def authenticate_credentials(request, access_key_id, request_signature): def authenticate_credentials(request, access_key_id, request_signature):
@ -68,14 +93,17 @@ class AccessKeyAuthentication(authentication.BaseAuthentication):
try: try:
request_unix_time = http_to_unixtime(request_date) request_unix_time = http_to_unixtime(request_date)
except ValueError: 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: if int(time.time()) - request_unix_time > 15 * 60:
raise exceptions.AuthenticationFailed(_('Expired, more than 15 minutes')) raise exceptions.AuthenticationFailed(
_('Expired, more than 15 minutes'))
signature = make_signature(access_key_secret, request_date) signature = make_signature(access_key_secret, request_date)
if not signature == request_signature: 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: if not access_key.user.is_active:
raise exceptions.AuthenticationFailed(_('User disabled.')) raise exceptions.AuthenticationFailed(_('User disabled.'))
@ -97,13 +125,15 @@ class AccessTokenAuthentication(authentication.BaseAuthentication):
msg = _('Invalid token header. No credentials provided.') msg = _('Invalid token header. No credentials provided.')
raise exceptions.AuthenticationFailed(msg) raise exceptions.AuthenticationFailed(msg)
elif len(auth) > 2: 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) raise exceptions.AuthenticationFailed(msg)
try: try:
token = auth[1].decode() token = auth[1].decode()
except UnicodeError: 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) raise exceptions.AuthenticationFailed(msg)
return self.authenticate_credentials(token) return self.authenticate_credentials(token)
@ -125,4 +155,6 @@ class PrivateTokenAuthentication(authentication.TokenAuthentication):
class SessionAuthentication(authentication.SessionAuthentication): class SessionAuthentication(authentication.SessionAuthentication):
def enforce_csrf(self, request): def enforce_csrf(self, request):
return None reason = CSRFCheck().process_view(request, None, (), {})
if reason:
raise exceptions.AuthenticationFailed(reason)

View File

@ -16,7 +16,8 @@ class AccessKey(models.Model):
default=uuid.uuid4, editable=False) default=uuid.uuid4, editable=False)
secret = models.UUIDField(verbose_name='AccessKeySecret', secret = models.UUIDField(verbose_name='AccessKeySecret',
default=uuid.uuid4, editable=False) 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): def get_id(self):
return str(self.id) return str(self.id)

View File

@ -18,9 +18,12 @@ urlpatterns = [
url(r'^v1/token/$', api.UserToken.as_view(), name='user-token'), 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/profile/$', api.UserProfile.as_view(), name='user-profile'),
url(r'^v1/auth/$', api.UserAuthApi.as_view(), name='user-auth'), 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+)/password/reset/$',
url(r'^v1/users/(?P<pk>\d+)/public-key/reset/$', api.UserResetPKApi.as_view(), name='user-public-key-reset'), api.UserResetPasswordApi.as_view(), name='user-reset-password'),
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+)/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/$', url(r'^v1/users/(?P<pk>\d+)/groups/$',
api.UserUpdateGroupApi.as_view(), name='user-update-group'), api.UserUpdateGroupApi.as_view(), name='user-update-group'),
url(r'^v1/user-groups/(?P<pk>\d+)/users/$', url(r'^v1/user-groups/(?P<pk>\d+)/users/$',

View File

@ -6,39 +6,72 @@ from .. import views
app_name = 'users' app_name = 'users'
urlpatterns = [ urlpatterns = [
# Login view
url(r'^login$', views.UserLoginView.as_view(), name='login'), url(r'^login$', views.UserLoginView.as_view(), name='login'),
url(r'^logout$', views.UserLogoutView.as_view(), name='logout'), 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$', url(r'^password/forgot/sendmail-success$',
views.UserForgotPasswordSendmailSuccessView.as_view(), name='forgot-password-sendmail-success'), views.UserForgotPasswordSendmailSuccessView.as_view(),
url(r'^password/reset$', views.UserResetPasswordView.as_view(), name='reset-password'), name='forgot-password-sendmail-success'),
url(r'^password/reset/success$', views.UserResetPasswordSuccessView.as_view(), url(r'^password/reset$', views.UserResetPasswordView.as_view(),
name='reset-password'),
url(r'^password/reset/success$',
views.UserResetPasswordSuccessView.as_view(),
name='reset-password-success'), name='reset-password-success'),
# User view # User view
url(r'^user$', views.UserListView.as_view(), name='user-list'), 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]+)$', views.UserDetailView.as_view(),
url(r'^user/(?P<pk>[0-9]+)/asset-permission$', views.UserAssetPermissionView.as_view(), name='user-detail'),
url(r'^user/(?P<pk>[0-9]+)/asset-permission$',
views.UserAssetPermissionView.as_view(),
name='user-asset-permission'), 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'), 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]+)/assets',
url(r'^user/(?P<pk>[0-9]+)/login-history', views.UserDetailView.as_view(), name='user-login-history'), views.UserGrantedAssetView.as_view(),
url(r'^user/export/', views.UserExportView.as_view(), name='user-export'), name='user-granted-asset'),
url(r'^first-login/$', views.UserFirstLoginView.as_view(), name='user-first-login'), url(r'^user/(?P<pk>[0-9]+)/login-history',
url(r'^user/import/$', views.BulkImportUserView.as_view(), name='user-import'), views.UserDetailView.as_view(),
# url(r'^user/(?P<pk>[0-9]+)/assets-perm$', views.UserDetailView.as_view(), name='user-detail'), name='user-login-history'),
url(r'^user/create$', views.UserCreateView.as_view(), name='user-create'), url(r'^user/export/',
url(r'^user/(?P<pk>[0-9]+)/update$', views.UserUpdateView.as_view(), name='user-update'), 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 # User group view
url(r'^user-group$', views.UserGroupListView.as_view(), name='user-group-list'), url(r'^user-group$',
url(r'^user-group/(?P<pk>[0-9]+)$', views.UserGroupDetailView.as_view(), name='user-group-detail'), views.UserGroupListView.as_view(),
url(r'^user-group/create$', views.UserGroupCreateView.as_view(), name='user-group-create'), name='user-group-list'),
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]+)$',
url(r'^user-group/(?P<pk>[0-9]+)/asset-permission$', views.UserGroupAssetPermissionView.as_view(), 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'), 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'), 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'), name='user-group-granted-asset'),
] ]

View File

@ -1,593 +0,0 @@
# ~*~ coding: utf-8 ~*~
from __future__ import unicode_literals
import json
import uuid
from openpyxl import Workbook
from openpyxl.writer.excel import save_virtual_workbook
from openpyxl import load_workbook
from django import forms
from django.utils import timezone
from django.core.cache import cache
from django.db import IntegrityError
from django.contrib.auth import login as auth_login, logout as auth_logout
from django.contrib.auth.mixins import LoginRequiredMixin
from django.contrib.messages.views import SuccessMessageMixin
from django.core.files.storage import default_storage
from django.http import HttpResponseRedirect, HttpResponse, JsonResponse
from django.shortcuts import reverse, redirect
from django.utils.decorators import method_decorator
from django.utils.translation import ugettext as _
from django.urls import reverse_lazy
from django.views import View
from django.views.decorators.cache import never_cache
from django.views.decorators.csrf import csrf_protect, csrf_exempt
from django.views.decorators.debug import sensitive_post_parameters
from django.views.generic.base import TemplateView
from django.views.generic.list import ListView
from django.views.generic.edit import CreateView, DeleteView, UpdateView, FormView, SingleObjectMixin, FormMixin
from django.views.generic.detail import DetailView
from formtools.wizard.views import SessionWizardView
from common.mixins import JSONResponseMixin
from common.utils import get_object_or_none, get_logger
from perms.models import AssetPermission
from .models import User, UserGroup
from .utils import AdminUserRequiredMixin, user_add_success_next, send_reset_password_mail
from .hands import write_login_log_async
from . import forms
logger = get_logger(__name__)
@method_decorator(sensitive_post_parameters(), name='dispatch')
@method_decorator(csrf_protect, name='dispatch')
@method_decorator(never_cache, name='dispatch')
class UserLoginView(FormView):
template_name = 'users/login.html'
form_class = forms.UserLoginForm
redirect_field_name = 'next'
def get(self, request, *args, **kwargs):
if request.user.is_staff:
return redirect(self.get_success_url())
return super(UserLoginView, self).get(request, *args, **kwargs)
def form_valid(self, form):
auth_login(self.request, form.get_user())
login_ip = self.request.META.get('REMOTE_ADDR', '')
user_agent = self.request.META.get('HTTP_USER_AGENT', '')
write_login_log_async.delay(self.request.user.username, self.request.user.name,
login_type='W', login_ip=login_ip, user_agent=user_agent)
return redirect(self.get_success_url())
def get_success_url(self):
if self.request.user.is_first_login:
return reverse('users:user-first-login')
return self.request.POST.get(
self.redirect_field_name,
self.request.GET.get(self.redirect_field_name, reverse('index')))
@method_decorator(never_cache, name='dispatch')
class UserLogoutView(TemplateView):
template_name = 'flash_message_standalone.html'
def get(self, request, *args, **kwargs):
auth_logout(request)
return super(UserLogoutView, self).get(request)
def get_context_data(self, **kwargs):
context = {
'title': _('Logout success'),
'messages': _('Logout success, return login page'),
'redirect_url': reverse('users:login'),
'auto_redirect': True,
}
kwargs.update(context)
return super(UserLogoutView, self).get_context_data(**kwargs)
class UserListView(AdminUserRequiredMixin, TemplateView):
template_name = 'users/user_list.html'
def get_context_data(self, **kwargs):
context = super(UserListView, self).get_context_data(**kwargs)
context.update({
'app': _('Users'),
'action': _('User list'),
'groups': UserGroup.objects.all()
})
return context
class UserCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateView):
model = User
form_class = forms.UserCreateUpdateForm
template_name = 'users/user_create.html'
success_url = reverse_lazy('users:user-list')
success_message = _('Create user <a href="%s">%s</a> successfully.')
def get_context_data(self, **kwargs):
context = super(UserCreateView, self).get_context_data(**kwargs)
context.update({'app': _('Users'), 'action': _('Create user')})
return context
def form_valid(self, form):
user = form.save(commit=False)
user.created_by = self.request.user.username or 'System'
user.save()
user_add_success_next(user)
return super(UserCreateView, self).form_valid(form)
def get_success_message(self, cleaned_data):
return self.success_message % (
reverse_lazy('users:user-detail', kwargs={'pk': self.object.pk}),
self.object.name,
)
class UserUpdateView(AdminUserRequiredMixin, UpdateView):
model = User
form_class = forms.UserCreateUpdateForm
template_name = 'users/user_update.html'
context_object_name = 'user_object'
success_url = reverse_lazy('users:user-list')
def form_valid(self, form):
username = self.object.username
user = form.save(commit=False)
user.username = username
user.save()
password = self.request.POST.get('password', '')
if password:
user.set_password(password)
return super(UserUpdateView, self).form_valid(form)
def get_context_data(self, **kwargs):
context = super(UserUpdateView, self).get_context_data(**kwargs)
context.update({'app': _('Users'), 'action': _('Update user')})
return context
class UserDetailView(AdminUserRequiredMixin, DetailView):
model = User
template_name = 'users/user_detail.html'
context_object_name = "user"
def get_context_data(self, **kwargs):
groups = UserGroup.objects.exclude(id__in=self.object.groups.all())
context = {'app': _('Users'), 'action': _('User detail'), 'groups': groups}
kwargs.update(context)
return super(UserDetailView, self).get_context_data(**kwargs)
class UserGroupListView(AdminUserRequiredMixin, TemplateView):
template_name = 'users/user_group_list.html'
def get_context_data(self, **kwargs):
context = super(UserGroupListView, self).get_context_data(**kwargs)
context.update({'app': _('Users'), 'action': _('User group list')})
return context
class UserGroupCreateView(AdminUserRequiredMixin, CreateView):
model = UserGroup
form_class = forms.UserGroupForm
template_name = 'users/user_group_create_update.html'
success_url = reverse_lazy('users:user-group-list')
def get_context_data(self, **kwargs):
context = super(UserGroupCreateView, self).get_context_data(**kwargs)
users = User.objects.all()
context.update({'app': _('Users'), 'action': _('Create user group'), 'users': users})
return context
def form_valid(self, form):
user_group = form.save()
users_id_list = self.request.POST.getlist('users', [])
users = User.objects.filter(id__in=users_id_list)
user_group.created_by = self.request.user.username or 'Admin'
user_group.users.add(*users)
user_group.save()
return super(UserGroupCreateView, self).form_valid(form)
class UserGroupUpdateView(AdminUserRequiredMixin, UpdateView):
model = UserGroup
form_class = forms.UserGroupForm
template_name = 'users/user_group_create_update.html'
success_url = reverse_lazy('users:user-group-list')
def get_context_data(self, **kwargs):
# self.object = self.get_object()
context = super(UserGroupUpdateView, self).get_context_data(**kwargs)
users = User.objects.all()
group_users = [user.id for user in self.object.users.all()]
context.update({
'app': _('Users'),
'action': _('Update User Group'),
'users': users,
'group_users': group_users
})
return context
def form_valid(self, form):
user_group = form.save()
users_id_list = self.request.POST.getlist('users', [])
users = User.objects.filter(id__in=users_id_list)
user_group.users.clear()
user_group.users.add(*users)
user_group.save()
return super(UserGroupUpdateView, self).form_valid(form)
class UserGroupDetailView(AdminUserRequiredMixin, DetailView):
model = UserGroup
context_object_name = 'user_group'
template_name = 'users/user_group_detail.html'
def get_context_data(self, **kwargs):
users = User.objects.exclude(id__in=self.object.users.all())
context = {
'app': _('Users'),
'action': _('User Group Detail'),
'users': users,
}
kwargs.update(context)
return super(UserGroupDetailView, self).get_context_data(**kwargs)
class UserForgotPasswordView(TemplateView):
template_name = 'users/forgot_password.html'
def post(self, request):
email = request.POST.get('email')
user = get_object_or_none(User, email=email)
if not user:
return self.get(request, errors=_('Email address invalid, input again'))
else:
send_reset_password_mail(user)
return HttpResponseRedirect(reverse('users:forgot-password-sendmail-success'))
class UserForgotPasswordSendmailSuccessView(TemplateView):
template_name = 'flash_message_standalone.html'
def get_context_data(self, **kwargs):
context = {
'title': _('Send reset password message'),
'messages': _('Send reset password mail success, login your mail box and follow it '),
'redirect_url': reverse('users:login'),
}
kwargs.update(context)
return super(UserForgotPasswordSendmailSuccessView, self).get_context_data(**kwargs)
class UserResetPasswordSuccessView(TemplateView):
template_name = 'flash_message_standalone.html'
def get_context_data(self, **kwargs):
context = {
'title': _('Reset password success'),
'messages': _('Reset password success, return to login page'),
'redirect_url': reverse('users:login'),
'auto_redirect': True,
}
kwargs.update(context)
return super(UserResetPasswordSuccessView, self).get_context_data(**kwargs)
class UserResetPasswordView(TemplateView):
template_name = 'users/reset_password.html'
def get(self, request, *args, **kwargs):
token = request.GET.get('token')
user = User.validate_reset_token(token)
if not user:
kwargs.update({'errors': _('Token invalid or expired')})
return super(UserResetPasswordView, self).get(request, *args, **kwargs)
def post(self, request, *args, **kwargs):
password = request.POST.get('password')
password_confirm = request.POST.get('password-confirm')
token = request.GET.get('token')
if password != password_confirm:
return self.get(request, errors=_('Password not same'))
user = User.validate_reset_token(token)
if not user:
return self.get(request, errors=_('Token invalid or expired'))
user.reset_password(password)
return HttpResponseRedirect(reverse('users:reset-password-success'))
class UserFirstLoginView(LoginRequiredMixin, SessionWizardView):
template_name = 'users/first_login.html'
form_list = [forms.UserInfoForm, forms.UserKeyForm]
file_storage = default_storage
def dispatch(self, request, *args, **kwargs):
if request.user.is_authenticated() and not request.user.is_first_login:
return redirect(reverse('index'))
return super(UserFirstLoginView, self).dispatch(request, *args, **kwargs)
def done(self, form_list, form_dict, **kwargs):
user = self.request.user
for form in form_list:
for field in form:
if field.value():
setattr(user, field.name, field.value())
if field.name == 'enable_otp':
user.enable_otp = field.value()
user.is_first_login = False
user.is_public_key_valid = True
user.save()
return redirect(reverse('index'))
def get_context_data(self, **kwargs):
context = super(UserFirstLoginView, self).get_context_data(**kwargs)
context.update({'app': _('Users'), 'action': _('First Login')})
return context
def get_form_initial(self, step):
user = self.request.user
if step == '0':
return {
'name': user.name or user.username,
'enable_otp': user.enable_otp or True,
'wechat': user.wechat or '',
'phone': user.phone or ''
}
return super(UserFirstLoginView, self).get_form_initial(step)
def get_form(self, step=None, data=None, files=None):
form = super(UserFirstLoginView, self).get_form(step, data, files)
if step is None:
step = self.steps.current
if step == '1':
form.user = self.request.user
return form
class UserAssetPermissionView(AdminUserRequiredMixin, FormMixin, SingleObjectMixin, ListView):
model = User
template_name = 'users/user_asset_permission.html'
context_object_name = 'user'
form_class = forms.UserPrivateAssetPermissionForm
def get(self, request, *args, **kwargs):
self.object = self.get_object(queryset=User.objects.all())
return super(UserAssetPermissionView, self).get(request, *args, **kwargs)
def get_context_data(self, **kwargs):
context = {
'app': 'Users',
'action': 'User asset permissions',
}
kwargs.update(context)
return super(UserAssetPermissionView, self).get_context_data(**kwargs)
class UserGroupAssetPermissionView(AdminUserRequiredMixin, FormMixin, SingleObjectMixin, ListView):
model = UserGroup
template_name = 'users/user_group_asset_permission.html'
context_object_name = 'user_group'
form_class = forms.UserPrivateAssetPermissionForm
def get(self, request, *args, **kwargs):
self.object = self.get_object(queryset=UserGroup.objects.all())
return super(UserGroupAssetPermissionView, self).get(request, *args, **kwargs)
def get_context_data(self, **kwargs):
context = {
'app': 'Users',
'action': 'User group asset permissions',
}
kwargs.update(context)
return super(UserGroupAssetPermissionView, self).get_context_data(**kwargs)
class UserAssetPermissionCreateView(AdminUserRequiredMixin, CreateView):
form_class = forms.UserPrivateAssetPermissionForm
model = AssetPermission
def get(self, request, *args, **kwargs):
user = self.get_object(queryset=User.objects.all())
return redirect(reverse('users:user-asset-permission', kwargs={'pk': user.id}))
def post(self, request, *args, **kwargs):
self.user = self.get_object(queryset=User.objects.all())
return super(UserAssetPermissionCreateView, self).post(request, *args, **kwargs)
def get_form(self, form_class=None):
form = super(UserAssetPermissionCreateView, self).get_form(form_class=form_class)
form.user = self.user
return form
def form_invalid(self, form):
return redirect(reverse('users:user-asset-permission', kwargs={'pk': self.user.id}))
def get_success_url(self):
return reverse('users:user-asset-permission', kwargs={'pk': self.user.id})
class UserGroupAssetPermissionCreateView(AdminUserRequiredMixin, CreateView):
form_class = forms.UserGroupPrivateAssetPermissionForm
model = AssetPermission
def get(self, request, *args, **kwargs):
user_group = self.get_object(queryset=UserGroup.objects.all())
return redirect(reverse('users:user-group-asset-permission', kwargs={'pk': user_group.id}))
def post(self, request, *args, **kwargs):
self.user_group = self.get_object(queryset=UserGroup.objects.all())
return super(UserGroupAssetPermissionCreateView, self).post(request, *args, **kwargs)
def get_form(self, form_class=None):
form = super(UserGroupAssetPermissionCreateView, self).get_form(form_class=form_class)
form.user_group = self.user_group
return form
def form_invalid(self, form):
return redirect(reverse('users:user-group-asset-permission', kwargs={'pk': self.user_group.id}))
def get_success_url(self):
return reverse('users:user-group-asset-permission', kwargs={'pk': self.user_group.id})
class UserGrantedAssetView(AdminUserRequiredMixin, DetailView):
model = User
template_name = 'users/user_granted_asset.html'
context_object_name = 'user'
def get(self, request, *args, **kwargs):
self.object = self.get_object(queryset=User.objects.all())
return super(UserGrantedAssetView, self).get(request, *args, **kwargs)
def get_context_data(self, **kwargs):
context = {
'app': 'User',
'action': 'User granted asset',
}
kwargs.update(context)
return super(UserGrantedAssetView, self).get_context_data(**kwargs)
class UserGroupGrantedAssetView(AdminUserRequiredMixin, DetailView):
model = User
template_name = 'users/user_group_granted_asset.html'
context_object_name = 'user_group'
def get(self, request, *args, **kwargs):
self.object = self.get_object(queryset=UserGroup.objects.all())
return super(UserGroupGrantedAssetView, self).get(request, *args, **kwargs)
def get_context_data(self, **kwargs):
context = {
'app': 'User',
'action': 'User group granted asset',
}
kwargs.update(context)
return super(UserGroupGrantedAssetView, self).get_context_data(**kwargs)
class BulkImportUserView(AdminUserRequiredMixin, JSONResponseMixin, FormView):
form_class = forms.FileForm
def form_invalid(self, form):
try:
error = form.errors.values()[-1][-1]
except Exception as e:
print e
error = _('Invalid file.')
data = {
'success': False,
'msg': error
}
return self.render_json_response(data)
def form_valid(self, form):
try:
wb = load_workbook(form.cleaned_data['file'])
ws = wb.get_active_sheet()
except Exception as e:
print(e)
data = {'valid': False, 'msg': 'Not a valid Excel file'}
return self.render_json_response(data)
rows = ws.rows
header_need = ["name", 'username', 'email', 'groups', "role", "phone", "wechat", "comment"]
header = [col.value for col in next(rows)]
print(header)
if header != header_need:
data = {'valid': False, 'msg': 'Must be same format as template or export file'}
return self.render_json_response(data)
created = []
updated = []
failed = []
for row in rows:
user_dict = dict(zip(header, [col.value for col in row]))
groups_name = user_dict.pop('groups')
if groups_name:
groups_name = groups_name.split(',')
groups = UserGroup.objects.filter(name__in=groups_name)
else:
groups = None
try:
user = User.objects.create(**user_dict)
user_add_success_next(user)
created.append(user_dict['username'])
except IntegrityError as e:
user = User.objects.filter(username=user_dict['username'])
if not user:
failed.append(user_dict['username'])
continue
user.update(**user_dict)
user = user[0]
updated.append(user_dict['username'])
except TypeError as e:
print(e)
failed.append(user_dict['username'])
user = None
if user and groups:
user.groups.add(*tuple(groups))
user.save()
data = {
'created': created,
'created_info': 'Created {}'.format(len(created)),
'updated': updated,
'updated_info': 'Updated {}'.format(len(updated)),
'failed': failed,
'failed_info': 'Failed {}'.format(len(failed)),
'valid': True,
'msg': 'Created: {}. Updated: {}, Error: {}'.format(len(created), len(updated), len(failed))
}
return self.render_json_response(data)
@method_decorator(csrf_exempt, name='dispatch')
class UserExportView(View):
def get(self, request, *args, **kwargs):
spm = request.GET.get('spm', '')
users_id = cache.get(spm)
if not users_id and not isinstance(users_id, list):
return HttpResponse('May be expired', status=404)
users = User.objects.filter(id__in=users_id)
wb = Workbook()
ws = wb.active
ws.title = 'User'
header = ["name", 'username', 'email', 'groups', "role", "phone", "wechat", "comment"]
ws.append(header)
for user in users:
ws.append([user.name, user.username, user.email,
','.join([group.name for group in user.groups.all()]),
user.role, user.phone, user.wechat, user.comment])
filename = 'users-{}.xlsx'.format(timezone.localtime(timezone.now()).strftime('%Y-%m-%d_%H-%M-%S'))
response = HttpResponse(save_virtual_workbook(wb), content_type='applications/vnd.ms-excel')
response['Content-Disposition'] = 'attachment; filename="%s"' % filename
return response
def post(self, request, *args, **kwargs):
try:
users_id = json.loads(request.body).get('users_id', [])
except ValueError:
return HttpResponse('Json object not valid', status=400)
spm = uuid.uuid4().get_hex()
cache.set(spm, users_id, 300)
url = reverse('users:user-export') + '?spm=%s' % spm
return JsonResponse({'redirect': url})

View File

@ -0,0 +1,5 @@
# ~*~ coding: utf-8 ~*~
from .login import *
from .user import *
from .group import *

169
apps/users/views/group.py Normal file
View File

@ -0,0 +1,169 @@
# ~*~ coding: utf-8 ~*~
from __future__ import unicode_literals
from django import forms
from django.shortcuts import reverse, redirect
from django.utils.translation import ugettext as _
from django.urls import reverse_lazy
from django.views.generic import ListView
from django.views.generic.base import TemplateView
from django.views.generic.edit import CreateView, UpdateView, FormMixin
from django.views.generic.detail import DetailView, SingleObjectMixin
from common.utils import get_logger
from perms.models import AssetPermission
from ..models import User, UserGroup
from ..utils import AdminUserRequiredMixin
from .. import forms
__all__ = ['UserGroupListView', 'UserGroupCreateView', 'UserGroupDetailView',
'UserGroupUpdateView', 'UserGroupAssetPermissionCreateView',
'UserGroupAssetPermissionView', 'UserGroupGrantedAssetView']
logger = get_logger(__name__)
class UserGroupListView(AdminUserRequiredMixin, TemplateView):
template_name = 'users/user_group_list.html'
def get_context_data(self, **kwargs):
context = super(UserGroupListView, self).get_context_data(**kwargs)
context.update({'app': _('Users'), 'action': _('User group list')})
return context
class UserGroupCreateView(AdminUserRequiredMixin, CreateView):
model = UserGroup
form_class = forms.UserGroupForm
template_name = 'users/user_group_create_update.html'
success_url = reverse_lazy('users:user-group-list')
def get_context_data(self, **kwargs):
context = super(UserGroupCreateView, self).get_context_data(**kwargs)
users = User.objects.all()
context.update({'app': _('Users'), 'action': _('Create user group'),
'users': users})
return context
def form_valid(self, form):
user_group = form.save()
users_id_list = self.request.POST.getlist('users', [])
users = User.objects.filter(id__in=users_id_list)
user_group.created_by = self.request.user.username or 'Admin'
user_group.users.add(*users)
user_group.save()
return super(UserGroupCreateView, self).form_valid(form)
class UserGroupUpdateView(AdminUserRequiredMixin, UpdateView):
model = UserGroup
form_class = forms.UserGroupForm
template_name = 'users/user_group_create_update.html'
success_url = reverse_lazy('users:user-group-list')
def get_context_data(self, **kwargs):
# self.object = self.get_object()
context = super(UserGroupUpdateView, self).get_context_data(**kwargs)
users = User.objects.all()
group_users = [user.id for user in self.object.users.all()]
context.update({
'app': _('Users'),
'action': _('Update User Group'),
'users': users,
'group_users': group_users
})
return context
def form_valid(self, form):
user_group = form.save()
users_id_list = self.request.POST.getlist('users', [])
users = User.objects.filter(id__in=users_id_list)
user_group.users.clear()
user_group.users.add(*users)
user_group.save()
return super(UserGroupUpdateView, self).form_valid(form)
class UserGroupDetailView(AdminUserRequiredMixin, DetailView):
model = UserGroup
context_object_name = 'user_group'
template_name = 'users/user_group_detail.html'
def get_context_data(self, **kwargs):
users = User.objects.exclude(id__in=self.object.users.all())
context = {
'app': _('Users'),
'action': _('User Group Detail'),
'users': users,
}
kwargs.update(context)
return super(UserGroupDetailView, self).get_context_data(**kwargs)
class UserGroupAssetPermissionView(AdminUserRequiredMixin, FormMixin,
SingleObjectMixin, ListView):
model = UserGroup
template_name = 'users/user_group_asset_permission.html'
context_object_name = 'user_group'
form_class = forms.UserPrivateAssetPermissionForm
def get(self, request, *args, **kwargs):
self.object = self.get_object(queryset=UserGroup.objects.all())
return super(UserGroupAssetPermissionView, self)\
.get(request, *args, **kwargs)
def get_context_data(self, **kwargs):
context = {
'app': 'Users',
'action': 'User group asset permissions',
}
kwargs.update(context)
return super(UserGroupAssetPermissionView, self)\
.get_context_data(**kwargs)
class UserGroupAssetPermissionCreateView(AdminUserRequiredMixin, CreateView):
form_class = forms.UserGroupPrivateAssetPermissionForm
model = AssetPermission
def get(self, request, *args, **kwargs):
user_group = self.get_object(queryset=UserGroup.objects.all())
return redirect(reverse('users:user-group-asset-permission',
kwargs={'pk': user_group.id}))
def post(self, request, *args, **kwargs):
self.user_group = self.get_object(queryset=UserGroup.objects.all())
return super(UserGroupAssetPermissionCreateView, self)\
.post(request, *args, **kwargs)
def get_form(self, form_class=None):
form = super(UserGroupAssetPermissionCreateView, self)\
.get_form(form_class=form_class)
form.user_group = self.user_group
return form
def form_invalid(self, form):
return redirect(reverse('users:user-group-asset-permission',
kwargs={'pk': self.user_group.id}))
def get_success_url(self):
return reverse('users:user-group-asset-permission',
kwargs={'pk': self.user_group.id})
class UserGroupGrantedAssetView(AdminUserRequiredMixin, DetailView):
model = User
template_name = 'users/user_group_granted_asset.html'
context_object_name = 'user_group'
def get(self, request, *args, **kwargs):
self.object = self.get_object(queryset=UserGroup.objects.all())
return super(UserGroupGrantedAssetView, self)\
.get(request, *args, **kwargs)
def get_context_data(self, **kwargs):
context = {
'app': 'User',
'action': 'User group granted asset',
}
kwargs.update(context)
return super(UserGroupGrantedAssetView, self).get_context_data(**kwargs)

202
apps/users/views/login.py Normal file
View File

@ -0,0 +1,202 @@
# ~*~ 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

300
apps/users/views/user.py Normal file
View File

@ -0,0 +1,300 @@
# ~*~ coding: utf-8 ~*~
from __future__ import unicode_literals
import uuid
import json
from django.shortcuts import redirect
from openpyxl import Workbook
from openpyxl.writer.excel import save_virtual_workbook
from openpyxl import load_workbook
from django import forms
from django.core.cache import cache
from django.http import HttpResponse, JsonResponse
from django.contrib.messages.views import SuccessMessageMixin
from django.urls import reverse_lazy, reverse
from django.utils import timezone
from django.utils.translation import ugettext as _
from django.utils.decorators import method_decorator
from django.views import View
from django.views.generic import ListView
from django.views.generic.base import TemplateView
from django.views.generic.edit import CreateView, UpdateView, FormMixin, \
FormView
from django.views.generic.detail import DetailView, SingleObjectMixin
from django.views.decorators.csrf import csrf_exempt
from common.mixins import JSONResponseMixin
from common.utils import get_logger
from perms.models import AssetPermission
from ..models import User, UserGroup
from ..utils import AdminUserRequiredMixin, user_add_success_next
from .. import forms
__all__ = ['UserListView', 'UserCreateView', 'UserDetailView',
'UserUpdateView', 'UserAssetPermissionCreateView',
'UserAssetPermissionView', 'UserGrantedAssetView',
'UserExportView', 'UserBulkImportView']
logger = get_logger(__name__)
class UserListView(AdminUserRequiredMixin, TemplateView):
template_name = 'users/user_list.html'
def get_context_data(self, **kwargs):
context = super(UserListView, self).get_context_data(**kwargs)
context.update({
'app': _('Users'),
'action': _('User list'),
'groups': UserGroup.objects.all()
})
return context
class UserCreateView(AdminUserRequiredMixin, SuccessMessageMixin, CreateView):
model = User
form_class = forms.UserCreateUpdateForm
template_name = 'users/user_create.html'
success_url = reverse_lazy('users:user-list')
success_message = _('Create user <a href="%s">%s</a> successfully.')
def get_context_data(self, **kwargs):
context = super(UserCreateView, self).get_context_data(**kwargs)
context.update({'app': _('Users'), 'action': _('Create user')})
return context
def form_valid(self, form):
user = form.save(commit=False)
user.created_by = self.request.user.username or 'System'
user.save()
user_add_success_next(user)
return super(UserCreateView, self).form_valid(form)
def get_success_message(self, cleaned_data):
return self.success_message % (
reverse_lazy('users:user-detail', kwargs={'pk': self.object.pk}),
self.object.name,
)
class UserUpdateView(AdminUserRequiredMixin, UpdateView):
model = User
form_class = forms.UserCreateUpdateForm
template_name = 'users/user_update.html'
context_object_name = 'user_object'
success_url = reverse_lazy('users:user-list')
def form_valid(self, form):
username = self.object.username
user = form.save(commit=False)
user.username = username
user.save()
password = self.request.POST.get('password', '')
if password:
user.set_password(password)
return super(UserUpdateView, self).form_valid(form)
def get_context_data(self, **kwargs):
context = super(UserUpdateView, self).get_context_data(**kwargs)
context.update({'app': _('Users'), 'action': _('Update user')})
return context
class UserDetailView(AdminUserRequiredMixin, DetailView):
model = User
template_name = 'users/user_detail.html'
context_object_name = "user"
def get_context_data(self, **kwargs):
groups = UserGroup.objects.exclude(id__in=self.object.groups.all())
context = {
'app': _('Users'),
'action': _('User detail'),
'groups': groups
}
kwargs.update(context)
return super(UserDetailView, self).get_context_data(**kwargs)
@method_decorator(csrf_exempt, name='dispatch')
class UserExportView(View):
def get(self, request, *args, **kwargs):
spm = request.GET.get('spm', '')
users_id = cache.get(spm)
if not users_id and not isinstance(users_id, list):
return HttpResponse('May be expired', status=404)
users = User.objects.filter(id__in=users_id)
wb = Workbook()
ws = wb.active
ws.title = 'User'
header = ["name", 'username', 'email', 'groups',
"role", "phone", "wechat", "comment"]
ws.append(header)
for user in users:
ws.append([user.name, user.username, user.email,
','.join([group.name for group in user.groups.all()]),
user.role, user.phone, user.wechat, user.comment])
filename = 'users-{}.xlsx'.format(
timezone.localtime(timezone.now()).strftime('%Y-%m-%d_%H-%M-%S'))
response = HttpResponse(save_virtual_workbook(wb),
content_type='applications/vnd.ms-excel')
response['Content-Disposition'] = 'attachment; filename="%s"' % filename
return response
def post(self, request, *args, **kwargs):
try:
users_id = json.loads(request.body).get('users_id', [])
except ValueError:
return HttpResponse('Json object not valid', status=400)
spm = uuid.uuid4().get_hex()
cache.set(spm, users_id, 300)
url = reverse('users:user-export') + '?spm=%s' % spm
return JsonResponse({'redirect': url})
class UserBulkImportView(AdminUserRequiredMixin, JSONResponseMixin, FormView):
form_class = forms.FileForm
def form_invalid(self, form):
try:
error = form.errors.values()[-1][-1]
except Exception as e:
print e
error = _('Invalid file.')
data = {
'success': False,
'msg': error
}
return self.render_json_response(data)
def form_valid(self, form):
try:
wb = load_workbook(form.cleaned_data['file'])
ws = wb.get_active_sheet()
except Exception as e:
print(e)
data = {'valid': False, 'msg': 'Not a valid Excel file'}
return self.render_json_response(data)
rows = ws.rows
header_need = ["name", 'username', 'email', 'groups', "role", "phone", "wechat", "comment"]
header = [col.value for col in next(rows)]
print(header)
if header != header_need:
data = {'valid': False, 'msg': 'Must be same format as template or export file'}
return self.render_json_response(data)
created = []
updated = []
failed = []
for row in rows:
user_dict = dict(zip(header, [col.value for col in row]))
groups_name = user_dict.pop('groups')
if groups_name:
groups_name = groups_name.split(',')
groups = UserGroup.objects.filter(name__in=groups_name)
else:
groups = None
try:
user = User.objects.create(**user_dict)
user_add_success_next(user)
created.append(user_dict['username'])
except User.IntegrityError as e:
user = User.objects.filter(username=user_dict['username'])
if not user:
failed.append(user_dict['username'])
continue
user.update(**user_dict)
user = user[0]
updated.append(user_dict['username'])
except TypeError as e:
print(e)
failed.append(user_dict['username'])
user = None
if user and groups:
user.groups.add(*tuple(groups))
user.save()
data = {
'created': created,
'created_info': 'Created {}'.format(len(created)),
'updated': updated,
'updated_info': 'Updated {}'.format(len(updated)),
'failed': failed,
'failed_info': 'Failed {}'.format(len(failed)),
'valid': True,
'msg': 'Created: {}. Updated: {}, Error: {}'.format(
len(created), len(updated), len(failed))
}
return self.render_json_response(data)
class UserAssetPermissionView(AdminUserRequiredMixin, FormMixin,
SingleObjectMixin, ListView):
model = User
template_name = 'users/user_asset_permission.html'
context_object_name = 'user'
form_class = forms.UserPrivateAssetPermissionForm
def get(self, request, *args, **kwargs):
self.object = self.get_object(queryset=User.objects.all())
return super(UserAssetPermissionView, self).get(request, *args, **kwargs)
def get_context_data(self, **kwargs):
context = {
'app': 'Users',
'action': 'User asset permissions',
}
kwargs.update(context)
return super(UserAssetPermissionView, self).get_context_data(**kwargs)
class UserAssetPermissionCreateView(AdminUserRequiredMixin, CreateView):
form_class = forms.UserPrivateAssetPermissionForm
model = AssetPermission
def get(self, request, *args, **kwargs):
user = self.get_object(queryset=User.objects.all())
return redirect(reverse('users:user-asset-permission',
kwargs={'pk': user.id}))
def post(self, request, *args, **kwargs):
self.user = self.get_object(queryset=User.objects.all())
return super(UserAssetPermissionCreateView, self)\
.post(request, *args, **kwargs)
def get_form(self, form_class=None):
form = super(UserAssetPermissionCreateView, self)\
.get_form(form_class=form_class)
form.user = self.user
return form
def form_invalid(self, form):
return redirect(reverse('users:user-asset-permission',
kwargs={'pk': self.user.id}))
def get_success_url(self):
return reverse('users:user-asset-permission',
kwargs={'pk': self.user.id})
class UserGrantedAssetView(AdminUserRequiredMixin, DetailView):
model = User
template_name = 'users/user_granted_asset.html'
context_object_name = 'user'
def get(self, request, *args, **kwargs):
self.object = self.get_object(queryset=User.objects.all())
return super(UserGrantedAssetView, self).get(request, *args, **kwargs)
def get_context_data(self, **kwargs):
context = {
'app': 'User',
'action': 'User granted asset',
}
kwargs.update(context)
return super(UserGrantedAssetView, self).get_context_data(**kwargs)