╭ changed ────────╮ │src/serve_ssh.zig│ ╰─────────────────╯ ╭───────────╮ │← all files│ ╰───────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │566 return if (isKeyInAuthorizedKeys(owner.event.ssh_keys, fingerprint)) .allowed else .denied; │ │567 }; │ │568 │ │569 - if ((service == .upload_pack and repo.repo.event.read_access == .public) or │ │570 - (service == .receive_pack and repo.repo.event.write_access == .public)) return .allowed; │ │571 - if (try keyBelongsToUser(moment, &arena, repo.repo.event.user_id, fingerprint)) return .allowed; │ │569 + const min_role: evt.Repo.Role = switch (service) { │ │570 + .upload_pack => .read, │ │571 + .receive_pack => .write, │ │572 + }; │ │573 + // skip the key lookups when the base role is enough │ │574 + if (evt.Repo.roleOf(repo.repo, null).atLeast(min_role)) return .allowed; │ │575 + │ │576 + const user_id_maybe = try repoUserWithKey(moment, &arena, repo.repo.event, fingerprint); │ │577 + return if (evt.Repo.roleOf(repo.repo, user_id_maybe).atLeast(min_role)) .allowed else .denied; │ │578 +} │ │579 + │ │580 +// the repo's owner or collaborator holding the key, or null for anyone else │ │581 +fn repoUserWithKey( │ │582 + moment: evt.AdminDB.HashMap(.read_only), │ │583 + arena: *std.heap.ArenaAllocator, │ │584 + repo: evt.Repo, │ │585 + fingerprint: *const [ssh.fingerprint_len]u8, │ │586 +) !?[evt.event_id_size]u8 { │ │587 + // a user found by this id has a full-size id │ │588 + if (try keyBelongsToUser(moment, arena, repo.user_id, fingerprint)) return repo.user_id[0..evt.event_id_size].*;│ │589 │ │573 - const user_id_lists: []const []const u8 = switch (service) { │ │574 - .upload_pack => &.{ repo.repo.event.write_user_ids, repo.repo.event.read_user_ids }, │ │575 - .receive_pack => &.{repo.repo.event.write_user_ids}, │ │576 - }; │ │577 - for (user_id_lists) |user_ids| { │ │590 + for ([_][]const u8{ repo.write_user_ids, repo.read_user_ids }) |user_ids| { │ │591 var lines = std.mem.tokenizeScalar(u8, user_ids, '\n'); │ │592 while (lines.next()) |id| { │ │593 const user_id = try evt.parseEventId(id); │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────╮ │581 - if (try keyBelongsToUser(moment, &arena, &user_id, fingerprint)) return .allowed;│ │594 + if (try keyBelongsToUser(moment, arena, &user_id, fingerprint)) return user_id; │ │595 } │ │596 } │ │584 - return .denied; │ │597 + return null; │ │598 } │ │599 │ │600 fn keyBelongsToUser( │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────╯