mirror of
https://github.com/andrewyng/openworker.git
synced 2026-09-11 06:30:25 +00:00
OpenWorker: initial import
Imported from andrewyng/aisuite@1b4bbf303e (contents of its platform/ directory, hoisted to the repo root). Development history prior to this commit lives in that repository. Co-authored-by: Devika <devikaverma11@gmail.com>
This commit is contained in:
@@ -0,0 +1,214 @@
|
||||
"""Per-workspace allow-list (managed Slack relay, M3.5).
|
||||
|
||||
Slack user ids are workspace-scoped, so each connected workspace (`slack:team:*`)
|
||||
carries its OWN allow-list; a relay event is authorized against ITS team's list alone.
|
||||
Team-less sources (manual Socket Mode) keep the flat `slack:default` list — these tests
|
||||
guard both paths so neither regresses the other.
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from fastapi.testclient import TestClient
|
||||
|
||||
from coworker.connectors import (
|
||||
ConnectorSettings,
|
||||
Gateway,
|
||||
MessageEvent,
|
||||
SessionSource,
|
||||
TeamAuth,
|
||||
load_settings,
|
||||
)
|
||||
from coworker.connectors.config import is_authorized
|
||||
from coworker.providers import ModelCapabilities, ProviderClient
|
||||
from coworker.secrets import SecretStore
|
||||
from coworker.server import create_app
|
||||
from coworker.server.manager import SessionManager
|
||||
|
||||
|
||||
class ScriptedProvider(ProviderClient):
|
||||
def complete(self, *, model, messages, tools=None, **settings):
|
||||
raise AssertionError("no turns expected")
|
||||
|
||||
def capabilities(self, model):
|
||||
return ModelCapabilities()
|
||||
|
||||
|
||||
def _relay_manager(tmp_path, *, teams=("T1",)) -> SessionManager:
|
||||
m = SessionManager(data_dir=tmp_path / "data", provider=ScriptedProvider())
|
||||
for t in teams:
|
||||
m.secrets.put(
|
||||
f"slack:team:{t}",
|
||||
{"type": "oauth", "managed": True, "bot_token": f"xoxb-{t}", "team_id": t},
|
||||
)
|
||||
m.secrets.put(
|
||||
"slack:default",
|
||||
{"type": "oauth", "managed": True, "mode": "relay", "enabled": True},
|
||||
)
|
||||
return m
|
||||
|
||||
|
||||
# -- is_authorized ---------------------------------------------------------------
|
||||
def test_is_authorized_team_scoped():
|
||||
s = ConnectorSettings(
|
||||
platform="slack",
|
||||
allowed_users={"U_FLAT"},
|
||||
teams={"T1": TeamAuth(allowed_users={"U_OK"}), "T2": TeamAuth()},
|
||||
)
|
||||
# authorized only via the event's OWN team's list
|
||||
assert is_authorized(s, SessionSource("slack", "C1", user_id="U_OK", team_id="T1"))
|
||||
assert not is_authorized(
|
||||
s, SessionSource("slack", "C1", user_id="U_OK", team_id="T2")
|
||||
)
|
||||
# the flat list never authorizes a team-scoped event
|
||||
assert not is_authorized(
|
||||
s, SessionSource("slack", "C1", user_id="U_FLAT", team_id="T1")
|
||||
)
|
||||
# unknown team = no install we know of → deny
|
||||
assert not is_authorized(
|
||||
s, SessionSource("slack", "C1", user_id="U_OK", team_id="T_UNKNOWN")
|
||||
)
|
||||
# per-team allow_all opens only that team
|
||||
s.teams["T2"].allow_all = True
|
||||
assert is_authorized(s, SessionSource("slack", "C1", user_id="U_X", team_id="T2"))
|
||||
assert not is_authorized(
|
||||
s, SessionSource("slack", "C1", user_id="U_X", team_id="T1")
|
||||
)
|
||||
|
||||
|
||||
def test_is_authorized_flat_path_unchanged():
|
||||
# Manual Socket Mode sources carry no team_id → the flat list, exactly as before,
|
||||
# even when team lists exist alongside.
|
||||
s = ConnectorSettings(
|
||||
platform="slack",
|
||||
allowed_users={"U_FLAT"},
|
||||
teams={"T1": TeamAuth(allowed_users={"U_OK"}, allow_all=True)},
|
||||
)
|
||||
assert is_authorized(s, SessionSource("slack", "C1", user_id="U_FLAT"))
|
||||
assert not is_authorized(s, SessionSource("slack", "C1", user_id="U_OK"))
|
||||
s2 = ConnectorSettings(platform="slack", allow_all=True)
|
||||
assert is_authorized(s2, SessionSource("slack", "C1", user_id="anyone"))
|
||||
|
||||
|
||||
# -- load_settings ---------------------------------------------------------------
|
||||
def test_load_settings_populates_teams(tmp_path):
|
||||
secrets = SecretStore(tmp_path / "secrets.json")
|
||||
secrets.put("slack:default", {"type": "oauth", "mode": "relay", "enabled": True})
|
||||
secrets.put(
|
||||
"slack:team:T1",
|
||||
{"bot_token": "xoxb-1", "allowed_users": ["U_A", "U_B"]},
|
||||
)
|
||||
secrets.put("slack:team:T2", {"bot_token": "xoxb-2", "allow_all": True})
|
||||
settings = load_settings(secrets)
|
||||
slack = settings["slack"]
|
||||
assert slack.enabled is True
|
||||
assert slack.teams["T1"].allowed_users == {"U_A", "U_B"}
|
||||
assert slack.teams["T1"].allow_all is False
|
||||
assert slack.teams["T2"].allow_all is True
|
||||
|
||||
|
||||
# -- manager write path ----------------------------------------------------------
|
||||
def test_set_allowed_with_team_writes_team_profile(tmp_path):
|
||||
m = _relay_manager(tmp_path, teams=("T1", "T2"))
|
||||
m.gateway = Gateway(
|
||||
secrets=m.secrets, settings={"slack": load_settings(m.secrets)["slack"]}
|
||||
)
|
||||
|
||||
out = m.allow_user("slack", "U_NEW", team_id="T1")
|
||||
assert out["ok"] is True and out["team_id"] == "T1"
|
||||
assert m.secrets.get("slack:team:T1")["allowed_users"] == ["U_NEW"]
|
||||
# the sibling team and the flat list are untouched
|
||||
assert not m.secrets.get("slack:team:T2").get("allowed_users")
|
||||
assert not m.secrets.get("slack:default").get("allowed_users")
|
||||
# live gateway reflects it without a restart
|
||||
assert m.gateway.settings["slack"].teams["T1"].allowed_users == {"U_NEW"}
|
||||
|
||||
m.disallow_user("slack", "U_NEW", team_id="T1")
|
||||
assert m.secrets.get("slack:team:T1")["allowed_users"] == []
|
||||
assert m.gateway.settings["slack"].teams["T1"].allowed_users == set()
|
||||
|
||||
# unknown workspace → error, nothing written
|
||||
assert m.allow_user("slack", "U_X", team_id="T_NOPE")["ok"] is False
|
||||
|
||||
|
||||
def test_set_allowed_without_team_keeps_flat_behavior(tmp_path):
|
||||
m = _relay_manager(tmp_path)
|
||||
assert m.allow_user("slack", "U_FLAT")["allowed_users"] == ["U_FLAT"]
|
||||
assert m.secrets.get("slack:default")["allowed_users"] == ["U_FLAT"]
|
||||
assert not m.secrets.get("slack:team:T1").get("allowed_users")
|
||||
|
||||
|
||||
# -- park + resolve --------------------------------------------------------------
|
||||
async def test_park_carries_team_and_resolve_allows_into_team(tmp_path):
|
||||
m = _relay_manager(tmp_path, teams=("T1",))
|
||||
delivered: list[MessageEvent] = []
|
||||
|
||||
async def _capture(event: MessageEvent) -> None:
|
||||
delivered.append(event)
|
||||
|
||||
m._dispatch_inbound = _capture
|
||||
|
||||
event = MessageEvent(
|
||||
text="hello from T1",
|
||||
source=SessionSource(
|
||||
"slack", "C9", user_id="U_STRANGER", user_name="Zed", team_id="T1"
|
||||
),
|
||||
)
|
||||
await m._park_unauthorized(event)
|
||||
items = m.parked.list("slack")
|
||||
assert items[0]["team_id"] == "T1"
|
||||
|
||||
out = await m.resolve_unauthorized("slack", items[0]["id"], "allow_deliver")
|
||||
assert out["ok"] is True
|
||||
# the allow landed on the WORKSPACE list, not the flat one
|
||||
assert m.secrets.get("slack:team:T1")["allowed_users"] == ["U_STRANGER"]
|
||||
assert not m.secrets.get("slack:default").get("allowed_users")
|
||||
# the replayed event keeps its workspace, so per-team auth re-checks correctly
|
||||
assert len(delivered) == 1
|
||||
assert delivered[0].source.team_id == "T1"
|
||||
assert delivered[0].text == "hello from T1"
|
||||
|
||||
|
||||
async def test_resolve_teamless_parked_uses_flat_list(tmp_path):
|
||||
# Manual-mode parked items (no team_id) keep resolving into slack:default.
|
||||
m = _relay_manager(tmp_path)
|
||||
|
||||
async def _noop(event) -> None:
|
||||
pass
|
||||
|
||||
m._dispatch_inbound = _noop
|
||||
await m._park_unauthorized(
|
||||
MessageEvent(
|
||||
text="hi",
|
||||
source=SessionSource("slack", "D1", user_id="U_M", chat_type="dm"),
|
||||
)
|
||||
)
|
||||
item = m.parked.list("slack")[0]
|
||||
assert item["team_id"] is None
|
||||
assert (await m.resolve_unauthorized("slack", item["id"], "allow"))["ok"] is True
|
||||
assert m.secrets.get("slack:default")["allowed_users"] == ["U_M"]
|
||||
assert not m.secrets.get("slack:team:T1").get("allowed_users")
|
||||
|
||||
|
||||
# -- REST + connector_list surface ------------------------------------------------
|
||||
def test_rest_allow_with_team_and_workspaces_field(tmp_path):
|
||||
m = _relay_manager(tmp_path, teams=("T1", "T2"))
|
||||
client = TestClient(create_app(m))
|
||||
|
||||
r = client.post(
|
||||
"/v1/connectors/slack/allow", json={"user_id": "U_W", "team_id": "T1"}
|
||||
)
|
||||
assert r.json()["ok"] is True
|
||||
slack = next(
|
||||
c
|
||||
for c in client.get("/v1/connectors").json()["connectors"]
|
||||
if c["name"] == "slack"
|
||||
)
|
||||
assert slack["connected"] is True and slack["mode"] == "relay"
|
||||
ws = {w["team_id"]: w for w in slack["workspaces"]}
|
||||
assert ws["T1"]["allowed_users"] == ["U_W"]
|
||||
assert ws["T2"]["allowed_users"] == []
|
||||
|
||||
r = client.post(
|
||||
"/v1/connectors/slack/disallow", json={"user_id": "U_W", "team_id": "T1"}
|
||||
)
|
||||
assert r.json()["allowed_users"] == []
|
||||
Reference in New Issue
Block a user