lvwerra HF Staff commited on
Commit
d5d5a50
·
verified ·
1 Parent(s): 6f7504b

Deploy msgboard

Browse files
Files changed (2) hide show
  1. src/msgboard/db.py +17 -1
  2. src/msgboard/store.py +18 -2
src/msgboard/db.py CHANGED
@@ -458,10 +458,26 @@ CREATE INDEX messages_response_dm ON messages(channel_id, sender_id, created_at,
458
  CREATE INDEX attachments_published_at ON attachments(published, created_at);
459
  """
460
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
461
  # Index i holds the SQL that moves the schema from version i to i + 1.
462
  MIGRATIONS = [SCHEMA_V1, SCHEMA_V2, SCHEMA_V3, SCHEMA_V4, SCHEMA_V5, SCHEMA_V6, SCHEMA_V7, SCHEMA_V8, SCHEMA_V9,
463
  SCHEMA_V10, SCHEMA_V11, SCHEMA_V12, SCHEMA_V13, SCHEMA_V14, SCHEMA_V15, SCHEMA_V16, SCHEMA_V17, SCHEMA_V18, SCHEMA_V19, SCHEMA_V20, SCHEMA_V21, SCHEMA_V22,
464
- SCHEMA_V23, SCHEMA_V24, SCHEMA_V25]
465
  SCHEMA_VERSION = len(MIGRATIONS)
466
 
467
 
 
458
  CREATE INDEX attachments_published_at ON attachments(published, created_at);
459
  """
460
 
461
+ # Agents are owned by people: an agent that an agent invited moves to the person at the top of the chain.
462
+ SCHEMA_V26 = """
463
+ WITH RECURSIVE up(agent, cur, path) AS (
464
+ SELECT p.id, p.owner_id, ',' || p.id || ',' FROM principals p JOIN principals o ON o.id = p.owner_id
465
+ WHERE o.identity_type != 'user'
466
+ UNION ALL
467
+ SELECT u.agent, o.owner_id, u.path || o.id || ',' FROM up u JOIN principals o ON o.id = u.cur
468
+ WHERE o.identity_type != 'user' AND o.owner_id IS NOT NULL AND instr(u.path, ',' || o.id || ',') = 0
469
+ ),
470
+ root(agent, person) AS (
471
+ SELECT u.agent, u.cur FROM up u JOIN principals h ON h.id = u.cur WHERE h.identity_type = 'user'
472
+ )
473
+ UPDATE principals SET owner_id = (SELECT person FROM root WHERE root.agent = principals.id)
474
+ WHERE id IN (SELECT agent FROM root);
475
+ """
476
+
477
  # Index i holds the SQL that moves the schema from version i to i + 1.
478
  MIGRATIONS = [SCHEMA_V1, SCHEMA_V2, SCHEMA_V3, SCHEMA_V4, SCHEMA_V5, SCHEMA_V6, SCHEMA_V7, SCHEMA_V8, SCHEMA_V9,
479
  SCHEMA_V10, SCHEMA_V11, SCHEMA_V12, SCHEMA_V13, SCHEMA_V14, SCHEMA_V15, SCHEMA_V16, SCHEMA_V17, SCHEMA_V18, SCHEMA_V19, SCHEMA_V20, SCHEMA_V21, SCHEMA_V22,
480
+ SCHEMA_V23, SCHEMA_V24, SCHEMA_V25, SCHEMA_V26]
481
  SCHEMA_VERSION = len(MIGRATIONS)
482
 
483
 
src/msgboard/store.py CHANGED
@@ -1061,6 +1061,18 @@ def _join_channels(conn, invite, principal_id: int, now: float) -> None:
1061
  "VALUES (?, ?, ?, ?)", (ch["id"], principal_id, invite["created_by"], now))
1062
 
1063
 
 
 
 
 
 
 
 
 
 
 
 
 
1064
  def join(conn, code, name, display_name, now: float) -> dict:
1065
  """Redeem an invite (or a sign-in link). Returns the identity and its token."""
1066
  row = _find_invite(conn, code)
@@ -1103,10 +1115,14 @@ def join(conn, code, name, display_name, now: float) -> dict:
1103
  audit(conn, pid, "join.human", f"principal:{pid}", {"invite": row["id"], "role": row["role"]}, now)
1104
  return {"token": token, "principal": principal_json(conn, _principal_row(conn, pid)), "created": True, "next": JOIN_NEXT}
1105
 
1106
- # Agent or service account, owned by the inviter.
 
1107
  needed = auth.owner_roles_for(row["role"])
1108
  if inviter["role"] not in needed:
1109
  raise ApiError(410, "invite_inactive", "the person who made this invite can no longer give out this access")
 
 
 
1110
  if row["agent_name"]: # a named invite decides the name; the agent need not (and may not) pick another
1111
  if name not in (None, "", row["agent_name"]):
1112
  raise bad_request(f"this invite is for the agent {row['agent_name']!r}: leave name out or use that one")
@@ -1127,7 +1143,7 @@ def join(conn, code, name, display_name, now: float) -> dict:
1127
  """INSERT INTO principals (issuer, external_id, identity_type, role, display_name, created_at, created_by,
1128
  joined_via, owner_id, can_create_channels, invite_id)
1129
  VALUES (?, ?, ?, ?, ?, ?, ?, 'invite', ?, ?, ?)""",
1130
- (issuer, name, row["identity_type"], row["role"], display_name, now, inviter["id"], inviter["id"],
1131
  row["can_create_channels"], row["id"])).lastrowid
1132
  conn.execute("UPDATE invites SET uses = uses + 1 WHERE id = ?", (row["id"],))
1133
  _join_channels(conn, row, pid, now)
 
1061
  "VALUES (?, ?, ?, ?)", (ch["id"], principal_id, invite["created_by"], now))
1062
 
1063
 
1064
+ def _human_owner(conn, row):
1065
+ """The person at the top of an ownership chain: the row itself for a person, else its owner's owner, and so on.
1066
+ A chain that loops or ends without a person is refused rather than answered with an agent."""
1067
+ seen = set()
1068
+ while row["identity_type"] != "user":
1069
+ if row["owner_id"] is None or row["id"] in seen:
1070
+ raise ApiError(410, "invite_inactive", "no person owns the agent that made this invite")
1071
+ seen.add(row["id"])
1072
+ row = _principal_row(conn, row["owner_id"])
1073
+ return row
1074
+
1075
+
1076
  def join(conn, code, name, display_name, now: float) -> dict:
1077
  """Redeem an invite (or a sign-in link). Returns the identity and its token."""
1078
  row = _find_invite(conn, code)
 
1115
  audit(conn, pid, "join.human", f"principal:{pid}", {"invite": row["id"], "role": row["role"]}, now)
1116
  return {"token": token, "principal": principal_json(conn, _principal_row(conn, pid)), "created": True, "next": JOIN_NEXT}
1117
 
1118
+ # Agent or service account. It is owned by a person: the inviter, or, when an agent made the invite,
1119
+ # the person who owns that agent. The inviting agent stays on record as created_by.
1120
  needed = auth.owner_roles_for(row["role"])
1121
  if inviter["role"] not in needed:
1122
  raise ApiError(410, "invite_inactive", "the person who made this invite can no longer give out this access")
1123
+ owner = _human_owner(conn, inviter)
1124
+ if owner["disabled"] or owner["role"] not in needed:
1125
+ raise ApiError(410, "invite_inactive", "the person who owns the agent that made this invite can no longer give out this access")
1126
  if row["agent_name"]: # a named invite decides the name; the agent need not (and may not) pick another
1127
  if name not in (None, "", row["agent_name"]):
1128
  raise bad_request(f"this invite is for the agent {row['agent_name']!r}: leave name out or use that one")
 
1143
  """INSERT INTO principals (issuer, external_id, identity_type, role, display_name, created_at, created_by,
1144
  joined_via, owner_id, can_create_channels, invite_id)
1145
  VALUES (?, ?, ?, ?, ?, ?, ?, 'invite', ?, ?, ?)""",
1146
+ (issuer, name, row["identity_type"], row["role"], display_name, now, inviter["id"], owner["id"],
1147
  row["can_create_channels"], row["id"])).lastrowid
1148
  conn.execute("UPDATE invites SET uses = uses + 1 WHERE id = ?", (row["id"],))
1149
  _join_channels(conn, row, pid, now)