75 lines
2.1 KiB
D
75 lines
2.1 KiB
D
module api_modules.auth;
|
|
|
|
import handy_httpd;
|
|
import handy_httpd.components.optional;
|
|
import slf4d;
|
|
import d2sqlite3;
|
|
|
|
import db;
|
|
import data_utils;
|
|
|
|
struct User {
|
|
const ulong id;
|
|
const string username;
|
|
const string passwordHash;
|
|
const ulong createdAt;
|
|
const bool isLocked;
|
|
const bool isAdmin;
|
|
}
|
|
|
|
private struct UserResponse {
|
|
ulong id;
|
|
string username;
|
|
ulong createdAt;
|
|
bool isLocked;
|
|
bool isAdmin;
|
|
}
|
|
|
|
Optional!User getUser(ref HttpRequestContext ctx, ref Database db) {
|
|
import std.base64;
|
|
import std.string : startsWith;
|
|
import std.digest.sha;
|
|
import std.algorithm : countUntil;
|
|
|
|
string headerStr = ctx.request.headers.getFirst("Authorization").orElse("");
|
|
if (headerStr.length == 0 || !startsWith(headerStr, "Basic ")) {
|
|
return Optional!User.empty;
|
|
}
|
|
string encodedCredentials = headerStr[6..$];
|
|
string decoded = cast(string) Base64.decode(encodedCredentials);
|
|
size_t idx = countUntil(decoded, ':');
|
|
string username = decoded[0..idx];
|
|
auto passwordHash = toHexString(sha256Of(decoded[idx+1 .. $]));
|
|
Optional!User optUser = findOne!(User)(db, "SELECT * FROM user WHERE username = ?", username);
|
|
if (!optUser.isNull && optUser.value.passwordHash != passwordHash) {
|
|
return Optional!User.empty;
|
|
}
|
|
return optUser;
|
|
}
|
|
|
|
User getUserOrThrow(ref HttpRequestContext ctx, ref Database db) {
|
|
Optional!User optUser = getUser(ctx, db);
|
|
if (optUser.isNull) {
|
|
throw new HttpStatusException(HttpStatus.UNAUTHORIZED, "Invalid credentials.");
|
|
}
|
|
return optUser.value;
|
|
}
|
|
|
|
void loginEndpoint(ref HttpRequestContext ctx) {
|
|
Database db = getDb();
|
|
Optional!User optUser = getUser(ctx, db);
|
|
if (optUser.isNull) {
|
|
ctx.response.status = HttpStatus.UNAUTHORIZED;
|
|
ctx.response.writeBodyString("Invalid credentials.");
|
|
return;
|
|
}
|
|
infoF!"Login successful for user \"%s\"."(optUser.value.username);
|
|
writeJsonBody(ctx, UserResponse(
|
|
optUser.value.id,
|
|
optUser.value.username,
|
|
optUser.value.createdAt,
|
|
optUser.value.isLocked,
|
|
optUser.value.isAdmin
|
|
));
|
|
}
|