如何在Hasura中基于用户所属组织限制event表的行级插入与更新权限
限制用户仅操作所属组织的Event数据方案
先明确你的表结构:
event { id organization__id } user { id } organization { id } organization_user { user__id organization__id }
要实现用户只能插入/更新自己所属组织的event数据,推荐从应用层常规控制和数据库层兜底强制两个层面入手,避免单一环节出问题:
一、应用层控制(日常开发首选)
在业务代码里,每次执行插入或更新操作前,先做权限校验:
- 插入event时:拿到当前登录用户的ID,去
organization_user表里查,确认该用户和要插入的event对应的organization__id存在关联记录,只有校验通过才执行插入。 - 更新event时:先根据要更新的event ID,查出它关联的
organization__id,再检查当前用户是否属于这个组织,校验通过再执行更新。
举个通用伪代码示例:
# 插入事件的权限校验逻辑 def add_event(current_user_id, event_info): target_org_id = event_info['organization__id'] # 检查用户是否属于目标组织 is_authorized = db.execute("SELECT 1 FROM organization_user WHERE user__id = %s AND organization__id = %s", (current_user_id, target_org_id)).has_rows() if not is_authorized: raise PermissionError("你无权操作该组织的事件") # 校验通过,执行插入 db.insert("event", event_info) # 更新事件的权限校验逻辑 def edit_event(current_user_id, event_id, update_info): # 先获取要更新事件的组织ID event_org_id = db.execute("SELECT organization__id FROM event WHERE id = %s", (event_id,)).fetch_scalar() if not event_org_id: raise ValueError("该事件不存在") # 校验用户是否属于该组织 is_authorized = db.execute("SELECT 1 FROM organization_user WHERE user__id = %s AND organization__id = %s", (current_user_id, event_org_id)).has_rows() if not is_authorized: raise PermissionError("你无权操作该组织的事件") # 校验通过,执行更新 db.update("event", update_info, {"id": event_id})
二、数据库层面强制控制(兜底保障)
如果担心应用层代码有疏漏(比如忘了加校验),可以直接在数据库层面做强制限制,避免非法数据写入:
1. PostgreSQL 用行级安全策略(RLS)
PostgreSQL的行级安全是最方便的方式,直接给表加策略:
- 先开启
event表的行级安全:
ALTER TABLE event ENABLE ROW LEVEL SECURITY;
- 创建权限策略,只允许用户操作自己所属组织的event:
CREATE POLICY event_access_control ON event FOR ALL USING ( EXISTS ( SELECT 1 FROM organization_user WHERE organization_user.user__id = current_setting('app.current_user_id')::integer AND organization_user.organization__id = event.organization__id ) );
- 注意:应用每次连接数据库后,需要设置当前用户的ID到会话变量里,比如执行:
SET app.current_user_id = '当前登录用户的ID';
2. MySQL 用触发器实现
MySQL没有原生行级安全,可以用触发器在插入/更新前做校验:
- 插入前校验的触发器:
DELIMITER // CREATE TRIGGER check_event_insert_permission BEFORE INSERT ON event FOR EACH ROW BEGIN DECLARE auth_count INT; -- 这里假设你用会话变量传递当前用户ID,应用层先执行 SET @current_user_id = '用户ID'; SELECT COUNT(*) INTO auth_count FROM organization_user WHERE user__id = @current_user_id AND organization__id = NEW.organization__id; IF auth_count = 0 THEN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '无权操作该组织的事件'; END IF; END // DELIMITER ;
- 更新前校验的触发器:
DELIMITER // CREATE TRIGGER check_event_update_permission BEFORE UPDATE ON event FOR EACH ROW BEGIN DECLARE target_org_id INT; DECLARE auth_count INT; -- 先拿到要更新事件的组织ID SELECT organization__id INTO target_org_id FROM event WHERE id = OLD.id; -- 校验用户权限 SELECT COUNT(*) INTO auth_count FROM organization_user WHERE user__id = @current_user_id AND organization__id = target_org_id; IF auth_count = 0 THEN SIGNAL SQLSTATE '45000' SET MESSAGE_TEXT = '无权操作该组织的事件'; END IF; END // DELIMITER ;
内容的提问来源于stack exchange,提问作者Nicolas Bourdin
相关产品推荐
相关产品推荐

