refactor db layer
This commit is contained in:
parent
cc4badae21
commit
494d317ac1
4 changed files with 137 additions and 51 deletions
|
@ -318,6 +318,10 @@ pub const ApiContext = struct {
|
|||
pub fn userId(self: ApiContext) ?Uuid {
|
||||
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 {
|
||||
|
@ -457,6 +461,10 @@ fn ApiConn(comptime DbConn: type, comptime models: anytype) type {
|
|||
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 fn register(self: *Self, username: []const u8, password: []const u8, opt: RegistrationOptions) !UserResponse {
|
||||
// const tx = try self.db.beginOrSavepoint();
|
||||
|
|
|
@ -91,57 +91,6 @@ pub fn methods(comptime models: type) type {
|
|||
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 {
|
||||
const hash = try hashToken(token, self.allocator);
|
||||
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
|
||||
// GPU-based cracking approaches by using large amounts of memory, for
|
||||
// password hashing.
|
||||
|
|
78
src/api/services.zig
Normal file
78
src/api/services.zig
Normal 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);
|
||||
}
|
||||
};
|
||||
}
|
|
@ -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 commitOrRelease = if (tx_level < 2) commit else release;
|
||||
|
||||
|
|
Loading…
Reference in a new issue