U 安全性改进

pull/410/head
vapao 2021-11-24 18:44:08 +08:00
parent 282f785928
commit 2b33df0765
23 changed files with 158 additions and 140 deletions

View File

@ -3,11 +3,12 @@
# Released under the AGPL-3.0 License.
from django.views.generic import View
from django.db.models import F
from libs import json_response
from libs import json_response, auth
from apps.account.models import History
class HistoryView(View):
@auth('dashboard.dashboard.view')
def get(self, request):
histories = []
for item in History.objects.annotate(nickname=F('user__nickname')):

View File

@ -2,6 +2,7 @@
# Copyright: (c) <spug.dev@gmail.com>
# Released under the AGPL-3.0 License.
from django.db import models
from django.core.cache import cache
from libs import ModelMixin, human_datetime
from django.contrib.auth.hashers import make_password, check_password
import json
@ -33,16 +34,25 @@ class User(models.Model, ModelMixin):
def verify_password(self, plain_password: str) -> bool:
return check_password(plain_password, self.password_hash)
def get_perms_cache(self):
return cache.get(f'perms_{self.id}', set())
def set_perms_cache(self, value=None):
cache.set(f'perms_{self.id}', value or set())
@property
def page_perms(self):
data = set()
data = self.get_perms_cache()
if data:
return data
for item in self.roles.all():
if item.page_perms:
perms = json.loads(item.page_perms)
for m, v in perms.items():
for p, d in v.items():
data.update(f'{m}.{p}.{x}' for x in d)
return list(data)
self.set_perms_cache(data)
return data
@property
def deploy_perms(self):
@ -63,8 +73,9 @@ class User(models.Model, ModelMixin):
return list(data)
def has_perms(self, codes):
# return self.is_supper or self.role in codes
return self.is_supper
if self.is_supper:
return True
return self.page_perms.intersection(codes)
def __repr__(self):
return '<User %r>' % self.username
@ -99,6 +110,10 @@ class Role(models.Model, ModelMixin):
self.deploy_perms = json.dumps(perms)
self.save()
def clear_perms_cache(self):
for item in self.user_set.all():
item.set_perms_cache()
def __repr__(self):
return '<Role name=%r>' % self.name

View File

@ -2,7 +2,7 @@
# Copyright: (c) <spug.dev@gmail.com>
# Released under the AGPL-3.0 License.
from django.core.cache import cache
from django.views.generic import View
from libs.mixins import AdminView, View
from libs import JsonParser, Argument, human_datetime, json_response
from libs.utils import get_request_real_ip, generate_random_str
from libs.spug import send_login_wx_code
@ -15,7 +15,7 @@ import uuid
import json
class UserView(View):
class UserView(AdminView):
def get(self, request):
users = []
for u in User.objects.filter(deleted_by_id__isnull=True):
@ -50,6 +50,7 @@ class UserView(View):
**form
)
user.roles.set(role_ids)
user.set_perms_cache()
return json_response(error=error)
def patch(self, request):
@ -88,7 +89,7 @@ class UserView(View):
return json_response(error=error)
class RoleView(View):
class RoleView(AdminView):
def get(self, request):
roles = Role.objects.all()
return json_response(roles)
@ -119,6 +120,7 @@ class RoleView(View):
return json_response(error='未找到指定角色')
if form.page_perms is not None:
role.page_perms = json.dumps(form.page_perms)
role.clear_perms_cache()
if form.deploy_perms is not None:
role.deploy_perms = json.dumps(form.deploy_perms)
if form.group_perms is not None:
@ -240,7 +242,7 @@ def handle_user_info(request, user, captcha):
'nickname': user.nickname,
'is_supper': user.is_supper,
'has_real_ip': x_real_ip and ipaddress.ip_address(x_real_ip).is_global if verify_ip else True,
'permissions': [] if user.is_supper else user.page_perms
'permissions': [] if user.is_supper else list(user.page_perms)
})

View File

@ -2,23 +2,26 @@
# Copyright: (c) <spug.dev@gmail.com>
# Released under the AGPL-3.0 License.
from django.views.generic import View
from libs import json_response, JsonParser, Argument
from libs import json_response, JsonParser, Argument, auth
from apps.alarm.models import Alarm, Group, Contact
from apps.monitor.models import Detection
import json
class AlarmView(View):
@auth('alarm.alarm.view')
def get(self, request):
alarms = Alarm.objects.all()
return json_response(alarms)
class GroupView(View):
@auth('alarm.group.view|monitor.monitor.add|monitor.monitor.edit|alarm.alarm.view')
def get(self, request):
groups = Group.objects.all()
return json_response(groups)
@auth('alarm.group.add|alarm.group.edit')
def post(self, request):
form, error = JsonParser(
Argument('id', type=int, required=False),
@ -35,6 +38,7 @@ class GroupView(View):
Group.objects.create(**form)
return json_response(error=error)
@auth('alarm.group.del')
def delete(self, request):
form, error = JsonParser(
Argument('id', type=int, help='请指定操作对象')
@ -48,10 +52,12 @@ class GroupView(View):
class ContactView(View):
@auth('alarm.contact.view|alarm.group.view')
def get(self, request):
contacts = Contact.objects.all()
return json_response(contacts)
@auth('alarm.contact.add|alarm.contact.edit')
def post(self, request):
form, error = JsonParser(
Argument('id', type=int, required=False),
@ -70,6 +76,7 @@ class ContactView(View):
Contact.objects.create(**form)
return json_response(error=error)
@auth('alarm.contact.del')
def delete(self, request):
form, error = JsonParser(
Argument('id', type=int, help='请指定操作对象')

View File

@ -3,7 +3,7 @@
# Released under the AGPL-3.0 License.
from django.views.generic import View
from django.db.models import F
from libs import JsonParser, Argument, json_response
from libs import JsonParser, Argument, json_response, auth
from apps.app.models import App, Deploy, DeployExtend1, DeployExtend2
from apps.config.models import Config
from apps.app.utils import fetch_versions, remove_repo
@ -13,6 +13,7 @@ import re
class AppView(View):
@auth('deploy.app.view|deploy.repository.view|deploy.request.view|config.app.view')
def get(self, request):
query = {}
if not request.user.is_supper:
@ -20,6 +21,7 @@ class AppView(View):
apps = App.objects.filter(**query)
return json_response(apps)
@auth('deploy.app.edit|config.app.add|config.app.edit')
def post(self, request):
form, error = JsonParser(
Argument('id', type=int, required=False),
@ -42,6 +44,7 @@ class AppView(View):
app.save()
return json_response(error=error)
@auth('deploy.app.edit|config.app.edit_config')
def patch(self, request):
form, error = JsonParser(
Argument('id', type=int, help='参数错误'),
@ -68,6 +71,7 @@ class AppView(View):
app.save()
return json_response(error=error)
@auth('deploy.app.del|config.app.del')
def delete(self, request):
form, error = JsonParser(
Argument('id', type=int, help='请指定操作对象')
@ -82,6 +86,7 @@ class AppView(View):
class DeployView(View):
@auth('deploy.app.view|deploy.request.view')
def get(self, request):
form, error = JsonParser(
Argument('app_id', type=int, required=False)
@ -95,6 +100,7 @@ class DeployView(View):
.order_by('-app__sort_id')
return json_response(deploys)
@auth('deploy.app.edit')
def post(self, request):
form, error = JsonParser(
Argument('id', type=int, required=False),
@ -157,6 +163,7 @@ class DeployView(View):
DeployExtend2.objects.create(deploy=deploy, **extend_form)
return json_response(error=error)
@auth('deploy.app.del')
def delete(self, request):
form, error = JsonParser(
Argument('id', type=int, help='请指定操作对象')
@ -171,6 +178,7 @@ class DeployView(View):
return json_response(error=error)
@auth('deploy.app.config')
def get_versions(request, d_id):
deploy = Deploy.objects.filter(pk=d_id).first()
if not deploy:
@ -181,6 +189,7 @@ def get_versions(request, d_id):
return json_response({'branches': branches, 'tags': tags})
@auth('deploy.app.config')
def kit_key(request):
api_key = AppSetting.get_default('api_key')
return json_response(api_key)

View File

@ -3,7 +3,7 @@
# Released under the AGPL-3.0 License.
from django.views.generic import View
from django.db.models import F
from libs import json_response, JsonParser, Argument
from libs import json_response, JsonParser, Argument, auth
from apps.app.models import Deploy
from apps.config.models import *
import json
@ -11,6 +11,7 @@ import re
class EnvironmentView(View):
@auth('deploy.repository.view|deploy.request.view|config.env.view')
def get(self, request):
query = {}
if not request.user.is_supper:
@ -18,6 +19,7 @@ class EnvironmentView(View):
envs = Environment.objects.filter(**query)
return json_response(envs)
@auth('config.env.add|config.env.edit')
def post(self, request):
form, error = JsonParser(
Argument('id', type=int, required=False),
@ -40,6 +42,7 @@ class EnvironmentView(View):
env.save()
return json_response(error=error)
@auth('config.env.edit')
def patch(self, request):
form, error = JsonParser(
Argument('id', type=int, help='参数错误'),
@ -60,6 +63,7 @@ class EnvironmentView(View):
env.save()
return json_response(error=error)
@auth('config.env.del')
def delete(self, request):
form, error = JsonParser(
Argument('id', type=int, help='请指定操作对象')
@ -74,10 +78,12 @@ class EnvironmentView(View):
class ServiceView(View):
@auth('config.src.view')
def get(self, request):
services = Service.objects.all()
return json_response(services)
@auth('config.src.add|config.src.edit')
def post(self, request):
form, error = JsonParser(
Argument('id', type=int, required=False),
@ -98,6 +104,7 @@ class ServiceView(View):
Service.objects.create(created_by=request.user, **form)
return json_response(error=error)
@auth('config.src.del')
def delete(self, request):
form, error = JsonParser(
Argument('id', type=int, help='请指定操作对象')
@ -110,6 +117,7 @@ class ServiceView(View):
class ConfigView(View):
@auth('config.src.view_config|config.app.view_config')
def get(self, request):
form, error = JsonParser(
Argument('id', type=int, help='未指定操作对象'),
@ -125,6 +133,7 @@ class ConfigView(View):
return json_response(data)
return json_response(error=error)
@auth('config.src.edit_config|config.app.edit_config')
def post(self, request):
form, error = JsonParser(
Argument('o_id', type=int, help='缺少必要参数'),
@ -148,6 +157,7 @@ class ConfigView(View):
ConfigHistory.objects.create(action='1', env_id=env_id, **form)
return json_response(error=error)
@auth('config.src.edit_config|config.app.edit_config')
def patch(self, request):
form, error = JsonParser(
Argument('id', type=int, help='缺少必要参数'),
@ -174,6 +184,7 @@ class ConfigView(View):
config.save()
return json_response(error=error)
@auth('config.src.edit_config|config.app.edit_config')
def delete(self, request):
form, error = JsonParser(
Argument('id', type=int, help='未指定操作对象')
@ -194,6 +205,7 @@ class ConfigView(View):
class HistoryView(View):
@auth('config.src.view_config|config.app.view_config')
def post(self, request):
form, error = JsonParser(
Argument('o_id', type=int, help='缺少必要参数'),
@ -211,6 +223,7 @@ class HistoryView(View):
return json_response(error=error)
@auth('config.src.view_config|config.app.view_config')
def post_diff(request):
form, error = JsonParser(
Argument('o_id', type=int, help='缺少必要参数'),
@ -228,6 +241,7 @@ def post_diff(request):
return json_response(error=error)
@auth('config.src.edit_config|config.app.edit_config')
def parse_json(request):
form, error = JsonParser(
Argument('o_id', type=int, help='缺少必要参数'),
@ -241,6 +255,7 @@ def parse_json(request):
return json_response(error=error)
@auth('config.src.edit_config|config.app.edit_config')
def parse_text(request):
form, error = JsonParser(
Argument('o_id', type=int, help='缺少必要参数'),

View File

@ -6,7 +6,7 @@ from django.db.models import F
from django.conf import settings
from django.http.response import HttpResponseBadRequest
from django_redis import get_redis_connection
from libs import json_response, JsonParser, Argument, human_datetime, human_time
from libs import json_response, JsonParser, Argument, human_datetime, human_time, auth
from apps.deploy.models import DeployRequest
from apps.app.models import Deploy, DeployExtend2
from apps.repository.models import Repository
@ -21,6 +21,7 @@ import os
class RequestView(View):
@auth('deploy.request.view')
def get(self, request):
data, query, counter = [], {}, {}
if not request.user.is_supper:
@ -54,37 +55,7 @@ class RequestView(View):
data.append(tmp)
return json_response(data)
def put(self, request):
form, error = JsonParser(
Argument('id', type=int, help='缺少必要参数'),
Argument('action', filter=lambda x: x in ('check', 'do'), help='参数错误')
).parse(request.body)
if error is None:
req = DeployRequest.objects.filter(pk=form.id).first()
if not req:
return json_response(error='未找到指定发布申请')
pre_req = DeployRequest.objects.filter(
deploy_id=req.deploy_id,
type='1',
id__lt=req.id,
version__isnull=False).first()
if not pre_req:
return json_response(error='未找到该应用可以用于回滚的版本')
if form.action == 'check':
return json_response({'date': pre_req.created_at, 'name': pre_req.name})
DeployRequest.objects.create(
deploy_id=req.deploy_id,
name=f'{req.name} - 回滚',
type='2',
extra=pre_req.extra,
host_ids=req.host_ids,
status='0' if pre_req.deploy.is_audit else '1',
desc='自动回滚至该应用的上个版本',
version=pre_req.version,
created_by=request.user
)
return json_response(error=error)
@auth('deploy.request.del')
def delete(self, request):
form, error = JsonParser(
Argument('id', type=int, required=False),
@ -123,6 +94,7 @@ class RequestView(View):
class RequestDetailView(View):
@auth('deploy.request.view')
def get(self, request, r_id):
req = DeployRequest.objects.filter(pk=r_id).first()
if not req:
@ -160,6 +132,7 @@ class RequestDetailView(View):
outputs['local'].update(step=100, data=f'{human_time()} 已构建完成忽略执行。')
return json_response(response)
@auth('deploy.request.do')
def post(self, request, r_id):
query = {'pk': r_id}
if not request.user.is_supper:
@ -194,6 +167,7 @@ class RequestDetailView(View):
return json_response({'s_actions': s_actions, 'h_actions': h_actions, 'outputs': outputs})
return json_response({'outputs': outputs})
@auth('deploy.request.approve')
def patch(self, request, r_id):
form, error = JsonParser(
Argument('reason', required=False),
@ -216,6 +190,7 @@ class RequestDetailView(View):
return json_response(error=error)
@auth('deploy.request.add|deploy.request.edit')
def post_request_ext1(request):
form, error = JsonParser(
Argument('id', type=int, required=False),
@ -264,6 +239,7 @@ def post_request_ext1(request):
return json_response(error=error)
@auth('deploy.request.do')
def post_request_ext1_rollback(request):
form, error = JsonParser(
Argument('request_id', type=int, help='请选择要回滚的版本'),
@ -295,6 +271,7 @@ def post_request_ext1_rollback(request):
return json_response(error=error)
@auth('deploy.request.add|deploy.request.edit')
def post_request_ext2(request):
form, error = JsonParser(
Argument('id', type=int, required=False),
@ -334,6 +311,7 @@ def post_request_ext2(request):
return json_response(error=error)
@auth('deploy.request.view')
def get_request_info(request):
form, error = JsonParser(
Argument('id', type=int, help='参数错误')
@ -346,6 +324,7 @@ def get_request_info(request):
return json_response(error=error)
@auth('deploy.request.add')
def do_upload(request):
repos_dir = settings.REPOS_DIR
file = request.FILES['file']

View File

@ -4,7 +4,7 @@
from django.views.generic import View
from django_redis import get_redis_connection
from django.conf import settings
from libs import json_response, JsonParser, Argument, human_datetime
from libs import json_response, JsonParser, Argument, human_datetime, auth
from apps.exec.models import ExecTemplate
from apps.host.models import Host
from apps.account.utils import has_host_perm
@ -13,11 +13,13 @@ import json
class TemplateView(View):
@auth('exec.template.view')
def get(self, request):
templates = ExecTemplate.objects.all()
types = [x['type'] for x in templates.order_by('type').values('type').distinct()]
return json_response({'types': types, 'templates': [x.to_dict() for x in templates]})
@auth('exec.template.add|exec.template.edit')
def post(self, request):
form, error = JsonParser(
Argument('id', type=int, required=False),
@ -36,6 +38,7 @@ class TemplateView(View):
ExecTemplate.objects.create(**form)
return json_response(error=error)
@auth('exec.template.del')
def delete(self, request):
form, error = JsonParser(
Argument('id', type=int, help='请指定操作对象')
@ -45,6 +48,7 @@ class TemplateView(View):
return json_response(error=error)
@auth('exec.task.do')
def do_task(request):
form, error = JsonParser(
Argument('host_ids', type=list, filter=lambda x: len(x), help='请选择执行主机'),

View File

@ -6,12 +6,13 @@ from django_redis import get_redis_connection
from apps.host.models import Host
from apps.account.utils import has_host_perm
from apps.file.utils import FileResponseAfter, fetch_dir_list
from libs import json_response, JsonParser, Argument
from libs import json_response, JsonParser, Argument, auth
from functools import partial
import os
class FileView(View):
@auth('host.console.list')
def get(self, request):
form, error = JsonParser(
Argument('id', type=int, help='参数错误'),
@ -29,6 +30,7 @@ class FileView(View):
class ObjectView(View):
@auth('host.console.list')
def get(self, request):
form, error = JsonParser(
Argument('id', type=int, help='参数错误'),
@ -47,6 +49,7 @@ class ObjectView(View):
return FileResponseAfter(ssh_cli.close, f, as_attachment=True, filename=filename)
return json_response(error=error)
@auth('host.console.upload')
def post(self, request):
form, error = JsonParser(
Argument('id', type=int, help='参数错误'),
@ -68,6 +71,7 @@ class ObjectView(View):
ssh.put_file_by_fl(file, f'{form.path}/{file.name}', callback=callback)
return json_response(error=error)
@auth('host.console.del')
def delete(self, request):
form, error = JsonParser(
Argument('id', type=int, help='参数错误'),

View File

@ -11,10 +11,12 @@ from apps.deploy.models import Deploy, DeployRequest
from apps.account.utils import get_host_perms
from libs.utils import json_response, human_date, parse_time
from libs.parser import JsonParser, Argument
from libs.decorators import auth
from datetime import datetime, timedelta
import json
@auth('dashboard.dashboard.view')
def get_statistic(request):
if request.user.is_supper:
app = App.objects.count()
@ -32,6 +34,7 @@ def get_statistic(request):
return json_response(data)
@auth('dashboard.dashboard.view')
def get_alarm(request):
form, error = JsonParser(
Argument('type', required=False),
@ -49,6 +52,7 @@ def get_alarm(request):
return json_response(error=error)
@auth('dashboard.dashboard.view')
def get_request(request):
form, error = JsonParser(
Argument('duration', type=list, help='参数错误')
@ -64,6 +68,7 @@ def get_request(request):
return json_response(error=error)
@auth('dashboard.dashboard.view')
def get_deploy(request):
host = Host.objects.filter(deleted_at__isnull=True).count()
data = {x.id: {'name': x.name, 'count': 0} for x in App.objects.all()}
@ -71,4 +76,3 @@ def get_deploy(request):
data[dep.app_id]['count'] += len(json.loads(dep.host_ids))
data = filter(lambda x: x['count'], data.values())
return json_response({'host': host, 'res': list(data)})

View File

@ -1,12 +1,13 @@
# Copyright: (c) OpenSpug Organization. https://github.com/openspug/spug
# Copyright: (c) <spug.dev@gmail.com>
# Released under the AGPL-3.0 License.
from libs import json_response, JsonParser, Argument
from libs import json_response, JsonParser, Argument, auth
from apps.host.models import Host, HostExtend, Group
from apps.host import utils
import json
@auth('host.host.add')
def get_regions(request):
form, error = JsonParser(
Argument('type', filter=lambda x: x in ('ali', 'tencent'), help='参数错误'),
@ -25,6 +26,7 @@ def get_regions(request):
return json_response(error=error)
@auth('host.host.add')
def cloud_import(request):
form, error = JsonParser(
Argument('type', filter=lambda x: x in ('ali', 'tencent'), help='参数错误'),

View File

@ -2,13 +2,14 @@
# Copyright: (c) <spug.dev@gmail.com>
# Released under the AGPL-3.0 License.
from django.views.generic import View
from libs import json_response, JsonParser, Argument, human_datetime
from libs import json_response, JsonParser, Argument, human_datetime, auth
from apps.host.models import Host, HostExtend
from apps.host.utils import check_os_type, fetch_host_extend
import json
class ExtendView(View):
@auth('host.host.add|host.host.edit')
def get(self, request):
form, error = JsonParser(
Argument('host_id', type=int, help='参数错误')
@ -24,6 +25,7 @@ class ExtendView(View):
return json_response(response)
return json_response(error=error)
@auth('host.host.add|host.host.edit')
def post(self, request):
form, error = JsonParser(
Argument('host_id', type=int, help='参数错误'),

View File

@ -3,7 +3,7 @@
# Released under the AGPL-3.0 License.
from django.views.generic import View
from django.db.models import F
from libs import json_response, JsonParser, Argument
from libs import json_response, JsonParser, Argument, auth
from apps.host.models import Group
from apps.account.models import Role
@ -39,6 +39,7 @@ def filter_by_perm(data, result, ids):
class GroupView(View):
@auth('host.host.view|host.console.view|exec.task.do')
def get(self, request):
with_hosts = request.GET.get('with_hosts')
data, data2 = dict(), dict()
@ -56,6 +57,7 @@ class GroupView(View):
merge_children(data2, '', tree_data)
return json_response({'treeData': tree_data, 'groups': data2})
@auth('admin')
def post(self, request):
form, error = JsonParser(
Argument('id', type=int, required=False),
@ -71,6 +73,7 @@ class GroupView(View):
group.save()
return json_response(error=error)
@auth('admin')
def patch(self, request):
form, error = JsonParser(
Argument('s_id', type=int, help='参数错误'),
@ -100,6 +103,7 @@ class GroupView(View):
src.save()
return json_response(error=error)
@auth('admin')
def delete(self, request):
form, error = JsonParser(
Argument('id', type=int, help='参数错误')

View File

@ -4,7 +4,7 @@
from django.views.generic import View
from django.db.models import F
from django.http.response import HttpResponseBadRequest
from libs import json_response, JsonParser, Argument, AttrDict
from libs import json_response, JsonParser, Argument, AttrDict, auth
from apps.setting.utils import AppSetting
from apps.account.utils import get_host_perms
from apps.host.models import Host, Group
@ -20,6 +20,7 @@ import uuid
class HostView(View):
@auth('host.host.view|exec.task.do')
def get(self, request):
hosts = Host.objects.select_related('hostextend')
if not request.user.is_supper:
@ -29,6 +30,7 @@ class HostView(View):
hosts[rel.host_id]['group_ids'].append(rel.group_id)
return json_response(list(hosts.values()))
@auth('host.host.add|host.host.edit')
def post(self, request):
form, error = JsonParser(
Argument('id', type=int, required=False),
@ -76,6 +78,7 @@ class HostView(View):
return json_response(response)
return json_response(error=error)
@auth('admin')
def patch(self, request):
form, error = JsonParser(
Argument('host_ids', type=list, filter=lambda x: len(x), help='请选择主机'),
@ -93,6 +96,7 @@ class HostView(View):
s_group.hosts.remove(*form.host_ids)
return json_response(error=error)
@auth('host.host.del')
def delete(self, request):
form, error = JsonParser(
Argument('id', type=int, required=False),
@ -122,6 +126,7 @@ class HostView(View):
return json_response(error=error)
@auth('host.host.add')
def post_import(request):
group_id = request.POST.get('group_id')
file = request.FILES['file']
@ -152,6 +157,7 @@ def post_import(request):
return json_response(summary)
@auth('host.host.add')
def post_parse(request):
file = request.FILES['file']
if file:
@ -161,6 +167,7 @@ def post_parse(request):
return HttpResponseBadRequest()
@auth('host.host.add')
def batch_valid(request):
form, error = JsonParser(
Argument('password', required=False),

View File

@ -2,7 +2,7 @@
# Copyright: (c) <spug.dev@gmail.com>
# Released under the AGPL-3.0 License.
from django.views.generic import View
from libs import json_response, JsonParser, Argument, human_datetime
from libs import json_response, JsonParser, Argument, human_datetime, auth
from apps.monitor.models import Detection
from django_redis import get_redis_connection
from django.conf import settings
@ -11,11 +11,13 @@ import json
class DetectionView(View):
@auth('dashboard.dashboard.view|monitor.monitor.view')
def get(self, request):
detections = Detection.objects.all()
groups = [x['group'] for x in detections.order_by('group').values('group').distinct()]
return json_response({'groups': groups, 'detections': [x.to_view() for x in detections]})
@auth('monitor.monitor.add|monitor.monitor.edit')
def post(self, request):
form, error = JsonParser(
Argument('id', type=int, required=False),
@ -53,6 +55,7 @@ class DetectionView(View):
rds_cli.lpush(settings.MONITOR_KEY, json.dumps(form))
return json_response(error=error)
@auth('monitor.monitor.edit')
def patch(self, request):
form, error = JsonParser(
Argument('id', type=int, help='请指定操作对象'),
@ -71,6 +74,7 @@ class DetectionView(View):
rds_cli.lpush(settings.MONITOR_KEY, json.dumps(message))
return json_response(error=error)
@auth('monitor.monitor.del')
def delete(self, request):
form, error = JsonParser(
Argument('id', type=int, help='请指定操作对象')
@ -84,6 +88,7 @@ class DetectionView(View):
return json_response(error=error)
@auth('monitor.monitor.add|monitor.monitor.edit')
def run_test(request):
form, error = JsonParser(
Argument('type', help='请选择监控类型'),

View File

@ -5,7 +5,7 @@ from django.views.generic import View
from django.db.models import F
from django.conf import settings
from django_redis import get_redis_connection
from libs import json_response, JsonParser, Argument, human_time, AttrDict
from libs import json_response, JsonParser, Argument, human_time, AttrDict, auth
from apps.repository.models import Repository
from apps.deploy.models import DeployRequest
from apps.repository.utils import dispatch
@ -15,6 +15,7 @@ import json
class RepositoryView(View):
@auth('deploy.repository.view')
def get(self, request):
deploy_id = request.GET.get('deploy_id')
data = Repository.objects.annotate(
@ -35,6 +36,7 @@ class RepositoryView(View):
response[item.app_id] = tmp
return json_response(list(response.values()))
@auth('deploy.repository.add')
def post(self, request):
form, error = JsonParser(
Argument('deploy_id', type=int, help='参数错误'),
@ -57,6 +59,7 @@ class RepositoryView(View):
return json_response(rep.to_view())
return json_response(error=error)
@auth('deploy.repository.add|deploy.repository.build')
def patch(self, request):
form, error = JsonParser(
Argument('id', type=int, help='参数错误'),
@ -71,6 +74,7 @@ class RepositoryView(View):
return json_response(rep.to_view())
return json_response(error=error)
@auth('deploy.repository.del')
def delete(self, request):
form, error = JsonParser(
Argument('id', type=int, help='请指定操作对象')
@ -85,6 +89,7 @@ class RepositoryView(View):
return json_response(error=error)
@auth('deploy.repository.view')
def get_requests(request):
form, error = JsonParser(
Argument('repository_id', type=int, help='参数错误')
@ -99,6 +104,7 @@ def get_requests(request):
return json_response(requests)
@auth('deploy.repository.view')
def get_detail(request, r_id):
repository = Repository.objects.filter(pk=r_id).first()
if not repository:

View File

@ -10,16 +10,18 @@ from apps.schedule.models import Task, History
from apps.schedule.executors import local_executor, host_executor
from apps.host.models import Host
from django.conf import settings
from libs import json_response, JsonParser, Argument, human_datetime
from libs import json_response, JsonParser, Argument, human_datetime, auth
import json
class Schedule(View):
@auth('schedule.schedule.view')
def get(self, request):
tasks = Task.objects.all()
types = [x['type'] for x in tasks.order_by('type').values('type').distinct()]
return json_response({'types': types, 'tasks': [x.to_dict() for x in tasks]})
@auth('schedule.schedule.add|schedule.schedule.edit')
def post(self, request):
form, error = JsonParser(
Argument('id', type=int, required=False),
@ -61,6 +63,7 @@ class Schedule(View):
Task.objects.create(created_by=request.user, **form)
return json_response(error=error)
@auth('schedule.schedule.edit')
def patch(self, request):
form, error = JsonParser(
Argument('id', type=int, help='请指定操作对象'),
@ -81,6 +84,7 @@ class Schedule(View):
task.save()
return json_response(error=error)
@auth('schedule.schedule.del')
def delete(self, request):
form, error = JsonParser(
Argument('id', type=int, help='请指定操作对象')
@ -96,6 +100,7 @@ class Schedule(View):
class HistoryView(View):
@auth('schedule.schedule.view')
def get(self, request, t_id):
task = Task.objects.filter(pk=t_id).first()
if not task:
@ -108,6 +113,7 @@ class HistoryView(View):
histories = History.objects.filter(task_id=t_id)
return json_response([x.to_list() for x in histories])
@auth('schedule.schedule.edit')
def post(self, request, t_id):
task = Task.objects.filter(pk=t_id).first()
if not task:
@ -156,6 +162,7 @@ class HistoryView(View):
return data
@auth('schedule.schedule.add|schedule.schedule.edit')
def next_run_time(request):
form, error = JsonParser(
Argument('rule', help='参数错误'),

View File

@ -3,19 +3,19 @@
# Released under the AGPL-3.0 License.
import django
from django.core.cache import cache
from django.views.generic import View
from django.conf import settings
from libs import JsonParser, Argument, json_response
from libs import JsonParser, Argument, json_response, auth
from libs.utils import generate_random_str
from libs.mail import Mail
from libs.spug import send_login_wx_code
from libs.mixins import AdminView
from apps.setting.utils import AppSetting
from apps.setting.models import Setting
import platform
import ldap
class SettingView(View):
class SettingView(AdminView):
def get(self, request):
data = Setting.objects.all()
return json_response([x.to_view() for x in data])
@ -30,7 +30,7 @@ class SettingView(View):
return json_response(error=error)
class MFAView(View):
class MFAView(AdminView):
def get(self, request):
if not request.user.wx_token:
return json_response(error='检测到当前账户未配置微信Token请配置后再尝试启用MFA认证否则可能造成系统无法正常登录。')
@ -61,6 +61,7 @@ class MFAView(View):
return json_response(error=error)
@auth('admin')
def ldap_test(request):
form, error = JsonParser(
Argument('server'),
@ -79,6 +80,7 @@ def ldap_test(request):
return json_response(error=error)
@auth('admin')
def email_test(request):
form, error = JsonParser(
Argument('server', help='请输入邮件服务地址'),
@ -97,6 +99,7 @@ def email_test(request):
return json_response(error=error)
@auth('admin')
def mfa_test(request):
if not request.user.wx_token:
return json_response(error='检测到当前账户未配置微信Token请配置后再尝试启用MFA认证否则可能造成系统无法正常登录。')
@ -106,6 +109,7 @@ def mfa_test(request):
return json_response()
@auth('admin')
def get_about(request):
return json_response({
'python_version': platform.python_version(),

View File

@ -5,35 +5,20 @@ from functools import wraps
from .utils import json_response
def permission_required_supper(view_func):
@wraps(view_func)
def wrapper(*args, **kwargs):
request = None
for item in args:
if hasattr(item, 'user'):
request = item
break
if request is None or not request.user.is_supper:
return json_response(error='需要管理员权限')
return view_func(*args, **kwargs)
return wrapper
def permission_required(perm_list):
def auth(perm_list):
def decorate(view_func):
codes = (perm_list,) if isinstance(perm_list, str) else perm_list
codes = perm_list.split('|')
@wraps(view_func)
def wrapper(*args, **kwargs):
request = None
for item in args:
user = None
for item in args[:2]:
if hasattr(item, 'user'):
request = item
user = item.user
break
if request is None or (not request.user.is_supper and not request.user.has_perms(codes)):
return json_response(error='拒绝访问')
if user and user.has_perms(codes):
return view_func(*args, **kwargs)
return json_response(error='权限拒绝')
return wrapper

View File

@ -1,6 +1,7 @@
# Copyright: (c) OpenSpug Organization. https://github.com/openspug/spug
# Copyright: (c) <spug.dev@gmail.com>
# Released under the AGPL-3.0 License.
from django.views.generic import View
from .utils import json_response
@ -24,38 +25,9 @@ class ModelMixin(object):
self.save()
# 使用该混入类需要request.user对象实现has_perms方法
class PermissionMixin(object):
"""
CBV mixin which verifies that the current user has all specified
permissions.
"""
permission_required = None
def get_permission_required(self):
"""
Override this method to override the permission_required attribute.
Must return an iterable.
"""
if self.permission_required is None:
raise AttributeError(
'{0} is missing the permission_required attribute. Define {0}.permission_required, or override '
'{0}.get_permission_required().'.format(self.__class__.__name__)
)
if isinstance(self.permission_required, str):
perms = (self.permission_required,)
else:
perms = self.permission_required
return perms
def has_permission(self):
"""
Override this method to customize the way permissions are checked.
"""
perms = self.get_permission_required()
return self.request.user.has_perms(perms)
class AdminView(View):
def dispatch(self, request, *args, **kwargs):
if not self.has_permission():
return json_response(error='拒绝访问')
return super(PermissionMixin, self).dispatch(request, *args, **kwargs)
if hasattr(request, 'user') and request.user.is_supper:
return super().dispatch(request, *args, **kwargs)
else:
return json_response(error='权限拒绝')

View File

@ -3,15 +3,22 @@
* Copyright (c) <spug.dev@gmail.com>
* Released under the AGPL-3.0 License.
*/
import React from 'react';
import React, {useEffect} from 'react';
import { observer } from 'mobx-react';
import { Modal, Steps } from 'antd';
import Step1 from './Step1';
import Step2 from './Step2';
import store from './store';
import styles from './index.module.css';
import groupStore from '../alarm/group/store';
export default observer(function () {
useEffect(() => {
if (groupStore.records.length === 0) {
groupStore.fetchRecords();
}
}, [])
return (
<Modal
visible

View File

@ -9,36 +9,13 @@ import { Table, Modal, Radio, Tag, message } from 'antd';
import { PlusOutlined } from '@ant-design/icons';
import { Action, AuthButton, TableCard } from 'components';
import { http, hasPermission } from 'libs';
import groupStore from '../alarm/group/store';
import hostStore from '../host/store';
import store from './store';
@observer
class ComTable extends React.Component {
constructor(props) {
super(props);
this.state = {
hosts: {}
}
}
componentDidMount() {
store.fetchRecords();
if (groupStore.records.length === 0) groupStore.fetchRecords();
if (!hostStore.records || hostStore.records.length === 0) {
hostStore.fetchRecords().then(this._handleHosts)
} else {
this._handleHosts()
}
}
_handleHosts = () => {
const tmp = {};
for (let item of hostStore.records) {
tmp[item.id] = item
}
this.setState({hosts: tmp})
};
handleActive = (text) => {
Modal.confirm({

View File

@ -22,7 +22,7 @@ class ComTable extends React.Component {
moreMenus = (info) => (
<Menu>
<Menu.Item>
<LinkButton onClick={() => this.handleTest(info)}>执行测试</LinkButton>
<LinkButton auth="schedule.schedule.edit" onClick={() => this.handleTest(info)}>执行测试</LinkButton>
</Menu.Item>
<Menu.Item>
<LinkButton