refactor db layer

This commit is contained in:
jaina heartles 2023-01-02 12:38:42 -08:00
parent cc4badae21
commit 494d317ac1
4 changed files with 137 additions and 51 deletions

View file

@ -318,6 +318,10 @@ pub const ApiContext = struct {
pub fn userId(self: ApiContext) ?Uuid { pub fn userId(self: ApiContext) ?Uuid {
if (self.token_info) |t| return t.user_id else return null; if (self.token_info) |t| return t.user_id else return null;
} }
pub fn isAdmin(self: ApiContext) bool {
return self.userId() != null and self.community.kind == .admin;
}
}; };
fn ApiConn(comptime DbConn: type, comptime models: anytype) type { fn ApiConn(comptime DbConn: type, comptime models: anytype) type {
@ -457,6 +461,10 @@ fn ApiConn(comptime DbConn: type, comptime models: anytype) type {
return true; return true;
} }
pub fn login(self: *Self, username: []const u8, password: []const u8) !Token {
return @import("./methods/auth.zig").login(self.allocator, self.context, @import("./services.zig").Services(DbConn){ .db = self.db }, username, password);
}
pub usingnamespace @import("./methods/auth.zig").methods(models); pub usingnamespace @import("./methods/auth.zig").methods(models);
// pub fn register(self: *Self, username: []const u8, password: []const u8, opt: RegistrationOptions) !UserResponse { // pub fn register(self: *Self, username: []const u8, password: []const u8, opt: RegistrationOptions) !UserResponse {
// const tx = try self.db.beginOrSavepoint(); // const tx = try self.db.beginOrSavepoint();

View file

@ -91,57 +91,6 @@ pub fn methods(comptime models: type) type {
return id; return id;
} }
pub fn login(self: anytype, username: []const u8, password: []const u8) !Token {
const community_id = self.context.community.id;
const credentials = try models.accounts.getCredentialsByUsername(
self.db,
username,
community_id,
self.allocator,
);
defer util.deepFree(self.allocator, credentials);
try verifyPassword(credentials.password_hash, password, self.allocator);
const token = try generateToken(self.allocator);
errdefer util.deepFree(self.allocator, token);
const token_hash = hashToken(token, self.allocator) catch |err| switch (err) {
error.OutOfMemory => return error.OutOfMemory,
else => unreachable,
};
defer util.deepFree(self.allocator, token_hash);
const tx = try self.db.begin();
errdefer tx.rollback();
// ensure that the password has not changed in the meantime
{
const updated_info = try models.accounts.getCredentialsByUsername(
tx,
username,
community_id,
self.allocator,
);
defer util.deepFree(self.allocator, updated_info);
if (!std.mem.eql(u8, credentials.password_hash, updated_info.password_hash)) return error.InvalidLogin;
}
try models.tokens.create(tx, credentials.account_id, token_hash, self.allocator);
try tx.commit();
const info = try models.tokens.getByHash(self.db, token_hash, community_id, self.allocator);
defer util.deepFree(self.allocator, info);
return .{
.value = token,
.info = .{
.user_id = info.account_id,
.issued_at = info.issued_at,
},
};
}
pub fn verifyToken(self: anytype, token: []const u8) !Token.Info { pub fn verifyToken(self: anytype, token: []const u8) !Token.Info {
const hash = try hashToken(token, self.allocator); const hash = try hashToken(token, self.allocator);
defer self.allocator.free(hash); defer self.allocator.free(hash);
@ -154,6 +103,56 @@ pub fn methods(comptime models: type) type {
}; };
} }
const ApiContext = @import("../lib.zig").ApiContext;
pub fn login(alloc: std.mem.Allocator, ctx: ApiContext, svcs: anytype, username: []const u8, password: []const u8) !Token {
const community_id = ctx.community.id;
const credentials = try svcs.getCredentialsByUsername(
alloc,
username,
community_id,
);
defer util.deepFree(alloc, credentials);
try verifyPassword(credentials.password_hash, password, alloc);
const token = try generateToken(alloc);
errdefer util.deepFree(alloc, token);
const token_hash = hashToken(token, alloc) catch |err| switch (err) {
error.OutOfMemory => return error.OutOfMemory,
else => unreachable,
};
defer util.deepFree(alloc, token_hash);
const tx = try svcs.beginTx();
errdefer tx.rollbackTx();
// ensure that the password has not changed in the meantime
{
const updated_info = try tx.getCredentialsByUsername(
alloc,
username,
community_id,
);
defer util.deepFree(alloc, updated_info);
if (!std.mem.eql(u8, credentials.password_hash, updated_info.password_hash)) return error.InvalidLogin;
}
try tx.createToken(alloc, credentials.account_id, token_hash);
try tx.commitTx();
const info = try tx.getTokenByHash(alloc, token_hash, community_id);
defer util.deepFree(alloc, info);
return .{
.value = token,
.info = .{
.user_id = info.account_id,
.issued_at = info.issued_at,
},
};
}
// We use scrypt, a password hashing algorithm that attempts to slow down // We use scrypt, a password hashing algorithm that attempts to slow down
// GPU-based cracking approaches by using large amounts of memory, for // GPU-based cracking approaches by using large amounts of memory, for
// password hashing. // password hashing.

78
src/api/services.zig Normal file
View file

@ -0,0 +1,78 @@
const std = @import("std");
const util = @import("util");
const Uuid = util.Uuid;
const DateTime = util.DateTime;
const communities = @import("./services/communities.zig");
const actors = @import("./services/actors.zig");
const drive = @import("./services/drive.zig");
const files = @import("./services/files.zig");
const invites = @import("./services/invites.zig");
const notes = @import("./services/notes.zig");
const follows = @import("./services/follows.zig");
const accounts = @import("./services/accounts.zig");
const tokens = @import("./services/tokens.zig");
pub const Token = tokens.Token;
pub const Account = accounts.Account;
pub const Credentials = accounts.Credentials;
pub fn Services(comptime Db: type) type {
return struct {
const Self = @This();
db: Db,
pub fn beginTx(self: Self) !Services(Db.BeginOrSavepoint) {
return Services(Db.BeginOrSavepoint){
.db = try self.db.beginOrSavepoint(),
};
}
pub fn commitTx(self: Self) !void {
return try self.db.commitOrRelease();
}
pub fn rollbackTx(self: Self) void {
return self.db.rollback();
}
pub fn createAccount(
self: Self,
alloc: std.mem.Allocator,
actor: Uuid,
password_hash: []const u8,
options: accounts.CreateOptions,
) !Account {
return try accounts.create(self.db, actor, password_hash, options, alloc);
}
pub fn getCredentialsByUsername(
self: Self,
alloc: std.mem.Allocator,
username: []const u8,
community_id: Uuid,
) !Credentials {
return try accounts.getCredentialsByUsername(self.db, username, community_id, alloc);
}
pub fn createToken(
self: Self,
alloc: std.mem.Allocator,
account_id: Uuid,
hash: []const u8,
) !void {
return try tokens.create(self.db, account_id, hash, alloc);
}
pub fn getTokenByHash(
self: Self,
alloc: std.mem.Allocator,
hash: []const u8,
community_id: Uuid,
) !Token {
return try tokens.getByHash(self.db, hash, community_id, alloc);
}
};
}

View file

@ -505,6 +505,7 @@ fn Tx(comptime tx_level: u8) type {
}; };
} }
pub const BeginOrSavepoint = Tx(tx_level + 1);
pub const beginOrSavepoint = if (tx_level == 0) begin else savepoint; pub const beginOrSavepoint = if (tx_level == 0) begin else savepoint;
pub const commitOrRelease = if (tx_level < 2) commit else release; pub const commitOrRelease = if (tx_level < 2) commit else release;