╭ changed ──╮ │src/web.zig│ ╰───────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │507 .local => |src| return .{ .author = try ui.localAuthor(src, io, allocator, arena), .role = .owner },│ │508 }; │ │509 const authorization: ui.Authorization = blk: { │ │510 - const token = getCookieValue(request, cookie_name) orelse break :blk .login_required; │ │511 - var user_id: [evt.event_id_size]u8 = undefined; │ │512 - if (!server.session_store.lookup(token, &user_id)) break :blk .login_required; │ │510 + const user_id = requestUserId(request, server.session_store) orelse break :blk .login_required; │ │511 │ │512 const repo_prefix = "/repo/"; │ │513 if (!std.mem.startsWith(u8, repo_base, repo_prefix)) break :blk .repo_not_found; │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │526 return null; │ │527 } │ │528 │ │529 +// whether the request may read the repo `repo_base` names. local mode always │ │530 +// may. │ │531 +fn mayRead( │ │532 + io: std.Io, │ │533 + allocator: std.mem.Allocator, │ │534 + request: *std.http.Server.Request, │ │535 + host: Host, │ │536 + repo_base: []const u8, │ │537 +) !bool { │ │538 + const server = switch (host) { │ │539 + .server => |server| server, │ │540 + .local => return true, │ │541 + }; │ │542 + const repo_prefix = "/repo/"; │ │543 + if (!std.mem.startsWith(u8, repo_base, repo_prefix)) return false; │ │544 + │ │545 + var admin_repo = try rp.Repo(.xit, evt.admin_repo_opts).open(io, allocator, .{ .path = server.admin_repo_path });│ │546 + defer admin_repo.deinit(io, allocator); │ │547 + var arena = std.heap.ArenaAllocator.init(allocator); │ │548 + defer arena.deinit(); │ │549 + const moment = try evt.currentMoment(evt.admin_repo_opts, &admin_repo); │ │550 + const user_id = blk: { │ │551 + const id = requestUserId(request, server.session_store) orelse break :blk null; │ │552 + break :blk if (try ui.activeUser(moment, &arena, id) != null) id else null; │ │553 + }; │ │554 + const role = (try ui.repoRole(moment, &arena, user_id, repo_base[repo_prefix.len..])) orelse return false; │ │555 + return role != .none; │ │556 +} │ │557 + │ │558 +// the logged-in user the request's session cookie names │ │559 +fn requestUserId(request: *std.http.Server.Request, session_store: SessionStore) ?[evt.event_id_size]u8 { │ │560 + const token = getCookieValue(request, cookie_name) orelse return null; │ │561 + var user_id: [evt.event_id_size]u8 = undefined; │ │562 + return if (session_store.lookup(token, &user_id)) user_id else null; │ │563 +} │ │564 + │ │565 // refuse a write from a request with no logged-in user. the body goes unread, │ │566 // so the connection closes. │ │567 fn respondLoginRequired(request: *std.http.Server.Request) !void { │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │968 attachment: AttachmentRequest, │ │969 host: Host, │ │970 ) !void { │ │971 + if (!try mayRead(io, allocator, request, host, attachment.repo_base)) return respondAttachmentNotFound(request); │ │972 const request_repo = (try requestRepoSource(io, allocator, host, attachment.repo_base)) orelse return respondAttachmentNotFound(request);│ │973 defer request_repo.deinit(allocator); │ │974 const source = request_repo.source; │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯