mirror of
https://github.com/pgadmin-org/pgadmin4.git
synced 2026-08-17 16:34:44 -05:00
Enforce tool RBAC on every backend route, not just the entry point
The tool permissions (tools_query_tool, tools_grant_wizard,
tools_schema_diff, tools_erd_tool, tools_psql_tool, tools_debugger) were
enforced only on a single "front door" route per tool, whilst the rest of
each tool's backend workflow relied on pga_login_required alone. An
authenticated user who had been denied a tool could therefore still drive
the tool through its other routes and Socket.IO handlers:
* Query Tool: View/Edit Data via sqleditor.initialize_viewdata and the
rest of the view-data chain, bypassing the gate on initialize_sqleditor.
* Grant Wizard: object discovery (objects), SQL preview (modified_sql)
and the actual privilege change (apply) were ungated; only acl_list
was protected. This allowed real GRANTs to be generated and applied.
* Schema Diff: initialize, servers, get_server, connect_server,
connect_database, databases, schemas, ddl_compare and the
compare_database/compare_schema socket handlers were ungated; only
panel was protected.
* ERD: initialize, prequisite, sql (table DDL generation) and the
tables socket handler were ungated; only panel was protected.
* PSQL: the panel route and the entire /pty Socket.IO namespace
(start_process, socket_input, socket_set_role, resize) had no tool
permission check at all, so a denied user could still obtain an
interactive psql session.
* Debugger: the directly addressable get_arguments/set_arguments/
clear_arguments routes were ungated.
Apply permissions_required to the HTTP routes and socket_permissions_required
to the Socket.IO handlers so the tool permission is enforced consistently
across each tool's surface. The permission check is the outermost
decorator, so it runs before any connection or transaction lookup.
This commit is contained in:
@@ -1786,6 +1786,7 @@ def select_frame(trans_id, frame_id):
|
||||
'/get_arguments/<int:sid>/<int:did>/<int:scid>/<int:func_id>',
|
||||
methods=['GET'], endpoint='get_arguments'
|
||||
)
|
||||
@permissions_required(AllPermissionTypes.tools_debugger)
|
||||
@pga_login_required
|
||||
def get_arguments_sqlite(sid, did, scid, func_id):
|
||||
"""
|
||||
@@ -1879,6 +1880,7 @@ def get_array_string(data, i):
|
||||
'/set_arguments/<int:sid>/<int:did>/<int:scid>/<int:func_id>',
|
||||
methods=['POST'], endpoint='set_arguments'
|
||||
)
|
||||
@permissions_required(AllPermissionTypes.tools_debugger)
|
||||
@pga_login_required
|
||||
def set_arguments_sqlite(sid, did, scid, func_id):
|
||||
"""
|
||||
@@ -1977,6 +1979,7 @@ def set_arguments_sqlite(sid, did, scid, func_id):
|
||||
'/clear_arguments/<int:sid>/<int:did>/<int:scid>/<int:func_id>',
|
||||
methods=['POST'], endpoint='clear_arguments'
|
||||
)
|
||||
@permissions_required(AllPermissionTypes.tools_debugger)
|
||||
@pga_login_required
|
||||
def clear_arguments_sqlite(sid, did, scid, func_id):
|
||||
"""
|
||||
|
||||
@@ -32,7 +32,7 @@ from pgadmin.utils.constants import PREF_LABEL_KEYBOARD_SHORTCUTS, \
|
||||
PREF_LABEL_OPTIONS
|
||||
from .utils import ERDHelper
|
||||
from pgadmin.utils.exception import ConnectionLost
|
||||
from pgadmin.authenticate import socket_login_required
|
||||
from pgadmin.authenticate import socket_permissions_required
|
||||
from pgadmin.tools.user_management.PgAdminPermissions import AllPermissionTypes
|
||||
from ... import socketio
|
||||
|
||||
@@ -585,6 +585,7 @@ def panel(trans_id):
|
||||
'/initialize/<int:trans_id>/<int:sgid>/<int:sid>/<int:did>',
|
||||
methods=["POST"], endpoint='initialize'
|
||||
)
|
||||
@permissions_required(AllPermissionTypes.tools_erd_tool)
|
||||
@pga_login_required
|
||||
def initialize_erd(trans_id, sgid, sid, did):
|
||||
"""
|
||||
@@ -659,6 +660,7 @@ def _get_connection(sid, did, trans_id, db_name=None):
|
||||
@blueprint.route('/prequisite/<int:trans_id>/<int:sgid>/<int:sid>/<int:did>',
|
||||
methods=["GET"],
|
||||
endpoint='prequisite')
|
||||
@permissions_required(AllPermissionTypes.tools_erd_tool)
|
||||
@pga_login_required
|
||||
def prequisite(trans_id, sgid, sid, did):
|
||||
conn = _get_connection(sid, did, trans_id)
|
||||
@@ -722,6 +724,7 @@ def translate_foreign_keys(tab_fks, tab_data, all_nodes):
|
||||
@blueprint.route('/sql/<int:trans_id>/<int:sgid>/<int:sid>/<int:did>',
|
||||
methods=["POST"],
|
||||
endpoint='sql')
|
||||
@permissions_required(AllPermissionTypes.tools_erd_tool)
|
||||
@pga_login_required
|
||||
def sql(trans_id, sgid, sid, did):
|
||||
data = json.loads(request.data)
|
||||
@@ -776,7 +779,7 @@ def connect():
|
||||
|
||||
|
||||
@socketio.on('tables', namespace=SOCKETIO_NAMESPACE)
|
||||
@socket_login_required
|
||||
@socket_permissions_required(AllPermissionTypes.tools_erd_tool)
|
||||
def tables(params):
|
||||
try:
|
||||
helper = ERDHelper(params['trans_id'], params['sid'], params['did'])
|
||||
|
||||
@@ -248,6 +248,7 @@ def get_node_sql_with_type(node_id, node_type, server_prop,
|
||||
'/<int:sid>/<int:did>/<int:node_id>/<node_type>/',
|
||||
methods=['GET'], endpoint='objects'
|
||||
)
|
||||
@permissions_required(AllPermissionTypes.tools_grant_wizard)
|
||||
@pga_login_required
|
||||
@check_precondition
|
||||
def properties(sid, did, node_id, node_type):
|
||||
@@ -404,6 +405,7 @@ def set_priv_for_package(server_prop, data, acls):
|
||||
'/sql/<int:sid>/<int:did>/',
|
||||
methods=['POST'], endpoint='modified_sql'
|
||||
)
|
||||
@permissions_required(AllPermissionTypes.tools_grant_wizard)
|
||||
@pga_login_required
|
||||
@check_precondition
|
||||
def msql(sid, did):
|
||||
@@ -538,6 +540,7 @@ def parse_priv(data, acls, server_prop):
|
||||
@blueprint.route(
|
||||
'/<int:sid>/<int:did>/', methods=['POST'], endpoint='apply'
|
||||
)
|
||||
@permissions_required(AllPermissionTypes.tools_grant_wizard)
|
||||
@pga_login_required
|
||||
@check_precondition
|
||||
def save(sid, did):
|
||||
|
||||
@@ -20,16 +20,18 @@ from flask import Response, request
|
||||
from flask import render_template, copy_current_request_context, \
|
||||
current_app as app
|
||||
from flask_babel import gettext
|
||||
from flask_security import current_user
|
||||
from flask_security import current_user, permissions_required
|
||||
from pgadmin.user_login_check import pga_login_required
|
||||
from pgadmin.browser.utils import underscore_unescape, underscore_escape
|
||||
from pgadmin.utils import PgAdminModule
|
||||
from pgadmin.utils.driver import get_driver
|
||||
from ... import socketio as sio
|
||||
from pgadmin.utils import get_complete_file_path
|
||||
from pgadmin.authenticate import socket_login_required
|
||||
from pgadmin.authenticate import socket_login_required, \
|
||||
socket_permissions_required
|
||||
from pgadmin.model import Server
|
||||
from pgadmin.utils.server_access import get_server
|
||||
from pgadmin.tools.user_management.PgAdminPermissions import AllPermissionTypes
|
||||
|
||||
if _platform == 'win32':
|
||||
# Check Windows platform support for WinPty api, Disable psql
|
||||
@@ -76,6 +78,7 @@ blueprint = PSQLModule('psql', __name__, static_url_path='/static')
|
||||
@blueprint.route('/panel/<int:trans_id>',
|
||||
methods=["POST"],
|
||||
endpoint="panel")
|
||||
@permissions_required(AllPermissionTypes.tools_psql_tool)
|
||||
@pga_login_required
|
||||
def panel(trans_id):
|
||||
"""
|
||||
@@ -292,7 +295,7 @@ def pty_handel_io(connection_data, data, sid):
|
||||
|
||||
|
||||
@sio.on('start_process', namespace='/pty')
|
||||
@socket_login_required
|
||||
@socket_permissions_required(AllPermissionTypes.tools_psql_tool)
|
||||
def start_process(data):
|
||||
"""
|
||||
Start the pty terminal and execute psql command and emit results to user.
|
||||
@@ -475,6 +478,7 @@ def other_key_press(data):
|
||||
|
||||
|
||||
@sio.on('socket_input', namespace='/pty')
|
||||
@socket_permissions_required(AllPermissionTypes.tools_psql_tool)
|
||||
def socket_input(data):
|
||||
"""
|
||||
This get the user input through socket.
|
||||
@@ -503,6 +507,7 @@ def socket_input(data):
|
||||
|
||||
|
||||
@sio.on('socket_set_role', namespace='/pty')
|
||||
@socket_permissions_required(AllPermissionTypes.tools_psql_tool)
|
||||
def socket_set_role(data):
|
||||
"""
|
||||
This function sets the role used to connect to server.
|
||||
@@ -537,6 +542,7 @@ def socket_set_role(data):
|
||||
|
||||
|
||||
@sio.on('resize', namespace='/pty')
|
||||
@socket_permissions_required(AllPermissionTypes.tools_psql_tool)
|
||||
def resize(data):
|
||||
"""
|
||||
Resize the pty terminal as per the UI terminal.
|
||||
|
||||
@@ -28,7 +28,7 @@ from pgadmin.utils.driver import get_driver
|
||||
from pgadmin.utils.constants import PREF_LABEL_DISPLAY, \
|
||||
ERROR_MSG_TRANS_ID_NOT_FOUND
|
||||
from sqlalchemy import or_
|
||||
from pgadmin.authenticate import socket_login_required
|
||||
from pgadmin.authenticate import socket_permissions_required
|
||||
from pgadmin import socketio
|
||||
from pgadmin.tools.user_management.PgAdminPermissions import AllPermissionTypes
|
||||
from pgadmin.utils.server_access import \
|
||||
@@ -207,6 +207,7 @@ def update_session_diff_transaction(trans_id, session_obj, diff_model_obj):
|
||||
methods=["GET"],
|
||||
endpoint="initialize"
|
||||
)
|
||||
@permissions_required(AllPermissionTypes.tools_schema_diff)
|
||||
@pga_login_required
|
||||
def initialize(trans_id):
|
||||
"""
|
||||
@@ -270,6 +271,7 @@ def close(trans_id):
|
||||
methods=["GET"],
|
||||
endpoint="servers"
|
||||
)
|
||||
@permissions_required(AllPermissionTypes.tools_schema_diff)
|
||||
@pga_login_required
|
||||
def servers():
|
||||
"""
|
||||
@@ -323,6 +325,7 @@ def servers():
|
||||
methods=["GET"],
|
||||
endpoint="get_server"
|
||||
)
|
||||
@permissions_required(AllPermissionTypes.tools_schema_diff)
|
||||
@pga_login_required
|
||||
def get_server(sid, did):
|
||||
"""
|
||||
@@ -366,6 +369,7 @@ def get_server(sid, did):
|
||||
methods=["POST"],
|
||||
endpoint="connect_server"
|
||||
)
|
||||
@permissions_required(AllPermissionTypes.tools_schema_diff)
|
||||
@pga_login_required
|
||||
def connect_server(sid):
|
||||
# Check if server is already connected then no need to reconnect again.
|
||||
@@ -394,6 +398,7 @@ def connect_server(sid):
|
||||
methods=["POST"],
|
||||
endpoint="connect_database"
|
||||
)
|
||||
@permissions_required(AllPermissionTypes.tools_schema_diff)
|
||||
@pga_login_required
|
||||
def connect_database(sid, did):
|
||||
server = get_server_access(sid)
|
||||
@@ -411,6 +416,7 @@ def connect_database(sid, did):
|
||||
methods=["GET"],
|
||||
endpoint="databases"
|
||||
)
|
||||
@permissions_required(AllPermissionTypes.tools_schema_diff)
|
||||
@pga_login_required
|
||||
def databases(sid):
|
||||
"""
|
||||
@@ -454,6 +460,7 @@ def databases(sid):
|
||||
methods=["GET"],
|
||||
endpoint="schemas"
|
||||
)
|
||||
@permissions_required(AllPermissionTypes.tools_schema_diff)
|
||||
@pga_login_required
|
||||
def schemas(sid, did):
|
||||
"""
|
||||
@@ -478,7 +485,7 @@ def schemas(sid, did):
|
||||
|
||||
|
||||
@socketio.on('compare_database', namespace=SOCKETIO_NAMESPACE)
|
||||
@socket_login_required
|
||||
@socket_permissions_required(AllPermissionTypes.tools_schema_diff)
|
||||
def compare_database(params):
|
||||
"""
|
||||
This function will compare the two databases.
|
||||
@@ -637,7 +644,7 @@ def compare_database(params):
|
||||
|
||||
|
||||
@socketio.on('compare_schema', namespace=SOCKETIO_NAMESPACE)
|
||||
@socket_login_required
|
||||
@socket_permissions_required(AllPermissionTypes.tools_schema_diff)
|
||||
def compare_schema(params):
|
||||
"""
|
||||
This function will compare the two schema.
|
||||
@@ -707,6 +714,7 @@ def compare_schema(params):
|
||||
methods=["GET"],
|
||||
endpoint="ddl_compare"
|
||||
)
|
||||
@permissions_required(AllPermissionTypes.tools_schema_diff)
|
||||
@pga_login_required
|
||||
def ddl_compare(trans_id, source_sid, source_did, source_scid,
|
||||
target_sid, target_did, target_scid, source_oid,
|
||||
|
||||
@@ -193,6 +193,7 @@ def index():
|
||||
methods=["PUT", "POST"],
|
||||
endpoint="initialize_viewdata"
|
||||
)
|
||||
@permissions_required(AllPermissionTypes.tools_query_tool)
|
||||
@pga_login_required
|
||||
def initialize_viewdata(trans_id, cmd_type, obj_type, sgid, sid, did, obj_id):
|
||||
"""
|
||||
|
||||
Reference in New Issue
Block a user