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 {
|
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();
|
||||||
|
|
|
@ -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
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 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;
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue