diff --git a/spug_api/apps/account/history.py b/spug_api/apps/account/history.py index 558b4bc..8af9545 100644 --- a/spug_api/apps/account/history.py +++ b/spug_api/apps/account/history.py @@ -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')): diff --git a/spug_api/apps/account/models.py b/spug_api/apps/account/models.py index 047a0f9..db7070d 100644 --- a/spug_api/apps/account/models.py +++ b/spug_api/apps/account/models.py @@ -2,6 +2,7 @@ # Copyright: (c) # 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 '' % 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 '' % self.name diff --git a/spug_api/apps/account/views.py b/spug_api/apps/account/views.py index 3732843..4d0515e 100644 --- a/spug_api/apps/account/views.py +++ b/spug_api/apps/account/views.py @@ -2,7 +2,7 @@ # Copyright: (c) # 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) }) diff --git a/spug_api/apps/alarm/views.py b/spug_api/apps/alarm/views.py index 0ff1309..774aac0 100644 --- a/spug_api/apps/alarm/views.py +++ b/spug_api/apps/alarm/views.py @@ -2,23 +2,26 @@ # Copyright: (c) # 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='请指定操作对象') diff --git a/spug_api/apps/app/views.py b/spug_api/apps/app/views.py index 3874404..f8dfab4 100644 --- a/spug_api/apps/app/views.py +++ b/spug_api/apps/app/views.py @@ -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) diff --git a/spug_api/apps/config/views.py b/spug_api/apps/config/views.py index 863ad14..944acc2 100644 --- a/spug_api/apps/config/views.py +++ b/spug_api/apps/config/views.py @@ -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='缺少必要参数'), diff --git a/spug_api/apps/deploy/views.py b/spug_api/apps/deploy/views.py index 021d157..4c6ac4f 100644 --- a/spug_api/apps/deploy/views.py +++ b/spug_api/apps/deploy/views.py @@ -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'] diff --git a/spug_api/apps/exec/views.py b/spug_api/apps/exec/views.py index bcd657e..fa3e5d5 100644 --- a/spug_api/apps/exec/views.py +++ b/spug_api/apps/exec/views.py @@ -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='请选择执行主机'), diff --git a/spug_api/apps/file/views.py b/spug_api/apps/file/views.py index 990aa4d..53004bf 100644 --- a/spug_api/apps/file/views.py +++ b/spug_api/apps/file/views.py @@ -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='参数错误'), diff --git a/spug_api/apps/home/views.py b/spug_api/apps/home/views.py index bb6d806..2343cbc 100644 --- a/spug_api/apps/home/views.py +++ b/spug_api/apps/home/views.py @@ -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)}) - diff --git a/spug_api/apps/host/add.py b/spug_api/apps/host/add.py index dad4b75..3a566d4 100644 --- a/spug_api/apps/host/add.py +++ b/spug_api/apps/host/add.py @@ -1,12 +1,13 @@ # Copyright: (c) OpenSpug Organization. https://github.com/openspug/spug # Copyright: (c) # 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='参数错误'), diff --git a/spug_api/apps/host/extend.py b/spug_api/apps/host/extend.py index 57f5b56..45d3a46 100644 --- a/spug_api/apps/host/extend.py +++ b/spug_api/apps/host/extend.py @@ -2,13 +2,14 @@ # Copyright: (c) # 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='参数错误'), diff --git a/spug_api/apps/host/group.py b/spug_api/apps/host/group.py index bde18fe..516446b 100644 --- a/spug_api/apps/host/group.py +++ b/spug_api/apps/host/group.py @@ -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='参数错误') diff --git a/spug_api/apps/host/views.py b/spug_api/apps/host/views.py index a7ab4f4..b35c454 100644 --- a/spug_api/apps/host/views.py +++ b/spug_api/apps/host/views.py @@ -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), diff --git a/spug_api/apps/monitor/views.py b/spug_api/apps/monitor/views.py index 5db6a91..70fa4d1 100644 --- a/spug_api/apps/monitor/views.py +++ b/spug_api/apps/monitor/views.py @@ -2,7 +2,7 @@ # Copyright: (c) # 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='请选择监控类型'), diff --git a/spug_api/apps/repository/views.py b/spug_api/apps/repository/views.py index 34675f9..a1c0d88 100644 --- a/spug_api/apps/repository/views.py +++ b/spug_api/apps/repository/views.py @@ -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: diff --git a/spug_api/apps/schedule/views.py b/spug_api/apps/schedule/views.py index 73faed8..ecbec4c 100644 --- a/spug_api/apps/schedule/views.py +++ b/spug_api/apps/schedule/views.py @@ -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='参数错误'), diff --git a/spug_api/apps/setting/views.py b/spug_api/apps/setting/views.py index c9c79ed..dce5b90 100644 --- a/spug_api/apps/setting/views.py +++ b/spug_api/apps/setting/views.py @@ -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(), diff --git a/spug_api/libs/decorators.py b/spug_api/libs/decorators.py index a13ee74..a1b2ff1 100644 --- a/spug_api/libs/decorators.py +++ b/spug_api/libs/decorators.py @@ -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='拒绝访问') - return view_func(*args, **kwargs) + if user and user.has_perms(codes): + return view_func(*args, **kwargs) + return json_response(error='权限拒绝') return wrapper diff --git a/spug_api/libs/mixins.py b/spug_api/libs/mixins.py index 8e418ce..5340ca6 100644 --- a/spug_api/libs/mixins.py +++ b/spug_api/libs/mixins.py @@ -1,6 +1,7 @@ # Copyright: (c) OpenSpug Organization. https://github.com/openspug/spug # Copyright: (c) # 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='权限拒绝') diff --git a/spug_web/src/pages/monitor/Form.js b/spug_web/src/pages/monitor/Form.js index 406201f..7a48a0f 100644 --- a/spug_web/src/pages/monitor/Form.js +++ b/spug_web/src/pages/monitor/Form.js @@ -3,15 +3,22 @@ * Copyright (c) * 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 ( { - const tmp = {}; - for (let item of hostStore.records) { - tmp[item.id] = item - } - this.setState({hosts: tmp}) - }; - handleActive = (text) => { Modal.confirm({ title: '操作确认', diff --git a/spug_web/src/pages/schedule/Table.js b/spug_web/src/pages/schedule/Table.js index 950f444..f0e8cb4 100644 --- a/spug_web/src/pages/schedule/Table.js +++ b/spug_web/src/pages/schedule/Table.js @@ -22,7 +22,7 @@ class ComTable extends React.Component { moreMenus = (info) => ( - this.handleTest(info)}>执行测试 + this.handleTest(info)}>执行测试