残留批次A:成员删除、PR needs_local_worker 落库、推送 DNS TOCTOU
- 成员删除:DELETE /projects/{id}/members/{user_id},限 owner 或
全局管理员,禁删唯一 owner,记 remove_member 审计
(AuthStore.remove_member + server 分发,沿用 PATCH 守卫口径)
- PR needs_local_worker 落库:非 local 扫到 queued 任务时 CAS
写行状态为 needs_local_worker;claim/find_open 同步认该状态,
标记过的行仍可被 local worker 认领、不被搁置;另修
claim/find_open 对裸 sqlite3 tuple 行的 dict() 兜底
(TypeError/ValueError)
- 推送 DNS TOCTOU:发送改走建连时解析+判段的连接类,解析
一次、只拨判过的 IP,Host/SNI 仍为原域名;opener 直接构造
以排除 ProxyHandler 改写(build_opener 默认会塞代理)
This commit is contained in:
1 parent
6e3eb7ba11
commit
9bb9a50e4e
10 files changed
+725
-38
No files matched your search
+51
-1
@@ -2471,6 +2471,54 @@ class CloudControlPlaneHandler(BaseHTTPRequestHandler):
|
||||
self._send_json(200, {"project_id": project_id,
|
||||
"user_id": target, "role": role})
|
||||
|
||||
def _project_member_delete(self, user_id, project_id, target):
|
||||
"""DELETE /projects/{id}/members/{user_id}:限项目 owner
|
||||
或全局管理员。删唯一 owner 时 409(否则项目将无 owner
|
||||
可管);目标非成员时 404。成功记 remove_member 审计。"""
|
||||
if not self._project_exists(project_id):
|
||||
self._send_json(404, {"error": "project not found"})
|
||||
return
|
||||
if not self._can_manage_members(user_id, project_id):
|
||||
self._send_json(403, {"error": "forbidden"})
|
||||
return
|
||||
with _auth_store(self.server.db_path) as store:
|
||||
cur = store.conn.execute(
|
||||
"SELECT role FROM project_members"
|
||||
" WHERE project_id = ? AND user_id = ?",
|
||||
(project_id, target)).fetchone()
|
||||
if cur is None:
|
||||
self._send_json(404, {"error": "member not found"})
|
||||
return
|
||||
if cur[0] == "owner":
|
||||
owners = store.conn.execute(
|
||||
"SELECT COUNT(*) FROM project_members"
|
||||
" WHERE project_id = ? AND role = 'owner'",
|
||||
(project_id,)).fetchone()[0]
|
||||
if owners <= 1:
|
||||
self._send_json(
|
||||
409,
|
||||
{"error": "cannot remove the only owner"})
|
||||
return
|
||||
self._remove_member_row(store, project_id, user_id, target)
|
||||
self._send_json(200, {"project_id": project_id,
|
||||
"user_id": target})
|
||||
|
||||
def _remove_member_row(self, store, project_id, actor_id, target_id):
|
||||
"""删成员行并记审计:owner 走 AuthStore.remove_member
|
||||
(内含 owner 校验与审计);非成员的全局管理员走直接删
|
||||
+ 同格式审计(remove_member 的 owner 校验会挡住全局
|
||||
管理员,故分流,与 _set_member_row 同款)。"""
|
||||
if store.check_permission(actor_id, project_id,
|
||||
"manage_project"):
|
||||
store.remove_member(project_id, actor_id, target_id)
|
||||
return
|
||||
store.conn.execute(
|
||||
"DELETE FROM project_members"
|
||||
" WHERE project_id = ? AND user_id = ?",
|
||||
(project_id, target_id))
|
||||
store.conn.commit()
|
||||
store.audit(actor_id, project_id, "remove_member", target_id)
|
||||
|
||||
# -- 项目子资源:记忆 / 知识库 / 成本 -------------------------------
|
||||
def _project_exists(self, project_id):
|
||||
conn = self._db()
|
||||
@@ -2510,10 +2558,12 @@ class CloudControlPlaneHandler(BaseHTTPRequestHandler):
|
||||
else:
|
||||
self._send_json(404, {"error": "not found"})
|
||||
return
|
||||
# /projects/{id}/members/{user_id}:PATCH 改角色
|
||||
# /projects/{id}/members/{user_id}:PATCH 改角色 / DELETE 删成员
|
||||
if len(parts) == 3 and parts[1] == "members":
|
||||
if method == "PATCH":
|
||||
self._project_member_patch(user_id, project_id, parts[2])
|
||||
elif method == "DELETE":
|
||||
self._project_member_delete(user_id, project_id, parts[2])
|
||||
else:
|
||||
self._send_json(404, {"error": "not found"})
|
||||
return
|
||||
|
||||
Reference in new issue
Block a user