ddblocal/src/Account.zig

105 lines
4.1 KiB
Zig

const std = @import("std");
const encryption = @import("encryption.zig");
const sqlite = @import("sqlite"); // TODO: If we use this across all services, Account should not have this, and we should have a localdbaccount struct
const test_account_key = "09aGW6z6QofVsPlWP9FGqVnshxHWAWrKZwLkwkgWs7w=";
const log = std.log.scoped(.Account);
const Self = @This();
allocator: std.mem.Allocator,
root_account_key: *[encryption.key_length]u8,
pub var root_key_mapping: ?std.StringHashMap([]const u8) = null;
pub fn accountForId(allocator: std.mem.Allocator, account_id: []const u8) !Self {
if (std.mem.eql(u8, account_id, "1234")) {
var key = try allocator.alloc(u8, encryption.key_length);
errdefer allocator.free(key);
try encryption.decodeKey(key[0..encryption.key_length], test_account_key.*);
return Self{
.allocator = allocator,
.root_account_key = key[0..encryption.key_length],
};
}
// Check our root mappings (populated elsewhere)
if (root_key_mapping) |m| {
if (m.get(account_id)) |k| {
var key = try allocator.alloc(u8, encryption.key_length);
errdefer allocator.free(key);
try encryption.decodeKey(key[0..encryption.key_length], @constCast(k[0..encryption.encoded_key_length]).*);
return Self{
.allocator = allocator,
.root_account_key = key[0..encryption.key_length],
};
}
}
// TODO: Check STS
log.err("Got account id '{s}', but could not find this ('1234' is test account). STS GetAccessKeyInfo not implemented", .{account_id});
return error.NotImplemented;
}
pub fn deinit(self: Self) void {
std.crypto.utils.secureZero(u8, self.root_account_key);
self.allocator.free(self.root_account_key);
}
pub var data_dir: []const u8 = "";
pub var test_retain_db: bool = false;
var test_db: ?*sqlite.Db = null;
pub fn testDbDeinit() void {
test_retain_db = false;
if (test_db) |db| {
db.deinit();
test_db = null;
}
}
/// Gets the database for this account. If under test, a memory database is used
/// instead. Will initialize the database with appropriate metadata tables
pub fn dbForAccount(allocator: std.mem.Allocator, account_id: []const u8) !*sqlite.Db {
const builtin = @import("builtin");
if (builtin.is_test and test_retain_db)
if (test_db) |db| return db;
// TODO: Need to move this function somewhere central
// TODO: Need configuration for what directory to use
// TODO: Should this be a pool, and if so, how would we know when to close?
const file_without_path = try std.fmt.allocPrint(allocator, "ddb-{s}.sqlite3", .{account_id});
defer allocator.free(file_without_path);
const db_file_name = try std.fs.path.joinZ(allocator, &[_][]const u8{ data_dir, file_without_path });
defer allocator.free(db_file_name);
const mode = if (builtin.is_test) sqlite.Db.Mode.Memory else sqlite.Db.Mode{ .File = db_file_name };
const new = mode == .Memory or (std.fs.cwd().statFile(file_without_path) catch null == null);
var db = try allocator.create(sqlite.Db);
db.* = try sqlite.Db.init(.{
.mode = mode,
.open_flags = .{
.write = true,
.create = new,
},
.threading_mode = .MultiThread,
});
// DDB minimum table name length is 3. DDB local creates this table with metadata
// This of course is only if the database is first run
if (new)
try db.exec(
\\CREATE TABLE dm (
\\ TableName TEXT,
\\ CreationDateTime INTEGER,
\\ LastDecreaseDate INTEGER,
\\ LastIncreaseDate INTEGER,
\\ NumberOfDecreasesToday INTEGER,
\\ ReadCapacityUnits INTEGER,
\\ WriteCapacityUnits INTEGER,
\\ TableInfo BLOB,
\\ BillingMode INTEGER DEFAULT 0,
\\ PayPerRequestDateTime INTEGER DEFAULT 0,
\\ PRIMARY KEY(TableName))
, .{}, .{});
if (builtin.is_test and test_retain_db) test_db = db;
return db;
}