mirror of
https://github.com/ziirish/burp-ui.git
synced 2026-05-15 14:16:08 -06:00
879 lines
33 KiB
Python
879 lines
33 KiB
Python
# -*- coding: utf8 -*-
|
|
"""
|
|
.. module:: burpui.api.settings
|
|
:platform: Unix
|
|
:synopsis: Burp-UI settings api module.
|
|
|
|
.. moduleauthor:: Ziirish <hi+burpui@ziirish.me>
|
|
|
|
"""
|
|
import json
|
|
|
|
from . import api
|
|
from ..engines.server import BUIServer # noqa
|
|
from ..ext.cache import cache
|
|
from .custom import Resource
|
|
from .._compat import unquote
|
|
from ..utils import NOTIF_INFO
|
|
|
|
from flask_babel import gettext as _, refresh
|
|
from flask import jsonify, request, url_for, current_app, g, session, render_template_string
|
|
from flask_login import current_user
|
|
from flask_restplus import inputs
|
|
from jinja2 import Environment, meta
|
|
from ..datastructures import ImmutableMultiDict, MultiDict
|
|
|
|
TEMPLATE_EXCLUDES = ['client', 'agent']
|
|
|
|
bui = current_app # type: BUIServer
|
|
ns = api.namespace('settings', 'Settings methods')
|
|
|
|
|
|
@ns.route('/server-config',
|
|
'/<server>/server-config',
|
|
'/server-config/<path:conf>',
|
|
'/<server>/server-config/<path:conf>',
|
|
endpoint='server_settings')
|
|
@ns.doc(
|
|
params={
|
|
'conf': 'Path of the configuration file',
|
|
'server': 'Which server to collect data from when in multi-agent mode',
|
|
},
|
|
)
|
|
class ServerSettings(Resource):
|
|
"""The :class:`burpui.api.settings.ServerSettings` resource allows you to
|
|
read and write the server's configuration.
|
|
|
|
This resource is part of the :mod:`burpui.api.settings` module.
|
|
"""
|
|
|
|
@api.disabled_on_demo()
|
|
@api.acl_admin_required(message='Sorry, you don\'t have rights to access the setting panel')
|
|
@ns.doc(
|
|
responses={
|
|
200: 'Success',
|
|
403: 'Insufficient permissions',
|
|
500: 'Internal failure',
|
|
}
|
|
)
|
|
def post(self, conf=None, server=None):
|
|
"""Saves the server configuration"""
|
|
noti = bui.client.store_conf_srv(request.form, conf, server)
|
|
bui.audit.logger.info(f'updated burp-server configuration ({conf})', server=server)
|
|
return {'notif': noti}, 200
|
|
|
|
@api.disabled_on_demo()
|
|
@api.acl_admin_required(message='Sorry, you don\'t have rights to access the setting panel')
|
|
@ns.doc(
|
|
responses={
|
|
200: 'Success',
|
|
403: 'Insufficient permissions',
|
|
500: 'Internal failure',
|
|
}
|
|
)
|
|
def delete(self, conf=None, server=None):
|
|
"""Deletes a configuration file"""
|
|
try:
|
|
conf = unquote(conf)
|
|
except:
|
|
pass
|
|
parser = bui.client.get_parser(agent=server)
|
|
bui.audit.logger.info(f'requested removal of {conf}', server=server)
|
|
return parser.remove_conf(conf)
|
|
|
|
@api.acl_admin_required(message='Sorry, you don\'t have rights to access the setting panel')
|
|
@ns.doc(
|
|
responses={
|
|
200: 'Success',
|
|
403: 'Insufficient permissions',
|
|
500: 'Internal failure',
|
|
}
|
|
)
|
|
def get(self, conf=None, server=None):
|
|
"""Reads the server configuration
|
|
|
|
**GET** method provided by the webservice.
|
|
|
|
The *JSON* returned is:
|
|
::
|
|
|
|
{
|
|
"boolean": [
|
|
"daemon",
|
|
"fork",
|
|
"..."
|
|
],
|
|
"defaults": {
|
|
"address": "",
|
|
"autoupgrade_dir": "",
|
|
"ca_burp_ca": "",
|
|
"ca_conf": "",
|
|
"ca_name": "",
|
|
"ca_server_name": "",
|
|
"client_can_delete": true,
|
|
"...": "..."
|
|
},
|
|
"integer": [
|
|
"port",
|
|
"status_port",
|
|
"..."
|
|
],
|
|
"multi": [
|
|
"keep",
|
|
"restore_client",
|
|
"..."
|
|
],
|
|
"placeholders": {
|
|
"autoupgrade_dir": "path",
|
|
"ca_burp_ca": "path",
|
|
"ca_conf": "path",
|
|
"ca_name": "name",
|
|
"ca_server_name": "name",
|
|
"client_can_delete": "0|1",
|
|
"...": "..."
|
|
},
|
|
"results": {
|
|
"boolean": [
|
|
{
|
|
"name": "hardlinked_archive",
|
|
"value": false,
|
|
"reset": false
|
|
},
|
|
{
|
|
"name": "syslog",
|
|
"value": true,
|
|
"reset": false
|
|
},
|
|
{ "...": "..." }
|
|
],
|
|
"clients": [
|
|
{
|
|
"name": "testclient",
|
|
"value": "/etc/burp/clientconfdir/testclient"
|
|
}
|
|
],
|
|
"common": [
|
|
{
|
|
"name": "mode",
|
|
"value": "server",
|
|
"reset": false
|
|
},
|
|
{
|
|
"name": "directory",
|
|
"value": "/var/spool/burp",
|
|
"reset": false
|
|
},
|
|
{ "...": "..." }
|
|
],
|
|
"includes": [],
|
|
"includes_ext": [],
|
|
"integer": [
|
|
{
|
|
"name": "port",
|
|
"value": 4971,
|
|
"reset": false
|
|
},
|
|
{
|
|
"name": "status_port",
|
|
"value": 4972,
|
|
"reset": false
|
|
},
|
|
{ "...": "..." }
|
|
],
|
|
"multi": [
|
|
{
|
|
"name": "keep",
|
|
"value": [
|
|
"7",
|
|
"4"
|
|
],
|
|
"reset": [
|
|
false,
|
|
true
|
|
]
|
|
},
|
|
{ "...": "..." }
|
|
],
|
|
"hierarchy": [
|
|
{
|
|
"children": [
|
|
{
|
|
"children": [],
|
|
"dir": "/tmp/burp/conf.d",
|
|
"full": "/tmp/burp/conf.d/empty.conf",
|
|
"name": "empty.conf",
|
|
"parent": "/tmp/burp/burp-server.conf"
|
|
},
|
|
{
|
|
"children": [],
|
|
"dir": "/tmp/burp/conf.d",
|
|
"full": "/tmp/burp/conf.d/ipv4.conf",
|
|
"name": "ipv4.conf",
|
|
"parent": "/tmp/burp/burp-server.conf"
|
|
}
|
|
],
|
|
"dir": "/tmp/burp",
|
|
"full": "/tmp/burp/burp-server.conf",
|
|
"name": "burp-server.conf",
|
|
"parent": null
|
|
}
|
|
]
|
|
},
|
|
"server_doc": {
|
|
"address": "Defines the main TCP address...",
|
|
"...": "..."
|
|
},
|
|
"string": [
|
|
"mode",
|
|
"address",
|
|
"..."
|
|
],
|
|
"suggest": {
|
|
"compression": [
|
|
"gzip1",
|
|
"gzip2",
|
|
"gzip3",
|
|
"gzip4",
|
|
"gzip5",
|
|
"gzip6",
|
|
"gzip7",
|
|
"gzip8",
|
|
"gzip9"
|
|
],
|
|
"mode": [
|
|
"client",
|
|
"server"
|
|
],
|
|
"...": [
|
|
"..."
|
|
]
|
|
}
|
|
}
|
|
|
|
|
|
:param server: Which server to collect data from when in multi-agent mode
|
|
:type server: str
|
|
|
|
:returns: The *JSON* described above.
|
|
"""
|
|
try:
|
|
conf = unquote(conf)
|
|
except:
|
|
pass
|
|
res = bui.client.read_conf_srv(conf, server)
|
|
refresh()
|
|
# Translate the doc and placeholder API side
|
|
cache_keys = {
|
|
'doc': '_doc_parser_{}-{}'.format(server, g.locale),
|
|
'placeholders': '_placeholders_parser_{}-{}'.format(server, g.locale),
|
|
'boolean_srv': '_boolean_srv_parser_{}'.format(server),
|
|
'string_srv': '_string_srv_parser_{}'.format(server),
|
|
'integer_srv': '_integer_srv_parser_{}'.format(server),
|
|
'multi_srv': '_multi_srv_parser_{}'.format(server),
|
|
'values': '_suggest_parser_{}'.format(server),
|
|
'defaults': '_defaults_parser_{}'.format(server),
|
|
'advanced_type': '_advanced_parser_{}'.format(server),
|
|
'pair_associations': '_pair_associations_parser_{}'.format(server),
|
|
}
|
|
cache_results = {}
|
|
for name, key in cache_keys.items():
|
|
if not cache.cache.has(key):
|
|
if name in ['doc', 'placeholders']:
|
|
_tmp = bui.client.get_parser_attr(name, server).copy()
|
|
_tmp2 = {}
|
|
for k, v in _tmp.items():
|
|
_tmp2[k] = _(v)
|
|
cache_results[name] = _tmp2
|
|
else:
|
|
cache_results[name] = bui.client.get_parser_attr(name, server)
|
|
cache.cache.set(key, cache_results[name], 3600)
|
|
else:
|
|
cache_results[name] = cache.cache.get(key)
|
|
|
|
return jsonify(results=res,
|
|
boolean=cache_results['boolean_srv'],
|
|
string=cache_results['string_srv'],
|
|
integer=cache_results['integer_srv'],
|
|
multi=cache_results['multi_srv'],
|
|
pair=cache_results['pair_associations'],
|
|
advanced=cache_results['advanced_type'],
|
|
server_doc=cache_results['doc'],
|
|
suggest=cache_results['values'],
|
|
placeholders=cache_results['placeholders'],
|
|
defaults=cache_results['defaults'])
|
|
|
|
|
|
@ns.route('/clients',
|
|
'/<server>/clients',
|
|
endpoint='clients_list')
|
|
@ns.doc(
|
|
params={
|
|
'server': 'Which server to collect data from when in multi-agent mode',
|
|
},
|
|
)
|
|
class ClientsList(Resource):
|
|
|
|
@api.acl_admin_or_moderator_required(message='Sorry, you don\'t have rights to access the setting panel')
|
|
@ns.doc(
|
|
responses={
|
|
200: 'Success',
|
|
403: 'Insufficient permissions',
|
|
500: 'Internal failure',
|
|
}
|
|
)
|
|
def get(self, server=None):
|
|
"""Returns a list of clients"""
|
|
parser = bui.client.get_parser(agent=server)
|
|
res = parser.list_clients()
|
|
return jsonify(result=res)
|
|
|
|
|
|
@ns.route('/static-templates',
|
|
'/<server>/static-templates',
|
|
endpoint='static_templates_list')
|
|
@ns.doc(
|
|
params={
|
|
'server': 'Which server to collect data from when in multi-agent mode',
|
|
},
|
|
)
|
|
class StaticTemplatesList(Resource):
|
|
|
|
@api.acl_admin_or_moderator_required(message='Sorry, you don\'t have rights to access the setting panel')
|
|
@ns.doc(
|
|
responses={
|
|
200: 'Success',
|
|
403: 'Insufficient permissions',
|
|
500: 'Internal failure',
|
|
}
|
|
)
|
|
def get(self, server=None):
|
|
"""Returns a list of clients"""
|
|
parser = bui.client.get_parser(agent=server)
|
|
res = parser.list_static_templates()
|
|
env = Environment()
|
|
for obj in res:
|
|
ast = env.parse(obj['content'])
|
|
obj['variables'] = [x for x in meta.find_undeclared_variables(ast) if x not in TEMPLATE_EXCLUDES]
|
|
return jsonify(result=res)
|
|
|
|
|
|
@ns.route('/static-template',
|
|
'/<server>/static-template',
|
|
endpoint='new_static_template',
|
|
methods=['PUT'])
|
|
@ns.doc(
|
|
params={
|
|
'server': 'Which server to collect data from when in multi-agent mode',
|
|
},
|
|
)
|
|
class NewStaticTemplateSettings(Resource):
|
|
parser = ns.parser()
|
|
parser.add_argument('newstatictemplate', required=True, help="No 'newstatictemplate' provided")
|
|
|
|
@api.disabled_on_demo()
|
|
@api.acl_admin_or_moderator_required(message='Sorry, you don\'t have rights to access the setting panel')
|
|
@ns.expect(parser)
|
|
@ns.doc(
|
|
responses={
|
|
200: 'Success',
|
|
400: 'Missing parameter',
|
|
403: 'Insufficient permissions',
|
|
500: 'Internal failure',
|
|
}
|
|
)
|
|
def put(self, server=None):
|
|
"""Creates a new template"""
|
|
if not current_user.is_anonymous and \
|
|
current_user.acl.is_moderator() and \
|
|
not current_user.acl.is_server_rw(server):
|
|
self.abort(403, 'You don\'t have rights on this server')
|
|
|
|
newtemplate = self.parser.parse_args()['newstatictemplate']
|
|
if not newtemplate:
|
|
self.abort(400, 'No template name provided')
|
|
parser = bui.client.get_parser(agent=server)
|
|
templates = parser.list_static_templates()
|
|
if any(tpl['name'] == newtemplate for tpl in templates):
|
|
self.abort(409, "Static template '{}' already exists".format(newtemplate))
|
|
# clientconfdir = bui.client.get_parser_attr('clientconfdir', server)
|
|
# if not clientconfdir:
|
|
# flash('Could not proceed, no \'clientconfdir\' find', 'warning')
|
|
# return redirect(request.referrer)
|
|
noti = bui.client.store_conf_cli(ImmutableMultiDict(), newtemplate, None, False, True, server)
|
|
if server:
|
|
url = url_for('view.cli_settings', server=server, client=newtemplate, statictemplate=True)
|
|
else:
|
|
url = url_for('view.cli_settings', client=newtemplate, statictemplate=True)
|
|
noti.append([NOTIF_INFO, _('<a href="%(url)s">Click here</a> to edit \'%(template)s\' configuration', url=url, template=newtemplate)])
|
|
# clear the cache when we add a new client
|
|
cache.clear()
|
|
bui.audit.logger.info(f'created new static template {newtemplate}', server=server)
|
|
return {'notif': noti}, 201
|
|
|
|
|
|
@ns.route('/templates',
|
|
'/<server>/templates',
|
|
endpoint='templates_list')
|
|
@ns.doc(
|
|
params={
|
|
'server': 'Which server to collect data from when in multi-agent mode',
|
|
},
|
|
)
|
|
class TemplatesList(Resource):
|
|
|
|
@api.acl_admin_or_moderator_required(message='Sorry, you don\'t have rights to access the setting panel')
|
|
@ns.doc(
|
|
responses={
|
|
200: 'Success',
|
|
403: 'Insufficient permissions',
|
|
500: 'Internal failure',
|
|
}
|
|
)
|
|
def get(self, server=None):
|
|
"""Returns a list of clients"""
|
|
parser = bui.client.get_parser(agent=server)
|
|
res = parser.list_templates()
|
|
return jsonify(result=res)
|
|
|
|
|
|
@ns.route('/template',
|
|
'/<server>/template',
|
|
endpoint='new_template',
|
|
methods=['PUT'])
|
|
@ns.doc(
|
|
params={
|
|
'server': 'Which server to collect data from when in multi-agent mode',
|
|
},
|
|
)
|
|
class NewTemplateSettings(Resource):
|
|
parser = ns.parser()
|
|
parser.add_argument('newtemplate', required=True, help="No 'newclient' provided")
|
|
|
|
@api.disabled_on_demo()
|
|
@api.acl_admin_or_moderator_required(message='Sorry, you don\'t have rights to access the setting panel')
|
|
@ns.expect(parser)
|
|
@ns.doc(
|
|
responses={
|
|
200: 'Success',
|
|
400: 'Missing parameter',
|
|
403: 'Insufficient permissions',
|
|
500: 'Internal failure',
|
|
}
|
|
)
|
|
def put(self, server=None):
|
|
"""Creates a new template"""
|
|
if not current_user.is_anonymous and \
|
|
current_user.acl.is_moderator() and \
|
|
not current_user.acl.is_server_rw(server):
|
|
self.abort(403, 'You don\'t have rights on this server')
|
|
|
|
newtemplate = self.parser.parse_args()['newtemplate']
|
|
if not newtemplate:
|
|
self.abort(400, 'No template name provided')
|
|
parser = bui.client.get_parser(agent=server)
|
|
templates = parser.list_templates()
|
|
for tpl in templates:
|
|
if tpl['name'] == newtemplate:
|
|
self.abort(409, "Template '{}' already exists".format(newtemplate))
|
|
# clientconfdir = bui.client.get_parser_attr('clientconfdir', server)
|
|
# if not clientconfdir:
|
|
# flash('Could not proceed, no \'clientconfdir\' find', 'warning')
|
|
# return redirect(request.referrer)
|
|
noti = bui.client.store_conf_cli(ImmutableMultiDict(), newtemplate, None, True, False, server)
|
|
if server:
|
|
url = url_for('view.cli_settings', server=server, client=newtemplate, template=True)
|
|
else:
|
|
url = url_for('view.cli_settings', client=newtemplate, template=True)
|
|
noti.append([NOTIF_INFO, _('<a href="%(url)s">Click here</a> to edit \'%(template)s\' configuration', url=url, template=newtemplate)])
|
|
# clear the cache when we add a new client
|
|
cache.clear()
|
|
bui.audit.logger.info(f'created new template {newtemplate}', server=server)
|
|
return {'notif': noti}, 201
|
|
|
|
|
|
@ns.route('/config',
|
|
'/<server>/config',
|
|
endpoint='new_client',
|
|
methods=['PUT'])
|
|
@ns.doc(
|
|
params={
|
|
'server': 'Which server to collect data from when in multi-agent mode',
|
|
},
|
|
)
|
|
class NewClientSettings(Resource):
|
|
parser = ns.parser()
|
|
parser.add_argument('newclient', required=True, help="No 'newclient' provided")
|
|
parser.add_argument('templates', help="Templates list", action='split')
|
|
parser.add_argument('statictemplate', help="Static template")
|
|
parser.add_argument('variables', help="Template variables")
|
|
|
|
@api.disabled_on_demo()
|
|
@api.acl_admin_or_moderator_required(message='Sorry, you don\'t have rights to access the setting panel')
|
|
@ns.expect(parser)
|
|
@ns.doc(
|
|
responses={
|
|
200: 'Success',
|
|
400: 'Missing parameter',
|
|
403: 'Insufficient permissions',
|
|
500: 'Internal failure',
|
|
}
|
|
)
|
|
def put(self, server=None):
|
|
"""Creates a new client"""
|
|
args = self.parser.parse_args()
|
|
newclient = args['newclient']
|
|
templates = [x for x in args.get('templates', []) if x]
|
|
statictemplate = args['statictemplate']
|
|
variables = json.loads(args['variables']) if args['variables'] else {}
|
|
variables['agent'] = server
|
|
variables['client'] = newclient
|
|
if not newclient:
|
|
self.abort(400, 'No client name provided')
|
|
|
|
if not current_user.is_anonymous and \
|
|
current_user.acl.is_moderator() and \
|
|
not current_user.acl.is_client_rw(newclient, server):
|
|
self.abort(403, 'You don\'t have rights on this server')
|
|
|
|
parser = bui.client.get_parser(agent=server)
|
|
clients = parser.list_clients()
|
|
for cl in clients:
|
|
if cl['name'] == newclient:
|
|
self.abort(409, "Client '{}' already exists".format(newclient))
|
|
# clientconfdir = bui.client.get_parser_attr('clientconfdir', server)
|
|
# if not clientconfdir:
|
|
# flash('Could not proceed, no \'clientconfdir\' find', 'warning')
|
|
# return redirect(request.referrer)
|
|
data = MultiDict()
|
|
content = ''
|
|
if templates:
|
|
real_templates = {x['name']: x['value'] for x in parser._list_templates()}
|
|
if any(x not in real_templates for x in templates):
|
|
self.abort(400, 'Wrong template')
|
|
data.setlist('templates', [real_templates[x] for x in templates])
|
|
if statictemplate:
|
|
statics = parser._list_static_templates()
|
|
for tpl in statics:
|
|
if tpl['name'] == statictemplate:
|
|
content = render_template_string(tpl['content'], **variables)
|
|
noti = bui.client.store_conf_cli(ImmutableMultiDict(data), newclient, None, content=content, agent=server)
|
|
if server:
|
|
url = url_for('view.cli_settings', server=server, client=newclient)
|
|
else:
|
|
url = url_for('view.cli_settings', client=newclient)
|
|
noti.append([NOTIF_INFO, _('<a href="%(url)s">Click here</a> to edit \'%(client)s\' configuration', url=url, client=newclient)])
|
|
# clear the cache when we add a new client
|
|
cache.clear()
|
|
# clear client-side cache through the _extra META variable
|
|
try:
|
|
_extra = session.get('_extra', g.now)
|
|
_extra = int(_extra)
|
|
except ValueError:
|
|
_extra = 0
|
|
session['_extra'] = '{}'.format(_extra + 1)
|
|
if bui.config['WITH_CELERY']:
|
|
from ..tasks import force_scheduling_now
|
|
force_scheduling_now()
|
|
|
|
bui.audit.logger.info(f'created new client configuration {newclient}', server=server)
|
|
return {'notif': noti}, 201
|
|
|
|
|
|
@ns.route('/config/<client>',
|
|
'/config/<client>/<path:conf>',
|
|
'/<server>/config/<client>',
|
|
'/<server>/config/<client>/<path:conf>',
|
|
endpoint='client_settings',
|
|
methods=['GET', 'POST', 'PUT', 'DELETE'])
|
|
@ns.doc(
|
|
params={
|
|
'server': 'Which server to collect data from when in multi-agent mode',
|
|
'client': 'Client name',
|
|
'conf': 'Path of the configuration file',
|
|
},
|
|
)
|
|
class ClientSettings(Resource):
|
|
parser_delete = ns.parser()
|
|
parser_delete.add_argument('revoke', type=inputs.boolean, help='Whether to revoke the certificate or not', default=False, nullable=True)
|
|
parser_delete.add_argument('delcert', type=inputs.boolean, help='Whether to delete the certificate or not', default=False, nullable=True)
|
|
parser_delete.add_argument('keepconf', type=inputs.boolean, help='Whether to keep the conf or not', default=False, nullable=True)
|
|
parser_delete.add_argument('template', type=inputs.boolean, help='Whether we work on a template or not', default=False, nullable=True)
|
|
parser_delete.add_argument('statictemplate', type=inputs.boolean, help='Whether we work on a static template or not', default=False, nullable=True)
|
|
parser_delete.add_argument('delete', type=inputs.boolean, help='Whether we should remove the data as well or not', default=False, nullable=True)
|
|
parser_put = ns.parser()
|
|
parser_put.add_argument('newname', help='New name of the client/template')
|
|
parser_put.add_argument('template', type=inputs.boolean, help='Whether we work on a template or not', default=False, nullable=True)
|
|
parser_put.add_argument('statictemplate', type=inputs.boolean, help='Whether we work on a static template or not', default=False, nullable=True)
|
|
parser_put.add_argument('keepcert', type=inputs.boolean, help='Whether to keep the same certificate or not', default=False, nullable=True)
|
|
parser_put.add_argument('keepdata', type=inputs.boolean, help='Whether to keep the data or not', default=False, nullable=True)
|
|
parser_post = ns.parser()
|
|
parser_post.add_argument('template', type=inputs.boolean, help='Whether we work on a template or not', default=False, nullable=True)
|
|
parser_post.add_argument('statictemplate', type=inputs.boolean, help='Whether we work on a static template or not', default=False, nullable=True)
|
|
parser_get = ns.parser()
|
|
parser_get.add_argument('template', type=inputs.boolean, help='Whether we work on a template or not', default=False, nullable=True)
|
|
parser_get.add_argument('statictemplate', type=inputs.boolean, help='Whether we work on a static template or not', default=False, nullable=True)
|
|
|
|
@api.disabled_on_demo()
|
|
@api.acl_admin_or_moderator_required(message=_('Sorry, you don\'t have rights to access the setting panel'))
|
|
@ns.expect(parser_post)
|
|
@ns.doc(
|
|
responses={
|
|
200: 'Success',
|
|
403: 'Insufficient permissions',
|
|
500: 'Internal failure',
|
|
}
|
|
)
|
|
def post(self, server=None, client=None, conf=None):
|
|
"""Saves a given client configuration"""
|
|
if not current_user.is_anonymous and \
|
|
current_user.acl.is_moderator() and \
|
|
not current_user.acl.is_client_rw(client, server):
|
|
self.abort(403, 'You don\'t have rights on this server')
|
|
|
|
args = self.parser_post.parse_args()
|
|
template = args.get('template', False)
|
|
statictemplate = args.get('statictemplate', False)
|
|
noti = bui.client.store_conf_cli(request.form, client, conf, template, statictemplate, server)
|
|
# clear cache
|
|
cache.clear()
|
|
# clear client-side cache through the _extra META variable
|
|
try:
|
|
_extra = session.get('_extra', g.now)
|
|
_extra = int(_extra)
|
|
except ValueError:
|
|
_extra = 0
|
|
session['_extra'] = '{}'.format(_extra + 1)
|
|
|
|
bui.audit.logger.info(f'updated client configuration {client} ({conf})', server=server)
|
|
return {'notif': noti}
|
|
|
|
@api.acl_admin_or_moderator_required(message=_('Sorry, you don\'t have rights to access the setting panel'))
|
|
@ns.expect(parser_get)
|
|
@ns.doc(
|
|
responses={
|
|
200: 'Success',
|
|
403: 'Insufficient permissions',
|
|
500: 'Internal failure',
|
|
}
|
|
)
|
|
def get(self, server=None, client=None, conf=None):
|
|
"""Reads a given client configuration"""
|
|
try:
|
|
conf = unquote(conf)
|
|
except:
|
|
pass
|
|
args = self.parser_get.parse_args()
|
|
template = args.get('template', False)
|
|
statictemplate = args.get('statictemplate', False)
|
|
parser = bui.client.get_parser(agent=server)
|
|
res = parser.read_client_conf(client, conf, template, statictemplate)
|
|
refresh()
|
|
# Translate the doc and placeholder API side
|
|
cache_keys = {
|
|
'doc': '_doc_parser_{}-{}'.format(server, g.locale),
|
|
'placeholders': '_placeholders_parser_{}-{}'.format(server, g.locale),
|
|
'boolean_cli': '_boolean_cli_parser_{}'.format(server),
|
|
'string_cli': '_string_cli_parser_{}'.format(server),
|
|
'integer_cli': '_integer_cli_parser_{}'.format(server),
|
|
'multi_cli': '_multi_cli_parser_{}'.format(server),
|
|
'values': '_suggest_parser_{}'.format(server),
|
|
'defaults': '_defaults_parser_{}'.format(server),
|
|
}
|
|
cache_results = {}
|
|
for name, key in cache_keys.items():
|
|
if not cache.cache.has(key):
|
|
if name in ['doc', 'placeholders']:
|
|
_tmp = bui.client.get_parser_attr(name, server).copy()
|
|
_tmp2 = {}
|
|
for k, v in _tmp.items():
|
|
_tmp2[k] = _(v)
|
|
cache_results[name] = _tmp2
|
|
else:
|
|
cache_results[name] = bui.client.get_parser_attr(name, server)
|
|
cache.cache.set(key, cache_results[name], 3600)
|
|
else:
|
|
cache_results[name] = cache.cache.get(key)
|
|
|
|
return jsonify(
|
|
results=res,
|
|
boolean=cache_results['boolean_cli'],
|
|
string=cache_results['string_cli'],
|
|
integer=cache_results['integer_cli'],
|
|
multi=cache_results['multi_cli'],
|
|
server_doc=cache_results['doc'],
|
|
suggest=cache_results['values'],
|
|
placeholders=cache_results['placeholders'],
|
|
defaults=cache_results['defaults']
|
|
)
|
|
|
|
@api.disabled_on_demo()
|
|
@api.acl_admin_or_moderator_required(message=_('Sorry, you don\'t have rights to access the setting panel'))
|
|
@ns.expect(parser_delete)
|
|
@ns.doc(
|
|
responses={
|
|
200: 'Success',
|
|
403: 'Insufficient permissions',
|
|
409: 'Conflict',
|
|
500: 'Internal failure',
|
|
}
|
|
)
|
|
def delete(self, server=None, client=None, conf=None):
|
|
"""Deletes a given client"""
|
|
if not current_user.is_anonymous and \
|
|
current_user.acl.is_moderator() and \
|
|
not current_user.acl.is_client_rw(client, server):
|
|
self.abort(403, 'You don\'t have rights on this server')
|
|
|
|
if bui.client.is_backup_running(client, server):
|
|
self.abort(409, 'There is currently a backup running for this client hence '
|
|
'we cannot delete it for now. Please try again later')
|
|
|
|
args = self.parser_delete.parse_args()
|
|
delcert = args.get('delcert', False)
|
|
revoke = args.get('revoke', False)
|
|
keepconf = args.get('keepconf', False)
|
|
template = args.get('template', False)
|
|
statictemplate = args.get('statictemplate', False)
|
|
delete = args.get('delete', False)
|
|
|
|
if not keepconf:
|
|
# clear the cache when we remove a client
|
|
cache.clear()
|
|
# clear client-side cache through the _extra META variable
|
|
try:
|
|
_extra = session.get('_extra', g.now)
|
|
_extra = int(_extra)
|
|
except ValueError:
|
|
_extra = 0
|
|
session['_extra'] = '{}'.format(_extra + 1)
|
|
if bui.config['WITH_CELERY']:
|
|
from ..tasks import force_scheduling_now
|
|
force_scheduling_now()
|
|
parser = bui.client.get_parser(agent=server)
|
|
|
|
bui.audit.logger.info(
|
|
f'deleted client configuration {client}, delete certificate: {delcert}, '
|
|
f'revoke certificate: {revoke}, keep a backup of the configuration: '
|
|
f'{keepconf}, delete data: {delete}, is template: {template} '
|
|
f'is static template: {statictemplate}', server=server)
|
|
return parser.remove_client(client, keepconf, delcert, revoke, template, statictemplate, delete), 200
|
|
|
|
@api.disabled_on_demo()
|
|
@api.acl_admin_or_moderator_required(message=_('Sorry, you don\'t have rights to access the setting panel'))
|
|
@ns.expect(parser_put)
|
|
@ns.doc(
|
|
responses={
|
|
200: 'Success',
|
|
403: 'Insufficient permissions',
|
|
409: 'Conflict',
|
|
500: 'Internal failure',
|
|
}
|
|
)
|
|
def put(self, server=None, client=None, conf=None):
|
|
"""Rename a given client"""
|
|
if not current_user.is_anonymous and \
|
|
current_user.acl.is_moderator() and \
|
|
not current_user.acl.is_client_rw(client, server):
|
|
self.abort(403, 'You don\'t have rights on this server')
|
|
|
|
if bui.client.is_backup_running(client, server):
|
|
self.abort(409, 'There is currently a backup running for this client hence '
|
|
'we cannot delete it for now. Please try again later')
|
|
|
|
args = self.parser_put.parse_args()
|
|
newname = args.get('newname', None)
|
|
keepcert = args.get('keepcert', False)
|
|
keepdata = args.get('keepdata', False)
|
|
template = args.get('template', False)
|
|
statictemplate = args.get('statictemplate', False)
|
|
|
|
# clear the cache when we remove a client
|
|
cache.clear()
|
|
# clear client-side cache through the _extra META variable
|
|
try:
|
|
_extra = session.get('_extra', g.now)
|
|
_extra = int(_extra)
|
|
except ValueError:
|
|
_extra = 0
|
|
session['_extra'] = '{}'.format(_extra + 1)
|
|
if bui.config['WITH_CELERY']:
|
|
from ..tasks import force_scheduling_now
|
|
force_scheduling_now()
|
|
parser = bui.client.get_parser(agent=server)
|
|
|
|
bui.audit.logger.info(
|
|
f'renaming client configuration {client} to {newname}, '
|
|
f'keep data: {keepdata}, keep certificate: {keepcert}, '
|
|
f'is template: {template}, is static template: {statictemplate}', server=server)
|
|
return parser.rename_client(client, newname, template, statictemplate, keepcert, keepdata), 200
|
|
|
|
|
|
@ns.route('/path-expander',
|
|
'/<server>/path-expander',
|
|
'/path-expander/<client>',
|
|
'/<server>/path-expander/<client>',
|
|
endpoint='path_expander')
|
|
@ns.doc(
|
|
params={
|
|
'server': 'Which server to collect data from when in multi-agent mode',
|
|
'client': 'Client name',
|
|
},
|
|
)
|
|
class PathExpander(Resource):
|
|
|
|
parser = ns.parser()
|
|
parser.add_argument('path', required=True, help="No 'path' provided")
|
|
parser.add_argument('source', required=False, help="Which file is it included in")
|
|
|
|
@api.acl_admin_or_moderator_required(message=_('Sorry, you don\'t have rights to access the setting panel'))
|
|
@ns.doc(
|
|
responses={
|
|
200: 'Success',
|
|
403: 'Insufficient permissions',
|
|
500: 'Internal failure',
|
|
}
|
|
)
|
|
def get(self, server=None, client=None):
|
|
"""Expends a given path
|
|
|
|
For instance if it's given a glob expression it will returns a list of
|
|
files matching the expression.
|
|
"""
|
|
args = self.parser.parse_args()
|
|
path = args['path']
|
|
source = args['source']
|
|
if path:
|
|
path = unquote(path)
|
|
if source:
|
|
source = unquote(source)
|
|
parser = bui.client.get_parser(agent=server)
|
|
paths = parser.path_expander(path, source, client)
|
|
if not paths:
|
|
self.abort(403, 'Path not found')
|
|
return {'result': paths}
|
|
|
|
|
|
@ns.route('/options',
|
|
'/<server>/options',
|
|
endpoint='setting_options')
|
|
@ns.doc(
|
|
params={
|
|
'server': 'Which server to collect data from when in multi-agent mode',
|
|
},
|
|
)
|
|
class SettingOptions(Resource):
|
|
|
|
@ns.doc(
|
|
responses={
|
|
200: 'Success',
|
|
403: 'Insufficient permissions',
|
|
500: 'Internal failure',
|
|
}
|
|
)
|
|
def get(self, server=None):
|
|
"""Returns various setting options"""
|
|
return {
|
|
'is_revocation_enabled': bui.client.revocation_enabled(server),
|
|
'server_can_restore': not bui.noserverrestore or bui.client.get_parser(agent=server).param('server_can_restore', 'client_conf'),
|
|
'batch_list_supported': bui.client.get_attr('batch_list_supported', False, server),
|
|
}
|