aboutsummaryrefslogtreecommitdiffstats
path: root/packages/meshbay-node/src/meshbay_node/ops/groups.py
diff options
context:
space:
mode:
Diffstat (limited to 'packages/meshbay-node/src/meshbay_node/ops/groups.py')
-rw-r--r--packages/meshbay-node/src/meshbay_node/ops/groups.py30
1 files changed, 21 insertions, 9 deletions
diff --git a/packages/meshbay-node/src/meshbay_node/ops/groups.py b/packages/meshbay-node/src/meshbay_node/ops/groups.py
index 1d8003c..ac14c3a 100644
--- a/packages/meshbay-node/src/meshbay_node/ops/groups.py
+++ b/packages/meshbay-node/src/meshbay_node/ops/groups.py
@@ -9,7 +9,7 @@ from meshbay_common.crypto import generate_gek, wrap_gek_aes
from meshbay_node.config import DEFAULT_CONFIG_PATH
from meshbay_node.ops.core import OpError, _config, _group_ctx, _hub
-from meshbay_node.ops.node_toml import _find_group_range
+from meshbay_node.ops.node_toml import _find_group_range, toml_string
log = logging.getLogger("meshbay_node.ops")
@@ -142,17 +142,28 @@ async def list_groups(state: dict) -> dict:
return {"groups": out, "operator_paired": has_operator, "settings": settings}
+JOIN_POLICIES = ("invite", "open")
+
+
async def attach_group(state: dict, name: str, shared_dir: str,
- writable: bool = True) -> dict:
+ writable: bool = True, join_policy: str = "invite") -> dict:
"""
Write a new [[groups]] block into node.toml.
The name-to-id lookup happens here because this process is the one logged
into the hub. Nothing is created on the hub: the group already exists, this
only tells the node to host it.
+
+ `join_policy` is the operator's, given with this request, and `invite`
+ unless they say otherwise. The hub's own record of the group is not read
+ for it: a hub that could declare a group open would be handed its key by
+ anyone it sent. The hub's value is returned beside it, so a caller can say
+ when the two differ.
"""
if not name or not shared_dir:
raise OpError("name and shared_dir are required")
+ if join_policy not in JOIN_POLICIES:
+ raise OpError(f"join_policy must be one of {', '.join(JOIN_POLICIES)}")
config = _config(state)
hub = _hub(state)
try:
@@ -182,12 +193,12 @@ async def attach_group(state: dict, name: str, shared_dir: str,
raise OpError(f"Cannot create {path}: {e}") from e
conf_path = Path(state.get("config_path") or DEFAULT_CONFIG_PATH)
- join_policy = group.get("join_policy", "invite")
+ visibility = "public" if join_policy == "open" else "private"
block = (f'\n[[groups]]\n'
- f'id = "{group["id"]}"\n'
- f'name = "{group["name"]}"\n'
- f'visibility = "{group.get("visibility", "private")}"\n'
- f'join_policy = "{join_policy}"\n')
+ f'id = {toml_string(group["id"])}\n'
+ f'name = {toml_string(group["name"])}\n'
+ f'visibility = {toml_string(visibility)}\n'
+ f'join_policy = {toml_string(join_policy)}\n')
# No `upload_dir` here. `GroupConfig.__post_init__` still *reads* it, so an
# existing node.toml keeps working — but what it does on read is force every
# other root read-only and append that path as the one writable one, which
@@ -198,7 +209,7 @@ async def attach_group(state: dict, name: str, shared_dir: str,
block += (f'\n [[groups.roots]]\n'
# Forward slashes: a Windows path in a TOML basic string is a
# parse error (`\U`, `\a`, ... are escapes). pathlib reads `/`.
- f' path = "{path.as_posix()}"\n'
+ f' path = {toml_string(path.as_posix())}\n'
f' writable = {"true" if writable else "false"}\n')
try:
with conf_path.open("a", encoding="utf-8", newline="\n") as f:
@@ -208,7 +219,8 @@ async def attach_group(state: dict, name: str, shared_dir: str,
result = {"group_id": group["id"], "name": group["name"],
"shared_dir": str(path), "config": str(conf_path),
- "writable": writable,
+ "writable": writable, "join_policy": join_policy,
+ "hub_join_policy": group.get("join_policy", "invite"),
"note": "restart the node to pick it up"}
return result