╭ changed ─────────╮ │src/event/User.zig│ ╰──────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────╮ │10 email: []const u8, │ │11 password_hash: []const u8, │ │12 enable_ansi: bool = false, │ │13 +// newline-separated authorized_keys lines (one OpenSSH public key per line)│ │14 +ssh_keys: []const u8 = "", │ │15 │ │16 const Self = @This(); │ │17 │ ╰────────────────────────────────────────────────────────────────────────────────╯ ╭ changed ──╮ │src/try.zig│ ╰───────────╯ ╭──────────────────────────────────────╮ │3 //! out safely. │ │4 │ │5 const std = @import("std"); │ │6 +const builtin = @import("builtin");│ │7 const hx = @import("haxy"); │ │8 const srv = hx.serve; │ │9 const evt = hx.event; │ ╰──────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────╮ │35 defer cwd.deleteTree(io, temp_dir_name) catch {}; │ │36 defer temp_dir.close(io); │ │37 │ │38 + // write the dev SSH private key so pushes can authenticate against the │ │39 + // matching public key seeded on the admin account below │ │40 + { │ │41 + const priv_key_file = try temp_dir.createFile(io, "key", .{}); │ │42 + defer priv_key_file.close(io); │ │43 + try priv_key_file.writeStreamingAll(io, │ │44 + \\-----BEGIN OPENSSH PRIVATE KEY----- │ │45 + \\b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW│ │46 + \\QyNTUxOQAAACCniLPJiaooAWecvOCeAjoJwCSeWxzysvpTNkpYjF22JgAAAJA+7hikPu4Y│ │47 + \\pAAAAAtzc2gtZWQyNTUxOQAAACCniLPJiaooAWecvOCeAjoJwCSeWxzysvpTNkpYjF22Jg│ │48 + \\AAAEDVlopOMnKt/7by/IA8VZvQXUS/O6VLkixOqnnahUdPCKeIs8mJqigBZ5y84J4COgnA│ │49 + \\JJ5bHPKy+lM2SliMXbYmAAAAC3JhZGFyQHJvYXJrAQI= │ │50 + \\-----END OPENSSH PRIVATE KEY----- │ │51 + \\ │ │52 + ); │ │53 + if (.windows != builtin.os.tag) { │ │54 + try priv_key_file.setPermissions(io, @enumFromInt(0o600)); │ │55 + } │ │56 + } │ │57 + │ │58 const cwd_path = try std.process.currentPathAlloc(io, allocator); │ │59 defer allocator.free(cwd_path); │ │60 │ ╰────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │163 var password_hash_buf: [evt.User.password_hash_max_len]u8 = undefined; │ │164 const password_hash = try evt.User.hashPassword("password", &password_hash_buf, io); │ │165 │ │166 + // public key matching temp-try/key, given to admin so we can push as admin │ │167 + const admin_ssh_key = "ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKeIs8mJqigBZ5y84J4COgnAJJ5bHPKy+lM2SliMXbYm radar@roark";│ │168 + │ │169 var events_to_consume: [user_data.len + repo_data.len]evt.EventWithId = undefined; │ │170 for (user_data, 0..) |u, i| { │ │171 events_to_consume[i] = .{ │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭───────────────────────────────────────────────────────────────────────────╮ │176 .display_name = u.display_name, │ │177 .email = u.email, │ │178 .password_hash = password_hash, │ │179 + .ssh_keys = if (i == 0) admin_ssh_key else "",│ │180 }, │ │181 }, │ │182 }; │ ╰───────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │268 var stderr_writer = std.Io.File.stderr().writer(io, &.{}); │ │269 const run_opts = hx.main.RunOpts{ .out = &stdout_writer.interface, .err = &stderr_writer.interface };│ │270 │ │271 + const key_path = try std.fs.path.join(allocator, &.{ cwd_path, temp_dir_name, "key" }); │ │272 + defer allocator.free(key_path); │ │273 + │ │274 const Runnable = struct { │ │275 io: std.Io, │ │276 + key_path: []const u8, │ │277 │ │278 pub fn run(self: @This()) !void { │ │279 std.debug.print( │ ╰──────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │288 \\ echo "hello" > hello.txt │ │289 \\ git add hello.txt │ │290 \\ git commit -m "let there be light" │ │262 - \\ GIT_SSH_COMMAND='ssh -p 8022 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null' git push localhost:admin/test HEAD:master │ │291 + \\ GIT_SSH_COMMAND='ssh -p 8022 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o IdentitiesOnly=yes -i {s}' git push localhost:admin/test HEAD:master│ │292 \\ │ │293 \\to quit, press enter. │ │294 \\ │ ╰─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ ╭──────────────────────────────────────────────────────────────────────────────╮ │266 - , .{}); │ │295 + , .{self.key_path}); │ │296 // portable stdin read via std.Io — equivalent to the │ │297 // std.posix.read(STDIN_FILENO, ...) that doesn't compile│ │298 // on windows. │ ╰──────────────────────────────────────────────────────────────────────────────╯ ╭────────────────────────────────────────────────────────────────────────────────────────────────────────╮ │304 │ │305 try srv.run(.xit, .{}, io, allocator, cwd_path, .{ │ │306 .data_dir = server_path, │ │278 - }, run_opts.err, Runnable{ .io = io }); │ │307 + }, run_opts.err, Runnable{ .io = io, .key_path = key_path }); │ │308 } else { │ │309 var null_writer = std.Io.Writer.Discarding.init(&.{}); │ │310 const run_opts = hx.main.RunOpts{ .out = &null_writer.writer, .err = &null_writer.writer };│ ╰────────────────────────────────────────────────────────────────────────────────────────────────────────╯