mirror of https://github.com/openspug/spug
update
parent
8bb828a4cb
commit
5953e8e61f
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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
|
|
@ -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
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
|
@ -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):
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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})
|
||||||
|
|
|
@ -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()))
|
||||||
|
|
||||||
|
|
|
@ -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:
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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'])
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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;
|
||||||
|
|
Loading…
Reference in New Issue