╭─────────────╮ │scroll to top│ ╰─────────────╯ ╭ User.zig ─────────────────────────────────────────────────────╮ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ ╰───────────────────────────────────────────────────────────────╯
╭────╮ ╭ clone ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╮ │http│ ssh │ │ ╰────╯ ╰ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ╯
16 event_id: *const [evt.event_id_size]u8, 17 event_maybe: ?@This(), 18 arena: *std.heap.ArenaAllocator, 19 ) !void { 20 const user_key = hash.hashInt(hash_kind, event_id); 21 22 const event_id_to_user_cursor = try haxy_moment.putCursor(hash.hashInt(hash_kind, "event-id->user")); 23 const event_id_to_user = try DB.HashMap(.read_write).init(event_id_to_user_cursor); 24 25 const name_to_user_id_cursor = try haxy_moment.putCursor(hash.hashInt(hash_kind, "name->user-id")); 26 const name_to_user_id = try DB.HashMap(.read_write).init(name_to_user_id_cursor); 27 28 if (event_maybe) |event| { 29 // if this event_id already maps to a user with a different name, 30 // drop the stale name->id entry first 31 if (try event_id_to_user.getCursor(user_key)) |existing_cursor| { 32 const existing_user = try DB.HashMap(.read_only).init(existing_cursor); 33 const existing_event = try evt.read(@This(), DB, hash_kind, arena, existing_user); 34 if (!std.mem.eql(u8, existing_event.name, event.name)) { 35 _ = try name_to_user_id.remove(hash.hashInt(hash_kind, existing_event.name)); 36 } 37 } 38 39 const user_cursor = try event_id_to_user.putCursor(user_key); 40 const user = try DB.HashMap(.read_write).init(user_cursor); 41 try evt.upsert(@This(), DB, hash_kind, user, event); 42 43 try name_to_user_id.put(hash.hashInt(hash_kind, event.name), .{ .bytes = event_id }); 44 } else { 45 // read the user's name so we can drop its name->id index entry 46 if (try event_id_to_user.getCursor(user_key)) |existing_cursor| { 47 const existing_user = try DB.HashMap(.read_only).init(existing_cursor); 48 const existing_event = try evt.read(@This(), DB, hash_kind, arena, existing_user); 49 _ = try name_to_user_id.remove(hash.hashInt(hash_kind, existing_event.name)); 50 } 51 52 if (!try event_id_to_user.remove(user_key)) return error.EventNotFound; 53 54 const user_id_to_repos_cursor = try haxy_moment.putCursor(hash.hashInt(hash_kind, "user-id->repos")); 55 const user_id_to_repos = try DB.HashMap(.read_write).init(user_id_to_repos_cursor); 56 _ = try user_id_to_repos.remove(user_key); 57 } 58 } 59 60 pub const password_hash_max_len = bcrypt.hash_length * 2; 61 62 pub fn hashPassword( 63 password: []const u8, 64 out: []u8, 65 io: std.Io, 66 ) ![]const u8 { 67 return bcrypt.strHash(password, .{ 68 .params = bcrypt.Params.owasp, 69 .encoding = .phc, 70 }, out, io); 71 } 72 73 pub const VerifyResult = union(enum) { 74 success: [evt.event_id_size]u8, 75 unknown_user, 76 wrong_password, 77 }; 78 79 // look up a user by name (via the name->user-id index) and verify the 80 // supplied password against the stored bcrypt hash. used by both the TTY 81 // login submit and the server's /login route. 82 pub fn verifyCredentials( 83 comptime DB: type, 84 comptime hash_kind: hash.HashKind, 85 haxy_moment: DB.HashMap(.read_only), 86 arena: *std.heap.ArenaAllocator, 87 name: []const u8, 88 password: []const u8, 89 ) !VerifyResult { 90 const name_index_cursor = try haxy_moment.getCursor(hash.hashInt(hash_kind, "name->user-id")) orelse return .unknown_user; 91 const name_index = try DB.HashMap(.read_only).init(name_index_cursor); 92 93 const user_id_cursor = try name_index.getCursor(hash.hashInt(hash_kind, name)) orelse return .unknown_user; 94 var user_id: [evt.event_id_size]u8 = undefined; 95 _ = try user_id_cursor.readBytes(&user_id); 96 97 const user_key = hash.hashInt(hash_kind, &user_id); 98 const event_id_to_user_cursor = try haxy_moment.getCursor(hash.hashInt(hash_kind, "event-id->user")) orelse return .unknown_user; 99 const event_id_to_user = try DB.HashMap(.read_only).init(event_id_to_user_cursor); 100 101 const user_cursor = try event_id_to_user.getCursor(user_key) orelse return .unknown_user; 102 const user_map = try DB.HashMap(.read_only).init(user_cursor); 103 const user_event = try evt.read(@This(), DB, hash_kind, arena, user_map); 104 105 bcrypt.strVerify(user_event.password_hash, password, .{ .silently_truncate_password = false }) catch { 106 return .wrong_password; 107 }; 108 109 return .{ .success = user_id }; 110 } 111