Merge branch 'bugfix' of github.com:jumpserver/jumpserver into bugfix

pull/2874/head
BaiJiangJie 2019-07-02 18:01:20 +08:00
commit bbf5e28571
44 changed files with 425 additions and 1069 deletions

View File

@ -1,6 +1,8 @@
# -*- coding: utf-8 -*-
#
from django import forms
from django.core.exceptions import ValidationError
import re
from orgs.mixins import OrgModelForm
from ..models import CommandFilter, CommandFilterRule
@ -15,6 +17,8 @@ class CommandFilterForm(OrgModelForm):
class CommandFilterRuleForm(OrgModelForm):
invalid_pattern = re.compile(r'[\.\*\+\[\\\?\{\}\^\$\|\(\)\#\<\>]')
class Meta:
model = CommandFilterRule
fields = [
@ -25,3 +29,11 @@ class CommandFilterRuleForm(OrgModelForm):
'placeholder': 'eg:\r\nreboot\r\nrm -rf'
}),
}
def clean_content(self):
content = self.cleaned_data.get("content")
if self.invalid_pattern.search(content):
invalid_char = self.invalid_pattern.pattern.replace('\\', '')
msg = _("Content should not be contain: {}").format(invalid_char)
raise ValidationError(msg)
return content

View File

@ -1,35 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-01-05 10:07
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('assets', '0001_initial'),
]
operations = [
migrations.AlterModelOptions(
name='adminuser',
options={'ordering': ['name'], 'verbose_name': 'Admin user'},
),
migrations.AlterModelOptions(
name='asset',
options={'verbose_name': 'Asset'},
),
migrations.AlterModelOptions(
name='assetgroup',
options={'ordering': ['name'], 'verbose_name': 'Asset group'},
),
migrations.AlterModelOptions(
name='cluster',
options={'ordering': ['name'], 'verbose_name': 'Cluster'},
),
migrations.AlterModelOptions(
name='systemuser',
options={'ordering': ['name'], 'verbose_name': 'System user'},
),
]

View File

@ -1,22 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-01-09 15:31
from __future__ import unicode_literals
import assets.models.asset
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('assets', '0002_auto_20180105_1807'),
]
operations = [
migrations.AlterField(
model_name='asset',
name='cluster',
field=models.ForeignKey(default=assets.models.asset.default_cluster, on_delete=django.db.models.deletion.SET_DEFAULT, related_name='assets', to='assets.Cluster', verbose_name='Cluster'),
),
]

View File

@ -1,20 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-01-25 04:18
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('assets', '0003_auto_20180109_2331'),
]
operations = [
migrations.AlterField(
model_name='assetgroup',
name='created_by',
field=models.CharField(blank=True, max_length=32, null=True, verbose_name='Created by'),
),
]

View File

@ -1,40 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-01-26 08:37
from __future__ import unicode_literals
from django.db import migrations, models
import uuid
class Migration(migrations.Migration):
dependencies = [
('assets', '0004_auto_20180125_1218'),
]
operations = [
migrations.CreateModel(
name='Label',
fields=[
('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
('name', models.CharField(max_length=128, verbose_name='Name')),
('value', models.CharField(max_length=128, verbose_name='Value')),
('category', models.CharField(choices=[('S', 'System'), ('U', 'User')], default='U', max_length=128, verbose_name='Category')),
('is_active', models.BooleanField(default=True, verbose_name='Is active')),
('comment', models.TextField(blank=True, null=True, verbose_name='Comment')),
('date_created', models.DateTimeField(auto_now_add=True, null=True, verbose_name='Date created')),
],
options={
'db_table': 'assets_label',
},
),
migrations.AlterUniqueTogether(
name='label',
unique_together=set([('name', 'value')]),
),
migrations.AddField(
model_name='asset',
name='labels',
field=models.ManyToManyField(blank=True, related_name='assets', to='assets.Label', verbose_name='Labels'),
),
]

View File

@ -1,39 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-01-30 07:02
from __future__ import unicode_literals
from django.db import migrations
class Migration(migrations.Migration):
dependencies = [
('assets', '0005_auto_20180126_1637'),
]
operations = [
migrations.RemoveField(
model_name='asset',
name='cabinet_no',
),
migrations.RemoveField(
model_name='asset',
name='cabinet_pos',
),
migrations.RemoveField(
model_name='asset',
name='env',
),
migrations.RemoveField(
model_name='asset',
name='remote_card_ip',
),
migrations.RemoveField(
model_name='asset',
name='status',
),
migrations.RemoveField(
model_name='asset',
name='type',
),
]

View File

@ -1,60 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-02-25 10:15
from __future__ import unicode_literals
import assets.models.asset
from django.db import migrations, models
import django.db.models.deletion
import uuid
class Migration(migrations.Migration):
dependencies = [
('assets', '0006_auto_20180130_1502'),
]
operations = [
migrations.CreateModel(
name='Node',
fields=[
('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
('key', models.CharField(max_length=64, unique=True, verbose_name='Key')),
('value', models.CharField(max_length=128, unique=True, verbose_name='Value')),
('child_mark', models.IntegerField(default=0)),
('date_create', models.DateTimeField(auto_now_add=True)),
],
),
migrations.RemoveField(
model_name='asset',
name='cluster',
),
migrations.RemoveField(
model_name='asset',
name='groups',
),
migrations.RemoveField(
model_name='systemuser',
name='cluster',
),
migrations.AlterField(
model_name='asset',
name='admin_user',
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.PROTECT, to='assets.AdminUser', verbose_name='Admin user'),
),
migrations.AlterField(
model_name='systemuser',
name='protocol',
field=models.CharField(choices=[('ssh', 'ssh'), ('rdp', 'rdp')], default='ssh', max_length=16, verbose_name='Protocol'),
),
migrations.AddField(
model_name='asset',
name='nodes',
field=models.ManyToManyField(default=assets.models.asset.default_node, related_name='assets', to='assets.Node', verbose_name='Nodes'),
),
migrations.AddField(
model_name='systemuser',
name='nodes',
field=models.ManyToManyField(blank=True, to='assets.Node', verbose_name='Nodes'),
),
]

View File

@ -1,40 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-03-06 10:04
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('assets', '0007_auto_20180225_1815'),
]
operations = [
migrations.AlterField(
model_name='adminuser',
name='created_by',
field=models.CharField(max_length=128, null=True, verbose_name='Created by'),
),
migrations.AlterField(
model_name='adminuser',
name='username',
field=models.CharField(max_length=128, verbose_name='Username'),
),
migrations.AlterField(
model_name='asset',
name='platform',
field=models.CharField(choices=[('Linux', 'Linux'), ('Unix', 'Unix'), ('MacOS', 'MacOS'), ('BSD', 'BSD'), ('Windows', 'Windows'), ('Other', 'Other')], default='Linux', max_length=128, verbose_name='Platform'),
),
migrations.AlterField(
model_name='systemuser',
name='created_by',
field=models.CharField(max_length=128, null=True, verbose_name='Created by'),
),
migrations.AlterField(
model_name='systemuser',
name='username',
field=models.CharField(max_length=128, verbose_name='Username'),
),
]

View File

@ -1,20 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-03-07 04:12
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('assets', '0008_auto_20180306_1804'),
]
operations = [
migrations.AlterField(
model_name='node',
name='value',
field=models.CharField(max_length=128, verbose_name='Value'),
),
]

View File

@ -1,20 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-03-07 09:49
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('assets', '0009_auto_20180307_1212'),
]
operations = [
migrations.AlterField(
model_name='node',
name='value',
field=models.CharField(max_length=128, unique=True, verbose_name='Value'),
),
]

View File

@ -1,55 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-03-26 01:57
from __future__ import unicode_literals
import assets.models.utils
from django.db import migrations, models
import django.db.models.deletion
import uuid
class Migration(migrations.Migration):
dependencies = [
('assets', '0010_auto_20180307_1749'),
]
operations = [
migrations.CreateModel(
name='Domain',
fields=[
('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
('name', models.CharField(max_length=128, unique=True, verbose_name='Name')),
('comment', models.TextField(blank=True, verbose_name='Comment')),
('date_created', models.DateTimeField(auto_now_add=True, null=True, verbose_name='Date created')),
],
),
migrations.CreateModel(
name='Gateway',
fields=[
('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
('name', models.CharField(max_length=128, unique=True, verbose_name='Name')),
('username', models.CharField(max_length=128, verbose_name='Username')),
('_password', models.CharField(blank=True, max_length=256, null=True, verbose_name='Password')),
('_private_key', models.TextField(blank=True, max_length=4096, null=True, validators=[assets.models.utils.private_key_validator], verbose_name='SSH private key')),
('_public_key', models.TextField(blank=True, max_length=4096, verbose_name='SSH public key')),
('date_created', models.DateTimeField(auto_now_add=True)),
('date_updated', models.DateTimeField(auto_now=True)),
('created_by', models.CharField(max_length=128, null=True, verbose_name='Created by')),
('ip', models.GenericIPAddressField(db_index=True, verbose_name='IP')),
('port', models.IntegerField(default=22, verbose_name='Port')),
('protocol', models.CharField(choices=[('ssh', 'ssh'), ('rdp', 'rdp')], default='ssh', max_length=16, verbose_name='Protocol')),
('comment', models.CharField(blank=True, max_length=128, null=True, verbose_name='Comment')),
('is_active', models.BooleanField(default=True, verbose_name='Is active')),
('domain', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='assets.Domain', verbose_name='Domain')),
],
options={
'abstract': False,
},
),
migrations.AddField(
model_name='asset',
name='domain',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, related_name='assets', to='assets.Domain', verbose_name='Domain'),
),
]

View File

@ -1,21 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-04-04 05:02
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
dependencies = [
('assets', '0011_auto_20180326_0957'),
]
operations = [
migrations.AlterField(
model_name='asset',
name='domain',
field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='assets', to='assets.Domain', verbose_name='Domain'),
),
]

View File

@ -1,25 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-04-11 03:35
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('assets', '0012_auto_20180404_1302'),
]
operations = [
migrations.AddField(
model_name='systemuser',
name='assets',
field=models.ManyToManyField(blank=True, to='assets.Asset', verbose_name='Assets'),
),
migrations.AlterField(
model_name='systemuser',
name='sudo',
field=models.TextField(default='/bin/whoami', verbose_name='Sudo'),
),
]

View File

@ -1,31 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-04-27 04:45
from __future__ import unicode_literals
import django.core.validators
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('assets', '0013_auto_20180411_1135'),
]
operations = [
migrations.AlterField(
model_name='adminuser',
name='username',
field=models.CharField(max_length=32, validators=[django.core.validators.RegexValidator('^[0-9a-zA-Z_-]*$', 'Special char not allowed')], verbose_name='Username'),
),
migrations.AlterField(
model_name='gateway',
name='username',
field=models.CharField(max_length=32, validators=[django.core.validators.RegexValidator('^[0-9a-zA-Z_-]*$', 'Special char not allowed')], verbose_name='Username'),
),
migrations.AlterField(
model_name='systemuser',
name='username',
field=models.CharField(max_length=32, validators=[django.core.validators.RegexValidator('^[0-9a-zA-Z_-]*$', 'Special char not allowed')], verbose_name='Username'),
),
]

View File

@ -1,31 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-05-10 04:35
from __future__ import unicode_literals
import django.core.validators
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('assets', '0014_auto_20180427_1245'),
]
operations = [
migrations.AlterField(
model_name='adminuser',
name='username',
field=models.CharField(max_length=32, validators=[django.core.validators.RegexValidator('^[0-9a-zA-Z_@\\-\\.]*$', 'Special char not allowed')], verbose_name='Username'),
),
migrations.AlterField(
model_name='gateway',
name='username',
field=models.CharField(max_length=32, validators=[django.core.validators.RegexValidator('^[0-9a-zA-Z_@\\-\\.]*$', 'Special char not allowed')], verbose_name='Username'),
),
migrations.AlterField(
model_name='systemuser',
name='username',
field=models.CharField(max_length=32, validators=[django.core.validators.RegexValidator('^[0-9a-zA-Z_@\\-\\.]*$', 'Special char not allowed')], verbose_name='Username'),
),
]

View File

@ -1,20 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-05-11 04:03
from __future__ import unicode_literals
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('assets', '0015_auto_20180510_1235'),
]
operations = [
migrations.AlterField(
model_name='node',
name='value',
field=models.CharField(max_length=128, verbose_name='Value'),
),
]

View File

@ -1,58 +0,0 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2018-07-02 06:15
from __future__ import unicode_literals
import django.core.validators
from django.db import migrations, models
def migrate_win_to_ssh_protocol(apps, schema_editor):
asset_model = apps.get_model("assets", "Asset")
db_alias = schema_editor.connection.alias
asset_model.objects.using(db_alias).filter(platform__startswith='Win').update(protocol='rdp')
class Migration(migrations.Migration):
dependencies = [
('assets', '0016_auto_20180511_1203'),
]
operations = [
migrations.AddField(
model_name='asset',
name='protocol',
field=models.CharField(choices=[('ssh', 'ssh'), ('rdp', 'rdp'), ('telnet', 'telnet (beta)')], default='ssh', max_length=128, verbose_name='Protocol'),
),
migrations.AddField(
model_name='systemuser',
name='login_mode',
field=models.CharField(choices=[('auto', 'Automatic login'), ('manual', 'Manually login')], default='auto', max_length=10, verbose_name='Login mode'),
),
migrations.AlterField(
model_name='adminuser',
name='username',
field=models.CharField(blank=True, max_length=32, validators=[django.core.validators.RegexValidator('^[0-9a-zA-Z_@\\-\\.]*$', 'Special char not allowed')], verbose_name='Username'),
),
migrations.AlterField(
model_name='asset',
name='platform',
field=models.CharField(choices=[('Linux', 'Linux'), ('Unix', 'Unix'), ('MacOS', 'MacOS'), ('BSD', 'BSD'), ('Windows', 'Windows'), ('Windows2016', 'Windows(2016)'), ('Other', 'Other')], default='Linux', max_length=128, verbose_name='Platform'),
),
migrations.AlterField(
model_name='gateway',
name='username',
field=models.CharField(blank=True, max_length=32, validators=[django.core.validators.RegexValidator('^[0-9a-zA-Z_@\\-\\.]*$', 'Special char not allowed')], verbose_name='Username'),
),
migrations.AlterField(
model_name='systemuser',
name='protocol',
field=models.CharField(choices=[('ssh', 'ssh'), ('rdp', 'rdp'), ('telnet', 'telnet (beta)')], default='ssh', max_length=16, verbose_name='Protocol'),
),
migrations.AlterField(
model_name='systemuser',
name='username',
field=models.CharField(blank=True, max_length=32, validators=[django.core.validators.RegexValidator('^[0-9a-zA-Z_@\\-\\.]*$', 'Special char not allowed')], verbose_name='Username'),
),
migrations.RunPython(migrate_win_to_ssh_protocol),
]

View File

@ -1,84 +0,0 @@
# Generated by Django 2.0.7 on 2018-08-07 03:16
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('assets', '0017_auto_20180702_1415'),
]
operations = [
migrations.AddField(
model_name='adminuser',
name='org_id',
field=models.CharField(blank=True, default=None, max_length=36, null=True),
),
migrations.AddField(
model_name='asset',
name='org_id',
field=models.CharField(blank=True, default=None, max_length=36, null=True),
),
migrations.AddField(
model_name='domain',
name='org_id',
field=models.CharField(blank=True, default=None, max_length=36, null=True),
),
migrations.AddField(
model_name='gateway',
name='org_id',
field=models.CharField(blank=True, default=None, max_length=36, null=True),
),
migrations.AddField(
model_name='label',
name='org_id',
field=models.CharField(blank=True, default=None, max_length=36, null=True),
),
migrations.AddField(
model_name='node',
name='org_id',
field=models.CharField(blank=True, default=None, max_length=36, null=True),
),
migrations.AddField(
model_name='systemuser',
name='org_id',
field=models.CharField(blank=True, default=None, max_length=36, null=True),
),
migrations.AlterField(
model_name='adminuser',
name='name',
field=models.CharField(max_length=128, verbose_name='Name'),
),
migrations.AlterField(
model_name='asset',
name='hostname',
field=models.CharField(max_length=128, verbose_name='Hostname'),
),
migrations.AlterField(
model_name='gateway',
name='name',
field=models.CharField(max_length=128, verbose_name='Name'),
),
migrations.AlterField(
model_name='systemuser',
name='name',
field=models.CharField(max_length=128, verbose_name='Name'),
),
migrations.AlterUniqueTogether(
name='adminuser',
unique_together={('name', 'org_id')},
),
migrations.AlterUniqueTogether(
name='asset',
unique_together={('org_id', 'hostname')},
),
migrations.AlterUniqueTogether(
name='gateway',
unique_together={('name', 'org_id')},
),
migrations.AlterUniqueTogether(
name='systemuser',
unique_together={('name', 'org_id')},
),
]

View File

@ -1,22 +0,0 @@
# Generated by Django 2.0.7 on 2018-08-16 05:20
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('assets', '0018_auto_20180807_1116'),
]
operations = [
migrations.AddField(
model_name='asset',
name='cpu_vcpus',
field=models.IntegerField(null=True, verbose_name='CPU vcpus'),
),
migrations.AlterUniqueTogether(
name='label',
unique_together={('name', 'value', 'org_id')},
),
]

View File

@ -1,6 +1,5 @@
# Generated by Django 2.1.7 on 2019-06-24 13:08
import common.fields.model
from django.db import migrations

View File

@ -1,5 +1,6 @@
# -*- coding: utf-8 -*-
#
import re
from rest_framework import serializers
from common.fields import ChoiceDisplayField
@ -20,8 +21,16 @@ class CommandFilterSerializer(BulkOrgResourceModelSerializer):
class CommandFilterRuleSerializer(BulkOrgResourceModelSerializer):
serializer_choice_field = ChoiceDisplayField
invalid_pattern = re.compile(r'[\.\*\+\[\\\?\{\}\^\$\|\(\)\#\<\>]')
class Meta:
model = CommandFilterRule
fields = '__all__'
list_serializer_class = AdaptedBulkListSerializer
def validate_content(self, content):
if self.invalid_pattern.search(content):
invalid_char = self.invalid_pattern.pattern.replace('\\', '')
msg = _("Content should not be contain: {}").format(invalid_char)
raise serializers.ValidationError(msg)
return content

View File

@ -1,234 +0,0 @@
# coding: utf-8
from django.db import models
from django.http import JsonResponse
from django.utils import timezone
from django.core.cache import cache
from django.utils.translation import ugettext_lazy as _
from django.contrib import messages
from rest_framework.utils import html
from rest_framework.settings import api_settings
from rest_framework.exceptions import ValidationError
from rest_framework.fields import SkipField
from .const import KEY_CACHE_RESOURCES_ID
class NoDeleteQuerySet(models.query.QuerySet):
def delete(self):
return self.update(is_discard=True, discard_time=timezone.now())
class NoDeleteManager(models.Manager):
def get_all(self):
return NoDeleteQuerySet(self.model, using=self._db)
def get_queryset(self):
return NoDeleteQuerySet(self.model, using=self._db).filter(is_discard=False)
def get_deleted(self):
return NoDeleteQuerySet(self.model, using=self._db).filter(is_discard=True)
class NoDeleteModelMixin(models.Model):
is_discard = models.BooleanField(verbose_name=_("is discard"), default=False)
discard_time = models.DateTimeField(verbose_name=_("discard time"), null=True, blank=True)
objects = NoDeleteManager()
class Meta:
abstract = True
def delete(self):
self.is_discard = True
self.discard_time = timezone.now()
return self.save()
class JSONResponseMixin(object):
"""JSON mixin"""
@staticmethod
def render_json_response(context):
return JsonResponse(context)
class IDInFilterMixin(object):
def filter_queryset(self, queryset):
queryset = super(IDInFilterMixin, self).filter_queryset(queryset)
id_list = self.request.query_params.get('id__in')
if id_list:
import json
try:
ids = json.loads(id_list)
except Exception as e:
return queryset
if isinstance(ids, list):
queryset = queryset.filter(id__in=ids)
return queryset
class IDInCacheFilterMixin(object):
def filter_queryset(self, queryset):
queryset = super(IDInCacheFilterMixin, self).filter_queryset(queryset)
spm = self.request.query_params.get('spm')
cache_key = KEY_CACHE_RESOURCES_ID.format(spm)
resources_id = cache.get(cache_key)
if resources_id and isinstance(resources_id, list):
queryset = queryset.filter(id__in=resources_id)
return queryset
class IDExportFilterMixin(object):
def filter_queryset(self, queryset):
# 下载导入模版
if self.request.query_params.get('template') == 'import':
return []
else:
return super(IDExportFilterMixin, self).filter_queryset(queryset)
class BulkSerializerMixin(object):
"""
Become rest_framework_bulk not support uuid as a primary key
so rewrite it. https://github.com/miki725/django-rest-framework-bulk/issues/66
"""
def to_internal_value(self, data):
from rest_framework_bulk import BulkListSerializer
ret = super(BulkSerializerMixin, self).to_internal_value(data)
id_attr = getattr(self.Meta, 'update_lookup_field', 'id')
if self.context.get('view'):
request_method = getattr(getattr(self.context.get('view'), 'request'), 'method', '')
# add update_lookup_field field back to validated data
# since super by default strips out read-only fields
# hence id will no longer be present in validated_data
if all((isinstance(self.root, BulkListSerializer),
id_attr,
request_method in ('PUT', 'PATCH'))):
id_field = self.fields[id_attr]
if data.get("id"):
id_value = id_field.to_internal_value(data.get("id"))
else:
id_value = id_field.to_internal_value(data.get("pk"))
ret[id_attr] = id_value
return ret
class BulkListSerializerMixin(object):
"""
Become rest_framework_bulk doing bulk update raise Exception:
'QuerySet' object has no attribute 'pk' when doing bulk update
so rewrite it .
https://github.com/miki725/django-rest-framework-bulk/issues/68
"""
def to_internal_value(self, data):
"""
List of dicts of native values <- List of dicts of primitive datatypes.
"""
if html.is_html_input(data):
data = html.parse_html_list(data)
if not isinstance(data, list):
message = self.error_messages['not_a_list'].format(
input_type=type(data).__name__
)
raise ValidationError({
api_settings.NON_FIELD_ERRORS_KEY: [message]
}, code='not_a_list')
if not self.allow_empty and len(data) == 0:
if self.parent and self.partial:
raise SkipField()
message = self.error_messages['empty']
raise ValidationError({
api_settings.NON_FIELD_ERRORS_KEY: [message]
}, code='empty')
ret = []
errors = []
for item in data:
try:
# prepare child serializer to only handle one instance
if 'id' in item.keys():
self.child.instance = self.instance.get(id=item['id']) if self.instance else None
if 'pk' in item.keys():
self.child.instance = self.instance.get(id=item['pk']) if self.instance else None
self.child.initial_data = item
# raw
validated = self.child.run_validation(item)
except ValidationError as exc:
errors.append(exc.detail)
else:
ret.append(validated)
errors.append({})
if any(errors):
raise ValidationError(errors)
return ret
class DatetimeSearchMixin:
date_format = '%Y-%m-%d'
date_from = date_to = None
def get_date_range(self):
date_from_s = self.request.GET.get('date_from')
date_to_s = self.request.GET.get('date_to')
if date_from_s:
date_from = timezone.datetime.strptime(date_from_s, self.date_format)
tz = timezone.get_current_timezone()
self.date_from = tz.localize(date_from)
else:
self.date_from = timezone.now() - timezone.timedelta(7)
if date_to_s:
date_to = timezone.datetime.strptime(
date_to_s + ' 23:59:59', self.date_format + ' %H:%M:%S'
)
self.date_to = date_to.replace(
tzinfo=timezone.get_current_timezone()
)
else:
self.date_to = timezone.now()
def get(self, request, *args, **kwargs):
self.get_date_range()
return super().get(request, *args, **kwargs)
class ApiMessageMixin:
success_message = _("%(name)s was %(action)s successfully")
_action_map = {"create": _("create"), "update": _("update")}
def get_success_message(self, cleaned_data):
if not isinstance(cleaned_data, dict):
return ''
data = {k: v for k, v in cleaned_data.items()}
action = getattr(self, "action", "create")
data["action"] = self._action_map.get(action)
try:
message = self.success_message % data
except:
message = ''
return message
def dispatch(self, request, *args, **kwargs):
resp = super().dispatch(request, *args, **kwargs)
if request.method.lower() in ("get", "delete", "patch"):
return resp
if resp.status_code >= 400:
return resp
message = self.get_success_message(resp.data)
if message:
messages.success(request, message)
return resp

View File

@ -0,0 +1,6 @@
# -*- coding: utf-8 -*-
#
from .models import *
from .serializers import *
from .api import *
from .views import *

84
apps/common/mixins/api.py Normal file
View File

@ -0,0 +1,84 @@
# -*- coding: utf-8 -*-
#
from django.http import JsonResponse
from django.core.cache import cache
from django.utils.translation import ugettext_lazy as _
from django.contrib import messages
from ..const import KEY_CACHE_RESOURCES_ID
__all__ = [
"JSONResponseMixin", "IDInCacheFilterMixin", "IDExportFilterMixin",
"IDInFilterMixin", "ApiMessageMixin"
]
class JSONResponseMixin(object):
"""JSON mixin"""
@staticmethod
def render_json_response(context):
return JsonResponse(context)
class IDInFilterMixin(object):
def filter_queryset(self, queryset):
queryset = super(IDInFilterMixin, self).filter_queryset(queryset)
id_list = self.request.query_params.get('id__in')
if id_list:
import json
try:
ids = json.loads(id_list)
except Exception as e:
return queryset
if isinstance(ids, list):
queryset = queryset.filter(id__in=ids)
return queryset
class IDInCacheFilterMixin(object):
def filter_queryset(self, queryset):
queryset = super(IDInCacheFilterMixin, self).filter_queryset(queryset)
spm = self.request.query_params.get('spm')
cache_key = KEY_CACHE_RESOURCES_ID.format(spm)
resources_id = cache.get(cache_key)
if resources_id and isinstance(resources_id, list):
queryset = queryset.filter(id__in=resources_id)
return queryset
class IDExportFilterMixin(object):
def filter_queryset(self, queryset):
# 下载导入模版
if self.request.query_params.get('template') == 'import':
return []
else:
return super(IDExportFilterMixin, self).filter_queryset(queryset)
class ApiMessageMixin:
success_message = _("%(name)s was %(action)s successfully")
_action_map = {"create": _("create"), "update": _("update")}
def get_success_message(self, cleaned_data):
if not isinstance(cleaned_data, dict):
return ''
data = {k: v for k, v in cleaned_data.items()}
action = getattr(self, "action", "create")
data["action"] = self._action_map.get(action)
try:
message = self.success_message % data
except:
message = ''
return message
def dispatch(self, request, *args, **kwargs):
resp = super().dispatch(request, *args, **kwargs)
if request.method.lower() in ("get", "delete", "patch"):
return resp
if resp.status_code >= 400:
return resp
message = self.get_success_message(resp.data)
if message:
messages.success(request, message)
return resp

View File

@ -0,0 +1,42 @@
# -*- coding: utf-8 -*-
#
from django.db import models
from django.utils import timezone
from django.utils.translation import ugettext_lazy as _
__all__ = ["NoDeleteManager", "NoDeleteModelMixin", "NoDeleteQuerySet"]
class NoDeleteQuerySet(models.query.QuerySet):
def delete(self):
return self.update(is_discard=True, discard_time=timezone.now())
class NoDeleteManager(models.Manager):
def get_all(self):
return NoDeleteQuerySet(self.model, using=self._db)
def get_queryset(self):
return NoDeleteQuerySet(self.model, using=self._db).filter(is_discard=False)
def get_deleted(self):
return NoDeleteQuerySet(self.model, using=self._db).filter(is_discard=True)
class NoDeleteModelMixin(models.Model):
is_discard = models.BooleanField(verbose_name=_("is discard"), default=False)
discard_time = models.DateTimeField(verbose_name=_("discard time"), null=True, blank=True)
objects = NoDeleteManager()
class Meta:
abstract = True
def delete(self):
self.is_discard = True
self.discard_time = timezone.now()
return self.save()

View File

@ -0,0 +1,95 @@
# -*- coding: utf-8 -*-
#
from rest_framework.utils import html
from rest_framework.settings import api_settings
from rest_framework.exceptions import ValidationError
from rest_framework.fields import SkipField
__all__ = ['BulkSerializerMixin', 'BulkListSerializerMixin']
class BulkSerializerMixin(object):
"""
Become rest_framework_bulk not support uuid as a primary key
so rewrite it. https://github.com/miki725/django-rest-framework-bulk/issues/66
"""
def to_internal_value(self, data):
from rest_framework_bulk import BulkListSerializer
ret = super(BulkSerializerMixin, self).to_internal_value(data)
id_attr = getattr(self.Meta, 'update_lookup_field', 'id')
if self.context.get('view'):
request_method = getattr(getattr(self.context.get('view'), 'request'), 'method', '')
# add update_lookup_field field back to validated data
# since super by default strips out read-only fields
# hence id will no longer be present in validated_data
if all((isinstance(self.root, BulkListSerializer),
id_attr,
request_method in ('PUT', 'PATCH'))):
id_field = self.fields[id_attr]
if data.get("id"):
id_value = id_field.to_internal_value(data.get("id"))
else:
id_value = id_field.to_internal_value(data.get("pk"))
ret[id_attr] = id_value
return ret
class BulkListSerializerMixin(object):
"""
Become rest_framework_bulk doing bulk update raise Exception:
'QuerySet' object has no attribute 'pk' when doing bulk update
so rewrite it .
https://github.com/miki725/django-rest-framework-bulk/issues/68
"""
def to_internal_value(self, data):
"""
List of dicts of native values <- List of dicts of primitive datatypes.
"""
if html.is_html_input(data):
data = html.parse_html_list(data)
if not isinstance(data, list):
message = self.error_messages['not_a_list'].format(
input_type=type(data).__name__
)
raise ValidationError({
api_settings.NON_FIELD_ERRORS_KEY: [message]
}, code='not_a_list')
if not self.allow_empty and len(data) == 0:
if self.parent and self.partial:
raise SkipField()
message = self.error_messages['empty']
raise ValidationError({
api_settings.NON_FIELD_ERRORS_KEY: [message]
}, code='empty')
ret = []
errors = []
for item in data:
try:
# prepare child serializer to only handle one instance
if 'id' in item.keys():
self.child.instance = self.instance.get(id=item['id']) if self.instance else None
if 'pk' in item.keys():
self.child.instance = self.instance.get(id=item['pk']) if self.instance else None
self.child.initial_data = item
# raw
validated = self.child.run_validation(item)
except ValidationError as exc:
errors.append(exc.detail)
else:
ret.append(validated)
errors.append({})
if any(errors):
raise ValidationError(errors)
return ret

View File

@ -0,0 +1,40 @@
# -*- coding: utf-8 -*-
#
# coding: utf-8
from django.utils import timezone
__all__ = ["DatetimeSearchMixin"]
class DatetimeSearchMixin:
date_format = '%Y-%m-%d'
date_from = date_to = None
def get_date_range(self):
date_from_s = self.request.GET.get('date_from')
date_to_s = self.request.GET.get('date_to')
if date_from_s:
date_from = timezone.datetime.strptime(date_from_s, self.date_format)
tz = timezone.get_current_timezone()
self.date_from = tz.localize(date_from)
else:
self.date_from = timezone.now() - timezone.timedelta(7)
if date_to_s:
date_to = timezone.datetime.strptime(
date_to_s + ' 23:59:59', self.date_format + ' %H:%M:%S'
)
self.date_to = date_to.replace(
tzinfo=timezone.get_current_timezone()
)
else:
self.date_to = timezone.now()
def get(self, request, *args, **kwargs):
self.get_date_range()
return super().get(request, *args, **kwargs)

View File

@ -301,10 +301,10 @@ LOGGING = {
'handlers': ['gunicorn_console', 'gunicorn_file'],
'level': 'INFO',
},
# 'django.db': {
# 'handlers': ['console', 'file'],
# 'level': 'DEBUG'
# }
'django.db': {
'handlers': ['console', 'file'],
'level': 'DEBUG'
}
}
}

View File

@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: Jumpserver 0.3.3\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2019-06-28 20:08+0800\n"
"POT-Creation-Date: 2019-07-02 14:47+0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: ibuler <ibuler@qq.com>\n"
"Language-Team: Jumpserver team<ibuler@qq.com>\n"
@ -86,7 +86,7 @@ msgstr "运行参数"
#: assets/templates/assets/system_user_list.html:55 audits/models.py:19
#: audits/templates/audits/ftp_log_list.html:41
#: audits/templates/audits/ftp_log_list.html:71
#: perms/forms/asset_permission.py:47 perms/models/asset_permission.py:53
#: perms/forms/asset_permission.py:68 perms/models/asset_permission.py:85
#: perms/templates/perms/asset_permission_create_update.html:45
#: perms/templates/perms/asset_permission_list.html:48
#: perms/templates/perms/asset_permission_list.html:117
@ -115,8 +115,8 @@ msgstr "资产"
#: assets/models/user.py:160 assets/templates/assets/user_asset_list.html:172
#: audits/models.py:20 audits/templates/audits/ftp_log_list.html:49
#: audits/templates/audits/ftp_log_list.html:72
#: perms/forms/asset_permission.py:53 perms/models/asset_permission.py:55
#: perms/models/asset_permission.py:76
#: perms/forms/asset_permission.py:74 perms/models/asset_permission.py:87
#: perms/models/asset_permission.py:104
#: perms/templates/perms/asset_permission_detail.html:140
#: perms/templates/perms/asset_permission_list.html:50
#: perms/templates/perms/asset_permission_list.html:71
@ -149,7 +149,7 @@ msgstr "系统用户"
#: assets/templates/assets/system_user_detail.html:58
#: assets/templates/assets/system_user_list.html:51 ops/models/adhoc.py:37
#: ops/templates/ops/task_detail.html:60 ops/templates/ops/task_list.html:27
#: orgs/models.py:11 perms/models/asset_permission.py:22
#: orgs/models.py:11 perms/models/asset_permission.py:23
#: perms/models/base.py:35
#: perms/templates/perms/asset_permission_detail.html:62
#: perms/templates/perms/asset_permission_list.html:45
@ -215,10 +215,10 @@ msgstr "参数"
#: assets/templates/assets/domain_detail.html:72
#: assets/templates/assets/system_user_detail.html:100
#: ops/templates/ops/adhoc_detail.html:86 orgs/models.py:14
#: perms/models/asset_permission.py:79 perms/models/base.py:41
#: perms/models/asset_permission.py:107 perms/models/base.py:41
#: perms/templates/perms/asset_permission_detail.html:98
#: perms/templates/perms/remote_app_permission_detail.html:90
#: users/models/user.py:105 users/serializers/v1.py:99
#: users/models/user.py:105 users/serializers/v1.py:107
#: users/templates/users/user_detail.html:111
#: xpack/plugins/change_auth_plan/models.py:106
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:113
@ -238,7 +238,7 @@ msgstr "创建者"
#: assets/templates/assets/domain_detail.html:68
#: assets/templates/assets/system_user_detail.html:96
#: ops/templates/ops/adhoc_detail.html:90 ops/templates/ops/task_detail.html:64
#: orgs/models.py:15 perms/models/asset_permission.py:80
#: orgs/models.py:15 perms/models/asset_permission.py:108
#: perms/models/base.py:42
#: perms/templates/perms/asset_permission_detail.html:94
#: perms/templates/perms/remote_app_permission_detail.html:86
@ -274,7 +274,7 @@ msgstr "创建日期"
#: assets/templates/assets/system_user_detail.html:104
#: assets/templates/assets/system_user_list.html:59
#: assets/templates/assets/user_asset_list.html:175 ops/models/adhoc.py:43
#: orgs/models.py:16 perms/models/asset_permission.py:81
#: orgs/models.py:16 perms/models/asset_permission.py:109
#: perms/models/base.py:43
#: perms/templates/perms/asset_permission_detail.html:102
#: perms/templates/perms/remote_app_permission_detail.html:94
@ -440,7 +440,6 @@ msgstr "详情"
#: users/templates/users/user_group_list.html:20
#: users/templates/users/user_group_list.html:70
#: users/templates/users/user_list.html:20
#: users/templates/users/user_list.html:96
#: users/templates/users/user_list.html:99
#: users/templates/users/user_profile.html:177
#: users/templates/users/user_profile.html:187
@ -481,7 +480,6 @@ msgstr "更新"
#: users/templates/users/user_detail.html:30
#: users/templates/users/user_group_detail.html:32
#: users/templates/users/user_group_list.html:72
#: users/templates/users/user_list.html:104
#: users/templates/users/user_list.html:108
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_detail.html:33
#: xpack/plugins/change_auth_plan/templates/change_auth_plan/plan_list.html:57
@ -529,8 +527,7 @@ msgstr "创建远程应用"
#: audits/templates/audits/operate_log_list.html:67
#: ops/templates/ops/adhoc_history.html:59 ops/templates/ops/task_adhoc.html:64
#: ops/templates/ops/task_history.html:65 ops/templates/ops/task_list.html:34
#: perms/forms/asset_permission.py:56 perms/models/asset_permission.py:26
#: perms/models/asset_permission.py:57
#: perms/forms/asset_permission.py:21 perms/models/asset_permission.py:27
#: perms/templates/perms/asset_permission_create_update.html:50
#: perms/templates/perms/asset_permission_list.html:52
#: perms/templates/perms/asset_permission_list.html:126
@ -552,7 +549,7 @@ msgstr "动作"
#: applications/templates/applications/user_remote_app_list.html:57
#: assets/templates/assets/user_asset_list.html:100 perms/const.py:19
#: perms/models/asset_permission.py:45
#: perms/models/asset_permission.py:46
msgid "Connect"
msgstr "连接"
@ -591,11 +588,11 @@ msgstr "请选择需要更新的资产"
msgid "You can't update the root node name"
msgstr "不能修改根节点名称"
#: assets/api/node.py:281
#: assets/api/node.py:283
msgid "Update node asset hardware information: {}"
msgstr "更新节点资产硬件信息: {}"
#: assets/api/node.py:295
#: assets/api/node.py:297
msgid "Test if the assets under the node are connectable: {}"
msgstr "测试节点下资产是否可连接: {}"
@ -622,7 +619,7 @@ msgstr "未知"
#: assets/templates/assets/asset_detail.html:194
#: assets/templates/assets/asset_detail.html:202
#: assets/templates/assets/system_user_assets.html:83
#: perms/models/asset_permission.py:54
#: perms/models/asset_permission.py:86
#: xpack/plugins/change_auth_plan/models.py:72
msgid "Nodes"
msgstr "节点"
@ -656,8 +653,8 @@ msgstr "网域"
#: assets/forms/asset.py:64 assets/forms/asset.py:86 assets/forms/asset.py:99
#: assets/forms/asset.py:134 assets/models/node.py:248
#: assets/templates/assets/asset_create.html:42
#: perms/forms/asset_permission.py:50 perms/forms/asset_permission.py:60
#: perms/models/asset_permission.py:74
#: perms/forms/asset_permission.py:71 perms/forms/asset_permission.py:79
#: perms/models/asset_permission.py:102
#: perms/templates/perms/asset_permission_list.html:49
#: perms/templates/perms/asset_permission_list.html:70
#: perms/templates/perms/asset_permission_list.html:120
@ -695,6 +692,10 @@ msgstr "如果有多个的互相隔离的网络,设置资产属于的网域,
msgid "Select assets"
msgstr "选择资产"
#: assets/forms/cmd_filter.py:37 assets/serializers/cmd_filter.py:34
msgid "Content should not be contain: {}"
msgstr "内容不能包含: {}"
#: assets/forms/domain.py:51
msgid "Password should not contain special characters"
msgstr "不能包含特殊字符"
@ -1000,7 +1001,7 @@ msgid "Operator"
msgstr "运营商"
#: assets/models/cluster.py:36 assets/models/group.py:34
#: perms/utils/asset_permission.py:67
#: perms/utils/asset_permission.py:106
msgid "Default"
msgstr "默认"
@ -1112,7 +1113,7 @@ msgstr "默认资产组"
#: audits/templates/audits/password_change_log_list.html:50
#: ops/templates/ops/command_execution_list.html:35
#: ops/templates/ops/command_execution_list.html:60
#: perms/forms/asset_permission.py:41 perms/forms/remote_app_permission.py:31
#: perms/forms/asset_permission.py:62 perms/forms/remote_app_permission.py:31
#: perms/models/base.py:36
#: perms/templates/perms/asset_permission_create_update.html:41
#: perms/templates/perms/asset_permission_list.html:46
@ -1124,9 +1125,9 @@ msgstr "默认资产组"
#: terminal/templates/terminal/command_list.html:72
#: terminal/templates/terminal/session_list.html:33
#: terminal/templates/terminal/session_list.html:71 users/forms.py:301
#: users/models/user.py:38 users/models/user.py:431 users/serializers/v1.py:88
#: users/models/user.py:38 users/models/user.py:431 users/serializers/v1.py:96
#: users/templates/users/user_group_detail.html:78
#: users/templates/users/user_group_list.html:36 users/views/user.py:407
#: users/templates/users/user_group_list.html:36 users/views/user.py:264
#: xpack/plugins/orgs/forms.py:26
#: xpack/plugins/orgs/templates/orgs/org_detail.html:113
#: xpack/plugins/orgs/templates/orgs/org_list.html:14
@ -1205,26 +1206,25 @@ msgstr "登录模式"
msgid "%(value)s is not an even number"
msgstr "%(value)s is not an even number"
#: assets/serializers/admin_user.py:36 assets/serializers/asset.py:47
#: assets/serializers/admin_user.py:36 assets/serializers/asset.py:46
#: assets/serializers/asset_user.py:30 assets/serializers/system_user.py:30
#: assets/templates/assets/_asset_user_list.html:18
msgid "Connectivity"
msgstr "连接"
#: assets/serializers/asset.py:45 assets/serializers/asset.py:155
#: assets/templates/assets/asset_create.html:24
#: assets/serializers/asset.py:44 assets/templates/assets/asset_create.html:24
msgid "Protocols"
msgstr "协议组"
#: assets/serializers/asset.py:73
#: assets/serializers/asset.py:72
msgid "Hardware info"
msgstr "硬件信息"
#: assets/serializers/asset.py:74 orgs/mixins.py:192
#: assets/serializers/asset.py:73 orgs/mixins.py:192
msgid "Org name"
msgstr "组织名称"
#: assets/serializers/asset.py:92
#: assets/serializers/asset.py:91
msgid "Protocol duplicate: {}"
msgstr "协议重复: {}"
@ -1710,7 +1710,7 @@ msgstr "创建日期"
#: assets/templates/assets/asset_detail.html:154
#: assets/templates/assets/user_asset_list.html:46
#: perms/models/asset_permission.py:77 perms/models/base.py:38
#: perms/models/asset_permission.py:105 perms/models/base.py:38
#: perms/templates/perms/asset_permission_create_update.html:55
#: perms/templates/perms/asset_permission_detail.html:120
#: perms/templates/perms/remote_app_permission_create_update.html:54
@ -2056,10 +2056,6 @@ msgstr "批量更新资产"
msgid "Update asset"
msgstr "更新资产"
#: assets/views/asset.py:347
msgid "already exists"
msgstr "已经存在"
#: assets/views/cmd_filter.py:32
msgid "Command filter list"
msgstr "命令过滤器列表"
@ -2557,8 +2553,8 @@ msgstr "欢迎回来,请输入用户名和密码登录"
msgid "Please enable cookies and try again."
msgstr "设置你的浏览器支持cookie"
#: authentication/views/login.py:172 users/views/user.py:555
#: users/views/user.py:580
#: authentication/views/login.py:172 users/views/user.py:412
#: users/views/user.py:437
msgid "MFA code invalid, or ntp sync server time"
msgstr "MFA验证码不正确或者服务器端时间不对"
@ -3000,11 +2996,11 @@ msgstr "命令执行"
msgid "Organization"
msgstr "组织"
#: perms/const.py:18 perms/models/asset_permission.py:44 settings/forms.py:143
#: perms/const.py:18 perms/models/asset_permission.py:45 settings/forms.py:143
msgid "All"
msgstr "全部"
#: perms/const.py:20 perms/models/asset_permission.py:46
#: perms/const.py:20 perms/models/asset_permission.py:47
msgid "Upload file"
msgstr "上传文件"
@ -3012,8 +3008,8 @@ msgstr "上传文件"
msgid "Download file"
msgstr "下载文件"
#: perms/forms/asset_permission.py:44 perms/forms/remote_app_permission.py:34
#: perms/models/asset_permission.py:75 perms/models/base.py:37
#: perms/forms/asset_permission.py:65 perms/forms/remote_app_permission.py:34
#: perms/models/asset_permission.py:103 perms/models/base.py:37
#: perms/templates/perms/asset_permission_list.html:47
#: perms/templates/perms/asset_permission_list.html:67
#: perms/templates/perms/asset_permission_list.html:114
@ -3026,30 +3022,34 @@ msgstr "下载文件"
msgid "User group"
msgstr "用户组"
#: perms/forms/asset_permission.py:63
#: perms/forms/asset_permission.py:82
msgid ""
"Tips: The RDP protocol does not support separate controls for uploading or "
"downloading files"
msgstr "提示RDP 协议不支持单独控制上传或下载文件"
#: perms/forms/asset_permission.py:73 perms/forms/remote_app_permission.py:47
#: perms/forms/asset_permission.py:92 perms/forms/remote_app_permission.py:47
msgid "User or group at least one required"
msgstr "用户和用户组至少选一个"
#: perms/forms/asset_permission.py:82
#: perms/forms/asset_permission.py:101
msgid "Asset or group at least one required"
msgstr "资产和节点至少选一个"
#: perms/models/asset_permission.py:47
#: perms/models/asset_permission.py:49
msgid "Upload download"
msgstr "上传下载"
#: perms/models/asset_permission.py:61 perms/models/asset_permission.py:87
#: perms/models/asset_permission.py:89
msgid "Actions"
msgstr "动作"
#: perms/models/asset_permission.py:93 perms/models/asset_permission.py:115
#: templates/_nav.html:44
msgid "Asset permission"
msgstr "资产授权"
#: perms/models/asset_permission.py:78 perms/models/base.py:40
#: perms/models/asset_permission.py:106 perms/models/base.py:40
#: perms/templates/perms/asset_permission_detail.html:90
#: perms/templates/perms/remote_app_permission_detail.html:82
#: users/models/user.py:102 users/templates/users/user_detail.html:107
@ -3199,8 +3199,8 @@ msgid "Add user group to this permission"
msgstr "添加用户组"
#: perms/views/asset_permission.py:34 perms/views/asset_permission.py:65
#: perms/views/asset_permission.py:81 perms/views/asset_permission.py:97
#: perms/views/asset_permission.py:134 perms/views/asset_permission.py:167
#: perms/views/asset_permission.py:82 perms/views/asset_permission.py:99
#: perms/views/asset_permission.py:136 perms/views/asset_permission.py:169
#: perms/views/remote_app_permission.py:33
#: perms/views/remote_app_permission.py:49
#: perms/views/remote_app_permission.py:65
@ -3219,19 +3219,19 @@ msgstr "资产授权列表"
msgid "Create asset permission"
msgstr "创建权限规则"
#: perms/views/asset_permission.py:82
#: perms/views/asset_permission.py:83
msgid "Update asset permission"
msgstr "更新资产授权"
#: perms/views/asset_permission.py:98
#: perms/views/asset_permission.py:100
msgid "Asset permission detail"
msgstr "资产授权详情"
#: perms/views/asset_permission.py:135
#: perms/views/asset_permission.py:137
msgid "Asset permission user list"
msgstr "资产授权用户列表"
#: perms/views/asset_permission.py:168
#: perms/views/asset_permission.py:170
msgid "Asset permission asset list"
msgstr "资产授权资产列表"
@ -3828,7 +3828,7 @@ msgstr "商业支持"
#: users/templates/users/user_profile.html:17
#: users/templates/users/user_profile_update.html:37
#: users/templates/users/user_profile_update.html:57
#: users/templates/users/user_pubkey_update.html:37 users/views/user.py:388
#: users/templates/users/user_pubkey_update.html:37 users/views/user.py:245
msgid "Profile"
msgstr "个人信息"
@ -3923,13 +3923,13 @@ msgstr ""
#: templates/_nav.html:10 users/views/group.py:28 users/views/group.py:45
#: users/views/group.py:62 users/views/group.py:79 users/views/group.py:96
#: users/views/login.py:154 users/views/user.py:70 users/views/user.py:87
#: users/views/user.py:131 users/views/user.py:211 users/views/user.py:374
#: users/views/user.py:426 users/views/user.py:467
#: users/views/login.py:154 users/views/user.py:68 users/views/user.py:85
#: users/views/user.py:129 users/views/user.py:209 users/views/user.py:231
#: users/views/user.py:283 users/views/user.py:324
msgid "Users"
msgstr "用户管理"
#: templates/_nav.html:13 users/views/user.py:71
#: templates/_nav.html:13 users/views/user.py:69
msgid "User list"
msgstr "用户列表"
@ -4369,11 +4369,11 @@ msgid ""
"You should use your ssh client tools connect terminal: {} <br /> <br />{}"
msgstr "你可以使用ssh客户端工具连接终端"
#: users/api/user.py:79 users/api/user.py:90 users/api/user.py:116
#: users/api/user.py:93
msgid "You do not have permission."
msgstr "你没有权限"
#: users/api/user.py:221
#: users/api/user.py:186
msgid "Could not reset self otp, use profile reset instead"
msgstr "不能再该页面重置MFA, 请去个人信息页面重置"
@ -4405,7 +4405,7 @@ msgstr "添加到用户组"
msgid "Public key should not be the same as your old one."
msgstr "不能和原来的密钥相同"
#: users/forms.py:90 users/forms.py:237 users/serializers/v1.py:74
#: users/forms.py:90 users/forms.py:237 users/serializers/v1.py:82
msgid "Not a valid ssh public key"
msgstr "ssh密钥不合法"
@ -4532,7 +4532,7 @@ msgid "Date password last updated"
msgstr "最后更新密码日期"
#: users/models/user.py:139 users/templates/users/user_update.html:22
#: users/views/login.py:46 users/views/login.py:107 users/views/user.py:439
#: users/views/login.py:46 users/views/login.py:107 users/views/user.py:296
msgid "User auth from {}, go there change password"
msgstr "用户认证源来自 {}, 请去相应系统修改密码"
@ -4569,10 +4569,12 @@ msgid "Avatar url"
msgstr "头像路径"
#: users/serializers/v1.py:46
#, fuzzy
#| msgid "Password does not match"
msgid "Role limit to {}"
msgstr "角色只能为 {}"
#: users/serializers/v1.py:54
msgid "Password does not match security rules"
msgstr "密码不一致"
msgstr "密码不满足安全规则"
#: users/serializers_v2/user.py:36
msgid "name not unique"
@ -4624,7 +4626,7 @@ msgid "Import users"
msgstr "导入用户"
#: users/templates/users/_user_update_modal.html:4
#: users/templates/users/user_update.html:4 users/views/user.py:132
#: users/templates/users/user_update.html:4 users/views/user.py:130
msgid "Update user"
msgstr "更新用户"
@ -4762,12 +4764,12 @@ msgid "Very strong"
msgstr "很强"
#: users/templates/users/user_create.html:4
#: users/templates/users/user_list.html:28 users/views/user.py:88
#: users/templates/users/user_list.html:28 users/views/user.py:86
msgid "Create user"
msgstr "创建用户"
#: users/templates/users/user_detail.html:19
#: users/templates/users/user_granted_asset.html:18 users/views/user.py:212
#: users/templates/users/user_granted_asset.html:18 users/views/user.py:210
msgid "User detail"
msgstr "用户详情"
@ -4967,8 +4969,7 @@ msgstr "安装完成后点击下一步进入绑定页面(如已安装,直接
msgid "Administrator Settings force MFA login"
msgstr "管理员设置强制使用MFA登录"
#: users/templates/users/user_profile.html:120 users/views/user.py:248
#: users/views/user.py:303
#: users/templates/users/user_profile.html:120
msgid "User groups"
msgstr "用户组"
@ -5243,7 +5244,7 @@ msgstr "Token错误或失效"
msgid "Password not same"
msgstr "密码不一致"
#: users/views/login.py:114 users/views/user.py:146 users/views/user.py:449
#: users/views/login.py:114 users/views/user.py:144 users/views/user.py:306
msgid "* Your password does not meet the requirements"
msgstr "* 您的密码不符合要求"
@ -5251,51 +5252,47 @@ msgstr "* 您的密码不符合要求"
msgid "First login"
msgstr "首次登录"
#: users/views/user.py:163
#: users/views/user.py:161
msgid "Bulk update user success"
msgstr "批量更新用户成功"
#: users/views/user.py:191
#: users/views/user.py:189
msgid "Bulk update user"
msgstr "批量更新用户"
#: users/views/user.py:278
msgid "Invalid file."
msgstr "文件不合法"
#: users/views/user.py:375
#: users/views/user.py:232
msgid "User granted assets"
msgstr "用户授权资产"
#: users/views/user.py:408
#: users/views/user.py:265
msgid "Profile setting"
msgstr "个人信息设置"
#: users/views/user.py:427
#: users/views/user.py:284
msgid "Password update"
msgstr "密码更新"
#: users/views/user.py:468
#: users/views/user.py:325
msgid "Public key update"
msgstr "密钥更新"
#: users/views/user.py:510
#: users/views/user.py:367
msgid "Password invalid"
msgstr "用户名或密码无效"
#: users/views/user.py:610
#: users/views/user.py:467
msgid "MFA enable success"
msgstr "MFA 绑定成功"
#: users/views/user.py:611
#: users/views/user.py:468
msgid "MFA enable success, return login page"
msgstr "MFA 绑定成功,返回到登录页面"
#: users/views/user.py:613
#: users/views/user.py:470
msgid "MFA disable success"
msgstr "MFA 解绑成功"
#: users/views/user.py:614
#: users/views/user.py:471
msgid "MFA disable success, return login page"
msgstr "MFA 解绑成功,返回登录页面"
@ -5783,10 +5780,8 @@ msgid "Restore default failed."
msgstr "恢复默认失败!"
#: xpack/plugins/interface/views.py:24
#, fuzzy
#| msgid "Interval"
msgid "Interface"
msgstr "间隔"
msgstr "界面"
#: xpack/plugins/interface/views.py:51
msgid "It is already in the default setting state!"
@ -5946,6 +5941,12 @@ msgstr "密码匣子"
msgid "vault create"
msgstr "创建"
#~ msgid "already exists"
#~ msgstr "已经存在"
#~ msgid "Invalid file."
#~ msgstr "文件不合法"
#~ msgid "Refresh all node assets amount"
#~ msgstr "刷新所有节点资产数量"

View File

@ -10,7 +10,7 @@ from rest_framework.pagination import LimitOffsetPagination
from common.permissions import IsOrgAdmin
from common.utils import get_object_or_none
from ..models import AssetPermission, Action
from ..models import AssetPermission
from ..hands import (
User, UserGroup, Asset, Node, SystemUser,
)

View File

@ -21,7 +21,7 @@ from ..utils import (
from ..hands import User, Asset, Node, SystemUser, NodeSerializer
from .. import serializers, const
from ..mixins import AssetsFilterMixin
from ..models import ActionFlag
from ..models import Action
logger = get_logger(__name__)
@ -423,7 +423,7 @@ class ValidateUserAssetPermissionApi(UserPermissionCacheMixin, APIView):
return Response({'msg': False}, status=403)
action = granted_system_users[su]
choices = ActionFlag.value_to_choices(action)
choices = Action.value_to_choices(action)
if action_name not in choices:
return Response({'msg': False}, status=403)

View File

@ -1,24 +1,4 @@
# -*- coding: utf-8 -*-
#
from django.utils.translation import ugettext_lazy as _
__all__ = [
'PERMS_ACTION_NAME_ALL', 'PERMS_ACTION_NAME_CONNECT',
'PERMS_ACTION_NAME_DOWNLOAD_FILE', 'PERMS_ACTION_NAME_UPLOAD_FILE',
'PERMS_ACTION_NAME_CHOICES'
]
PERMS_ACTION_NAME_ALL = 'all'
PERMS_ACTION_NAME_CONNECT = 'connect'
PERMS_ACTION_NAME_UPLOAD_FILE = 'upload_file'
PERMS_ACTION_NAME_DOWNLOAD_FILE = 'download_file'
PERMS_ACTION_NAME_CHOICES = (
(PERMS_ACTION_NAME_ALL, _('All')),
(PERMS_ACTION_NAME_CONNECT, _('Connect')),
(PERMS_ACTION_NAME_UPLOAD_FILE, _('Upload file')),
(PERMS_ACTION_NAME_DOWNLOAD_FILE, _('Download file')),
)
UNGROUPED_NODE_ID = "00000000-0000-0000-0000-000000000000"
UNGROUPED_NODE_ID = "00000000-0000-0000-0000-000000000002"

View File

@ -7,7 +7,7 @@ from django.utils.translation import ugettext_lazy as _
from orgs.mixins import OrgModelForm
from orgs.utils import current_org
from assets.models import Asset, Node
from ..models import AssetPermission, ActionFlag
from ..models import AssetPermission, Action
__all__ = [
'AssetPermissionForm',
@ -16,20 +16,20 @@ __all__ = [
class ActionField(forms.MultipleChoiceField):
def __init__(self, *args, **kwargs):
kwargs['choices'] = ActionFlag.CHOICES
kwargs['initial'] = ActionFlag.ALL
kwargs['choices'] = Action.CHOICES
kwargs['initial'] = Action.ALL
kwargs['label'] = _("Action")
kwargs['widget'] = forms.CheckboxSelectMultiple()
super().__init__(*args, **kwargs)
def to_python(self, value):
value = super().to_python(value)
return ActionFlag.choices_to_value(value)
return Action.choices_to_value(value)
def prepare_value(self, value):
if value is None:
return value
value = ActionFlag.value_to_choices(value)
value = Action.value_to_choices(value)
return value

View File

@ -4,14 +4,6 @@ from django.db import migrations, models
import uuid
def add_default_actions(apps, schema_editor):
from ..const import PERMS_ACTION_NAME_CHOICES
action_model = apps.get_model('perms', 'Action')
db_alias = schema_editor.connection.alias
for action, _ in PERMS_ACTION_NAME_CHOICES:
action_model.objects.using(db_alias).update_or_create(name=action)
class Migration(migrations.Migration):
dependencies = [
@ -29,5 +21,4 @@ class Migration(migrations.Migration):
'verbose_name': 'Action',
},
),
migrations.RunPython(add_default_actions)
]

View File

@ -3,18 +3,6 @@
from django.db import migrations, models
def set_default_action_to_existing_perms(apps, schema_editor):
from orgs.utils import set_to_root_org
from ..models import Action
set_to_root_org()
perm_model = apps.get_model('perms', 'AssetPermission')
db_alias = schema_editor.connection.alias
perms = perm_model.objects.using(db_alias).all()
default_action = Action.get_action_all()
for perm in perms:
perm.actions.add(default_action.id)
class Migration(migrations.Migration):
dependencies = [
@ -27,5 +15,4 @@ class Migration(migrations.Migration):
name='actions',
field=models.ManyToManyField(blank=True, related_name='permissions', to='perms.Action', verbose_name='Action'),
),
migrations.RunPython(set_default_action_to_existing_perms)
]

View File

@ -6,21 +6,22 @@ from functools import reduce
def migrate_old_actions(apps, schema_editor):
from orgs.utils import set_to_root_org
from ..models import ActionFlag
set_to_root_org()
perm_model = apps.get_model('perms', 'AssetPermission')
db_alias = schema_editor.connection.alias
perms = perm_model.objects.using(db_alias).all()
actions_map = {
"all": ActionFlag.ALL,
"connect": ActionFlag.CONNECT,
"upload_file": ActionFlag.UPLOAD,
"download_file": ActionFlag.DOWNLOAD,
"all": 0b11111111,
"connect": 0b00000001,
"upload_file": 0b00000010,
"download_file": 0b00000100,
}
for perm in perms:
actions = perm.actions.all()
new_actions = [actions_map.get(action.name, ActionFlag.ALL) for action in actions]
if not actions:
continue
new_actions = [actions_map.get(action.name, 0b11111111) for action in actions]
new_action = reduce(lambda x, y: x | y, new_actions)
perm.action = new_action
perm.save()

View File

@ -19,4 +19,5 @@ class Migration(migrations.Migration):
old_name='action',
new_name='actions',
),
migrations.DeleteModel(name='Action'),
]

View File

@ -4,37 +4,18 @@ from functools import reduce
from django.db import models
from django.utils.translation import ugettext_lazy as _
from common.utils import date_expired_default, set_or_append_attr_bulk
from common.utils import date_expired_default
from orgs.mixins import OrgModelMixin
from ..const import PERMS_ACTION_NAME_CHOICES, PERMS_ACTION_NAME_ALL
from .base import BasePermission
__all__ = [
'Action', 'AssetPermission', 'NodePermission', 'ActionFlag'
'AssetPermission', 'NodePermission', 'Action',
]
class Action(models.Model):
id = models.UUIDField(default=uuid.uuid4, primary_key=True)
name = models.CharField(
max_length=128, unique=True, choices=PERMS_ACTION_NAME_CHOICES,
verbose_name=_('Name')
)
class Meta:
verbose_name = _('Action')
def __str__(self):
return self.get_name_display()
@classmethod
def get_action_all(cls):
return cls.objects.get(name=PERMS_ACTION_NAME_ALL)
class ActionFlag:
class Action:
CONNECT = 0b00000001
UPLOAD = 0b00000010
DOWNLOAD = 0b00000100
@ -74,6 +55,8 @@ class ActionFlag:
x = cls.NAME_MAP_REVERSE.get(x, 0)
y = cls.NAME_MAP_REVERSE.get(y, 0)
return x | y
if not value:
return None
return reduce(to_choices, value)
@classmethod
@ -86,7 +69,7 @@ class AssetPermission(BasePermission):
nodes = models.ManyToManyField('assets.Node', related_name='granted_by_permissions', blank=True, verbose_name=_("Nodes"))
system_users = models.ManyToManyField('assets.SystemUser', related_name='granted_by_permissions', verbose_name=_("System user"))
# actions = models.ManyToManyField(Action, related_name='permissions', blank=True, verbose_name=_('Action'))
actions = models.IntegerField(choices=ActionFlag.DB_CHOICES, default=ActionFlag.ALL, verbose_name=_("Actions"))
actions = models.IntegerField(choices=Action.DB_CHOICES, default=Action.ALL, verbose_name=_("Actions"))
class Meta:
unique_together = [('org_id', 'name')]

View File

@ -5,36 +5,38 @@ from rest_framework import serializers
from common.fields import StringManyToManyField
from orgs.mixins import BulkOrgResourceModelSerializer
from perms.models import AssetPermission, ActionFlag
from perms.models import AssetPermission, Action
__all__ = [
'AssetPermissionCreateUpdateSerializer', 'AssetPermissionListSerializer',
'AssetPermissionUpdateUserSerializer', 'AssetPermissionUpdateAssetSerializer',
'ActionField',
'ActionsField',
]
class ActionField(serializers.MultipleChoiceField):
class ActionsField(serializers.MultipleChoiceField):
def __init__(self, *args, **kwargs):
kwargs['choices'] = ActionFlag.CHOICES
kwargs['choices'] = Action.CHOICES
super().__init__(*args, **kwargs)
def to_representation(self, value):
return ActionFlag.value_to_choices(value)
return Action.value_to_choices(value)
def to_internal_value(self, data):
return ActionFlag.choices_to_value(data)
if data is None:
return data
return Action.choices_to_value(data)
class ActionDisplayField(ActionField):
class ActionsDisplayField(ActionsField):
def to_representation(self, value):
values = super().to_representation(value)
choices = dict(ActionFlag.CHOICES)
choices = dict(Action.CHOICES)
return [choices.get(i) for i in values]
class AssetPermissionCreateUpdateSerializer(BulkOrgResourceModelSerializer):
actions = ActionField()
actions = ActionsField(required=False, allow_null=True)
class Meta:
model = AssetPermission
@ -47,7 +49,7 @@ class AssetPermissionListSerializer(BulkOrgResourceModelSerializer):
assets = StringManyToManyField(many=True, read_only=True)
nodes = StringManyToManyField(many=True, read_only=True)
system_users = StringManyToManyField(many=True, read_only=True)
actions = ActionDisplayField()
actions = ActionsDisplayField()
is_valid = serializers.BooleanField()
is_expired = serializers.BooleanField()

View File

@ -6,7 +6,7 @@ from rest_framework import serializers
from assets.models import Node, SystemUser
from assets.serializers import AssetSerializer
from .asset_permission import ActionField
from .asset_permission import ActionsField
__all__ = [
'AssetPermissionNodeSerializer', 'GrantedNodeSerializer',
@ -19,7 +19,7 @@ class AssetSystemUserSerializer(serializers.ModelSerializer):
"""
查看授权的资产系统用户的数据结构这个和AssetSerializer不同字段少
"""
actions = ActionField(read_only=True)
actions = ActionsField(read_only=True)
class Meta:
model = SystemUser
@ -115,4 +115,4 @@ class GrantedNodeSerializer(serializers.ModelSerializer):
class ActionsSerializer(serializers.Serializer):
actions = ActionField(read_only=True)
actions = ActionsField(read_only=True)

View File

@ -6,7 +6,7 @@ from django.db import transaction
from common.utils import get_logger
from .utils import AssetPermissionUtil
from .models import AssetPermission, Action
from .models import AssetPermission
logger = get_logger(__file__)

View File

@ -17,7 +17,7 @@ from orgs.utils import set_to_root_org
from common.utils import get_logger
from common.tree import TreeNode
from .. import const
from ..models import AssetPermission, Action, ActionFlag
from ..models import AssetPermission, Action
from ..hands import Node, Asset
from assets.utils import NodeUtil
@ -569,7 +569,7 @@ def parse_asset_to_tree_node(node, asset, system_users):
'protocol': system_user.protocol,
'priority': system_user.priority,
'login_mode': system_user.login_mode,
'actions': [ActionFlag.value_to_choices(action)],
'actions': [Action.value_to_choices(action)],
})
data = {
'id': str(asset.id),

View File

@ -10,10 +10,9 @@ from django.conf import settings
from common.permissions import PermissionsMixin, IsOrgAdmin
from orgs.utils import current_org
from perms.hands import Node, Asset, SystemUser, User, UserGroup
from perms.models import AssetPermission, Action
from perms.hands import Node, Asset, SystemUser, UserGroup
from perms.models import AssetPermission
from perms.forms import AssetPermissionForm
from perms.const import PERMS_ACTION_NAME_ALL
__all__ = [

View File

@ -77,6 +77,7 @@ def monkey_patch_settings(sender, **kwargs):
@receiver(django_ready)
def auto_generate_terminal_host_key(sender, **kwargs):
try:
print("Auto gen host key")
if Setting.objects.filter(name='TERMINAL_HOST_KEY').exists():
return
private_key, public_key = ssh_key_gen()