pull/330/head
vapao 2021-06-03 11:34:42 +08:00
parent 8bb828a4cb
commit 5953e8e61f
13 changed files with 50 additions and 37 deletions

View File

@ -52,13 +52,8 @@ class User(models.Model, ModelMixin):
return perms return perms
@property @property
def host_perms(self): def group_perms(self):
return json.loads(self.role.host_perms) if self.role and self.role.host_perms else [] return json.loads(self.role.group_perms) if self.role and self.role.group_perms else []
def has_host_perm(self, host_id):
if isinstance(host_id, (list, set, tuple)):
return self.is_supper or set(host_id).issubset(set(self.host_perms))
return self.is_supper or int(host_id) in self.host_perms
def has_perms(self, codes): def has_perms(self, codes):
# return self.is_supper or self.role in codes # return self.is_supper or self.role in codes
@ -77,8 +72,7 @@ class Role(models.Model, ModelMixin):
desc = models.CharField(max_length=255, null=True) desc = models.CharField(max_length=255, null=True)
page_perms = models.TextField(null=True) page_perms = models.TextField(null=True)
deploy_perms = models.TextField(null=True) deploy_perms = models.TextField(null=True)
host_perms = models.TextField(null=True) group_perms = models.TextField(null=True)
created_at = models.CharField(max_length=20, default=human_datetime) created_at = models.CharField(max_length=20, default=human_datetime)
created_by = models.ForeignKey(User, on_delete=models.PROTECT, related_name='+') created_by = models.ForeignKey(User, on_delete=models.PROTECT, related_name='+')
@ -86,7 +80,7 @@ class Role(models.Model, ModelMixin):
tmp = super().to_dict(*args, **kwargs) tmp = super().to_dict(*args, **kwargs)
tmp['page_perms'] = json.loads(self.page_perms) if self.page_perms else {} tmp['page_perms'] = json.loads(self.page_perms) if self.page_perms else {}
tmp['deploy_perms'] = json.loads(self.deploy_perms) if self.deploy_perms else {} tmp['deploy_perms'] = json.loads(self.deploy_perms) if self.deploy_perms else {}
tmp['host_perms'] = json.loads(self.host_perms) if self.host_perms else [] tmp['group_perms'] = json.loads(self.group_perms) if self.group_perms else []
tmp['used'] = self.user_set.count() tmp['used'] = self.user_set.count()
return tmp return tmp

View File

@ -0,0 +1,21 @@
# Copyright: (c) OpenSpug Organization. https://github.com/openspug/spug
# Copyright: (c) <spug.dev@gmail.com>
# Released under the AGPL-3.0 License.
from apps.host.models import Group
def get_host_perms(user):
ids = sub_ids = set(user.group_perms)
while sub_ids:
sub_ids = [x.id for x in Group.objects.filter(parent_id__in=sub_ids)]
ids.update(sub_ids)
return set(x.host_id for x in Group.hosts.through.objects.filter(group_id__in=ids))
def has_host_perm(user, target):
if user.is_supper:
return True
host_ids = get_host_perms(user)
if isinstance(target, (list, set, tuple)):
return set(target).issubset(host_ids)
return int(target) in host_ids

View File

@ -100,7 +100,7 @@ class RoleView(View):
Argument('id', type=int, help='参数错误'), Argument('id', type=int, help='参数错误'),
Argument('page_perms', type=dict, required=False), Argument('page_perms', type=dict, required=False),
Argument('deploy_perms', type=dict, required=False), Argument('deploy_perms', type=dict, required=False),
Argument('host_perms', type=list, required=False) Argument('group_perms', type=list, required=False)
).parse(request.body) ).parse(request.body)
if error is None: if error is None:
role = Role.objects.filter(pk=form.pop('id')).first() role = Role.objects.filter(pk=form.pop('id')).first()
@ -110,8 +110,8 @@ class RoleView(View):
role.page_perms = json.dumps(form.page_perms) role.page_perms = json.dumps(form.page_perms)
if form.deploy_perms is not None: if form.deploy_perms is not None:
role.deploy_perms = json.dumps(form.deploy_perms) role.deploy_perms = json.dumps(form.deploy_perms)
if form.host_perms is not None: if form.group_perms is not None:
role.host_perms = json.dumps(form.host_perms) role.group_perms = json.dumps(form.group_perms)
role.user_set.update(token_expired=0) role.user_set.update(token_expired=0)
role.save() role.save()
return json_response(error=error) return json_response(error=error)
@ -206,7 +206,6 @@ def handle_user_info(user, x_real_ip):
'nickname': user.nickname, 'nickname': user.nickname,
'is_supper': user.is_supper, '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, 'has_real_ip': x_real_ip and ipaddress.ip_address(x_real_ip).is_global if verify_ip else True,
'host_perms': [] if user.is_supper else user.host_perms,
'permissions': [] if user.is_supper else user.page_perms 'permissions': [] if user.is_supper else user.page_perms
}) })

View File

@ -7,6 +7,7 @@ from django.conf import settings
from libs import json_response, JsonParser, Argument, human_datetime from libs import json_response, JsonParser, Argument, human_datetime
from apps.exec.models import ExecTemplate from apps.exec.models import ExecTemplate
from apps.host.models import Host from apps.host.models import Host
from apps.account.utils import has_host_perm
import uuid import uuid
import json import json
@ -50,7 +51,7 @@ def do_task(request):
Argument('command', help='请输入执行命令内容') Argument('command', help='请输入执行命令内容')
).parse(request.body) ).parse(request.body)
if error is None: if error is None:
if not request.user.has_host_perm(form.host_ids): if not has_host_perm(request.user, form.host_ids):
return json_response(error='无权访问主机,请联系管理员') return json_response(error='无权访问主机,请联系管理员')
token, rds = uuid.uuid4().hex, get_redis_connection() token, rds = uuid.uuid4().hex, get_redis_connection()
for host in Host.objects.filter(id__in=form.host_ids): for host in Host.objects.filter(id__in=form.host_ids):

View File

@ -4,6 +4,7 @@
from django.views.generic import View from django.views.generic import View
from django_redis import get_redis_connection from django_redis import get_redis_connection
from apps.host.models import Host from apps.host.models import Host
from apps.account.utils import has_host_perm
from apps.file.utils import FileResponseAfter, parse_sftp_attr from apps.file.utils import FileResponseAfter, parse_sftp_attr
from libs import json_response, JsonParser, Argument from libs import json_response, JsonParser, Argument
from functools import partial from functools import partial
@ -17,7 +18,7 @@ class FileView(View):
Argument('path', help='参数错误') Argument('path', help='参数错误')
).parse(request.GET) ).parse(request.GET)
if error is None: if error is None:
if not request.user.has_host_perm(form.id): if not has_host_perm(request.user, form.id):
return json_response(error='无权访问主机,请联系管理员') return json_response(error='无权访问主机,请联系管理员')
host = Host.objects.get(pk=form.id) host = Host.objects.get(pk=form.id)
if not host: if not host:
@ -35,7 +36,7 @@ class ObjectView(View):
Argument('file', help='请输入文件路径') Argument('file', help='请输入文件路径')
).parse(request.GET) ).parse(request.GET)
if error is None: if error is None:
if not request.user.has_host_perm(form.id): if not has_host_perm(request.user, form.id):
return json_response(error='无权访问主机,请联系管理员') return json_response(error='无权访问主机,请联系管理员')
host = Host.objects.filter(pk=form.id).first() host = Host.objects.filter(pk=form.id).first()
if not host: if not host:
@ -54,7 +55,7 @@ class ObjectView(View):
Argument('path', help='参数错误'), Argument('path', help='参数错误'),
).parse(request.POST) ).parse(request.POST)
if error is None: if error is None:
if not request.user.has_host_perm(form.id): if not has_host_perm(request.user, form.id):
return json_response(error='无权访问主机,请联系管理员') return json_response(error='无权访问主机,请联系管理员')
file = request.FILES.get('file') file = request.FILES.get('file')
if not file: if not file:
@ -74,7 +75,7 @@ class ObjectView(View):
Argument('file', help='请输入文件路径') Argument('file', help='请输入文件路径')
).parse(request.GET) ).parse(request.GET)
if error is None: if error is None:
if not request.user.has_host_perm(form.id): if not has_host_perm(request.user, form.id):
return json_response(error='无权访问主机,请联系管理员') return json_response(error='无权访问主机,请联系管理员')
host = Host.objects.get(pk=form.id) host = Host.objects.get(pk=form.id)
if not host: if not host:

View File

@ -8,6 +8,7 @@ from apps.schedule.models import Task
from apps.monitor.models import Detection from apps.monitor.models import Detection
from apps.alarm.models import Alarm from apps.alarm.models import Alarm
from apps.deploy.models import Deploy, DeployRequest 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.utils import json_response, human_date, parse_time
from libs.parser import JsonParser, Argument from libs.parser import JsonParser, Argument
from datetime import datetime, timedelta from datetime import datetime, timedelta
@ -19,9 +20,9 @@ def get_statistic(request):
app = App.objects.count() app = App.objects.count()
host = Host.objects.count() host = Host.objects.count()
else: else:
deploy_perms, host_perms = request.user.deploy_perms, request.user.host_perms deploy_perms, host_perms = request.user.deploy_perms, get_host_perms(request.user)
app = App.objects.filter(id__in=deploy_perms['apps']).count() app = App.objects.filter(id__in=deploy_perms['apps']).count()
host = Host.objects.filter(id__in=host_perms).count() host = len(host_perms)
data = { data = {
'app': app, 'app': app,
'host': host, 'host': host,

View File

@ -50,7 +50,7 @@ class GroupView(View):
if request.user.is_supper: if request.user.is_supper:
tree_data = list(data.values()) tree_data = list(data.values())
else: else:
tree_data, ids = [], request.user.host_perms tree_data, ids = [], request.user.group_perms
filter_by_perm(data.values(), tree_data, ids) filter_by_perm(data.values(), tree_data, ids)
merge_children(data2, '', tree_data) merge_children(data2, '', tree_data)
return json_response({'treeData': tree_data, 'groups': data2}) return json_response({'treeData': tree_data, 'groups': data2})

View File

@ -6,6 +6,7 @@ from django.db.models import F
from django.http.response import HttpResponseBadRequest from django.http.response import HttpResponseBadRequest
from libs import json_response, JsonParser, Argument from libs import json_response, JsonParser, Argument
from apps.setting.utils import AppSetting from apps.setting.utils import AppSetting
from apps.account.utils import get_host_perms
from apps.host.models import Host, Group from apps.host.models import Host, Group
from apps.app.models import Deploy from apps.app.models import Deploy
from apps.schedule.models import Task from apps.schedule.models import Task
@ -18,8 +19,11 @@ from openpyxl import load_workbook
class HostView(View): class HostView(View):
def get(self, request): def get(self, request):
hosts = {x.id: x.to_view() for x in Host.objects.select_related('hostextend').all()} hosts = Host.objects.select_related('hostextend')
for rel in Group.hosts.through.objects.all(): if not request.user.is_supper:
hosts = hosts.filter(id__in=get_host_perms(request.user))
hosts = {x.id: x.to_view() for x in hosts}
for rel in Group.hosts.through.objects.filter(host_id__in=hosts.keys()):
hosts[rel.host_id]['group_ids'].append(rel.group_id) hosts[rel.host_id]['group_ids'].append(rel.group_id)
return json_response(list(hosts.values())) return json_response(list(hosts.values()))

View File

@ -6,6 +6,7 @@ from django.conf import settings
from django_redis import get_redis_connection from django_redis import get_redis_connection
from asgiref.sync import async_to_sync from asgiref.sync import async_to_sync
from apps.host.models import Host from apps.host.models import Host
from apps.account.utils import has_host_perm
from threading import Thread from threading import Thread
import time import time
import json import json
@ -109,7 +110,7 @@ class SSHConsumer(WebsocketConsumer):
# print('Connection close') # print('Connection close')
def connect(self): def connect(self):
if self.user.has_host_perm(self.id): if has_host_perm(self.user, self.id):
self.accept() self.accept()
self._init() self._init()
else: else:

View File

@ -6,7 +6,6 @@
let Permission = { let Permission = {
isReady: false, isReady: false,
isSuper: false, isSuper: false,
hostPerms: [],
permissions: [] permissions: []
}; };
@ -16,7 +15,6 @@ export function updatePermissions() {
X_TOKEN = localStorage.getItem('token'); X_TOKEN = localStorage.getItem('token');
Permission.isReady = true; Permission.isReady = true;
Permission.isSuper = localStorage.getItem('is_supper') === 'true'; Permission.isSuper = localStorage.getItem('is_supper') === 'true';
Permission.hostPerms = JSON.parse(localStorage.getItem('host_perms') || '[]');
Permission.permissions = JSON.parse(localStorage.getItem('permissions') || '[]'); Permission.permissions = JSON.parse(localStorage.getItem('permissions') || '[]');
} }
@ -32,11 +30,6 @@ export function hasPermission(strCode) {
return false return false
} }
export function hasHostPermission(id) {
const {isSuper, hostPerms} = Permission;
return isSuper || hostPerms.includes(id)
}
export function includes(s, key) { export function includes(s, key) {
key = key.toLowerCase(); key = key.toLowerCase();
if (s) { if (s) {

View File

@ -69,7 +69,6 @@ export default function () {
localStorage.setItem('nickname', data['nickname']); localStorage.setItem('nickname', data['nickname']);
localStorage.setItem('is_supper', data['is_supper']); localStorage.setItem('is_supper', data['is_supper']);
localStorage.setItem('permissions', JSON.stringify(data['permissions'])); localStorage.setItem('permissions', JSON.stringify(data['permissions']));
localStorage.setItem('host_perms', JSON.stringify(data['host_perms']));
updatePermissions(); updatePermissions();
if (history.location.state && history.location.state['from']) { if (history.location.state && history.location.state['from']) {
history.push(history.location.state['from']) history.push(history.location.state['from'])

View File

@ -7,7 +7,6 @@ import React from 'react';
import { observer } from 'mobx-react'; import { observer } from 'mobx-react';
import { MinusCircleOutlined, PlusOutlined } from '@ant-design/icons'; import { MinusCircleOutlined, PlusOutlined } from '@ant-design/icons';
import { Form, Select, Button } from 'antd'; import { Form, Select, Button } from 'antd';
import { hasHostPermission } from 'libs';
import store from './store'; import store from './store';
import hostStore from 'pages/host/store'; import hostStore from 'pages/host/store';
import styles from './index.module.css'; import styles from './index.module.css';
@ -27,7 +26,7 @@ export default observer(function () {
filterOption={(input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0} filterOption={(input, option) => option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0}
onChange={v => store.editTarget(index, v)}> onChange={v => store.editTarget(index, v)}>
<Select.Option value="local" disabled={store.targets.includes('local')}>本机</Select.Option> <Select.Option value="local" disabled={store.targets.includes('local')}>本机</Select.Option>
{hostStore.records.filter(x => x.id === id || hasHostPermission(x.id)).map(item => ( {hostStore.records.map(item => (
<Select.Option key={item.id} value={item.id} disabled={store.targets.includes(item.id)}> <Select.Option key={item.id} value={item.id} disabled={store.targets.includes(item.id)}>
{`${item.name}(${item['hostname']}:${item['port']})`} {`${item.name}(${item['hostname']}:${item['port']})`}
</Select.Option> </Select.Option>

View File

@ -14,7 +14,7 @@ import styles from './index.module.css';
export default observer(function () { export default observer(function () {
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [groups, setGroups] = useState([...store.record.host_perms]); const [groups, setGroups] = useState([...store.record.group_perms]);
useEffect(() => { useEffect(() => {
if (hostStore.treeData.length === 0) { if (hostStore.treeData.length === 0) {
@ -24,7 +24,7 @@ export default observer(function () {
function handleSubmit() { function handleSubmit() {
setLoading(true); setLoading(true);
http.patch('/api/account/role/', {id: store.record.id, host_perms: groups}) http.patch('/api/account/role/', {id: store.record.id, group_perms: groups})
.then(res => { .then(res => {
message.success('操作成功'); message.success('操作成功');
store.hostPermVisible = false; store.hostPermVisible = false;