summaryrefslogtreecommitdiff
path: root/backend
diff options
context:
space:
mode:
authorphyscik <mynameisgennadiy@vk.com>2026-04-27 16:52:45 +0500
committerphyscik <mynameisgennadiy@vk.com>2026-04-27 16:52:45 +0500
commit9a43fa05ade031c91d515d1254e05fd33cc7a482 (patch)
treec585ee7725c5e88518e18ff98b93b944caa4520e /backend
parentb5ce961b3ef30758f77e2487dc9b6ed2dd39de73 (diff)
Auth middleware + origin check
Diffstat (limited to 'backend')
-rw-r--r--backend/src/API/WeaponsAPI.zig16
-rw-r--r--backend/src/Database/RangedWeaponsAccessLayer.zig9
-rw-r--r--backend/src/Handler.zig25
-rw-r--r--backend/src/Models/RangedWeapon.zig11
4 files changed, 52 insertions, 9 deletions
diff --git a/backend/src/API/WeaponsAPI.zig b/backend/src/API/WeaponsAPI.zig
index 7c8d72a..101c436 100644
--- a/backend/src/API/WeaponsAPI.zig
+++ b/backend/src/API/WeaponsAPI.zig
@@ -42,7 +42,9 @@ fn getRangedWeaponById(_: *Handler.RequestData, req: *httpz.Request, res: *httpz
try res.json(found, .{});
}
-fn newRangedWeapon(_: *Handler.RequestData, req: *httpz.Request, res: *httpz.Response) !void {
+fn newRangedWeapon(data: *Handler.RequestData, req: *httpz.Request, res: *httpz.Response) !void {
+ try data.CheckAccess(.editor);
+
var body = try req.json(model.RequestBody) orelse {
res.setStatus(.bad_request);
return;
@@ -63,7 +65,9 @@ fn newRangedWeapon(_: *Handler.RequestData, req: *httpz.Request, res: *httpz.Res
res.setStatus(.created);
}
-fn updateRangedWeapon(_: *Handler.RequestData, req: *httpz.Request, res: *httpz.Response) !void {
+fn updateRangedWeapon(data: *Handler.RequestData, req: *httpz.Request, res: *httpz.Response) !void {
+ try data.CheckAccess(.editor);
+
const id = req.param("id") orelse {
res.setStatus(.bad_request);
return;
@@ -85,7 +89,9 @@ fn updateRangedWeapon(_: *Handler.RequestData, req: *httpz.Request, res: *httpz.
};
}
-fn deleteRangedWeapon(_: *Handler.RequestData, req: *httpz.Request, res: *httpz.Response) !void {
+fn deleteRangedWeapon(data: *Handler.RequestData, req: *httpz.Request, res: *httpz.Response) !void {
+ try data.CheckAccess(.editor);
+
const id = req.param("id") orelse {
res.setStatus(.bad_request);
return;
@@ -118,7 +124,9 @@ fn getRangedWeaponDescription(_: *Handler.RequestData, req: *httpz.Request, res:
try res.json(desc, .{});
}
-fn setRangedWeaponDescription(_: *Handler.RequestData, req: *httpz.Request, res: *httpz.Response) !void {
+fn setRangedWeaponDescription(data: *Handler.RequestData, req: *httpz.Request, res: *httpz.Response) !void {
+ try data.CheckAccess(.editor);
+
const id = req.param("id") orelse {
res.setStatus(.bad_request);
return;
diff --git a/backend/src/Database/RangedWeaponsAccessLayer.zig b/backend/src/Database/RangedWeaponsAccessLayer.zig
index 6857581..6170a60 100644
--- a/backend/src/Database/RangedWeaponsAccessLayer.zig
+++ b/backend/src/Database/RangedWeaponsAccessLayer.zig
@@ -46,8 +46,9 @@ pub fn Update(displayName: []const u8, updatedModel: *model.RangedWeaponType) !v
\\ number_of_shots = $9,
\\ rate_of_fire = $10,
\\ reliability = $11,
+ \\ origin = $12,
\\ updated_at = CURRENT_TIMESTAMP
- \\ WHERE id = $12
+ \\ WHERE id = $13
;
_ = try conn.pool.exec(query, .{
@@ -62,6 +63,7 @@ pub fn Update(displayName: []const u8, updatedModel: *model.RangedWeaponType) !v
updatedModel.NumberOfShots,
updatedModel.RateOfFire,
updatedModel.Reliability,
+ updatedModel.Origin,
displayName,
});
}
@@ -70,9 +72,9 @@ pub fn Create(NewWeapon: *model.RangedWeaponType) !void {
if (try Exists(NewWeapon.Id)) return conn.ResultErrors.AlreadyExists;
const query =
\\ INSERT INTO RangedWeapons
- \\ (id, name, weapon_type, accuracy, concealability, availability, damage, ammunition, number_of_shots, rate_of_fire, reliability)
+ \\ (id, name, weapon_type, accuracy, concealability, availability, damage, ammunition, number_of_shots, rate_of_fire, reliability, origin)
\\ VALUES
- \\ ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11)
+ \\ ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12)
;
_ = try conn.pool.exec(query, .{
@@ -87,6 +89,7 @@ pub fn Create(NewWeapon: *model.RangedWeaponType) !void {
NewWeapon.NumberOfShots,
NewWeapon.RateOfFire,
NewWeapon.Reliability,
+ NewWeapon.Origin,
});
}
diff --git a/backend/src/Handler.zig b/backend/src/Handler.zig
index 3e40cf8..2f3389e 100644
--- a/backend/src/Handler.zig
+++ b/backend/src/Handler.zig
@@ -3,6 +3,11 @@ const httpz = @import("httpz");
const userModel = @import("Models/User.zig");
const tokens = @import("Authentication/Tokens.zig");
+pub const errors = error {
+ Unauthorized,
+ Forbidden,
+};
+
pub const RequestData = struct {
User: ?userModel.User,
@@ -32,13 +37,31 @@ pub const RequestData = struct {
if (self.User == null) return false;
return self.User.?.Role >= minimalRole;
}
+
+ pub fn CheckAccess(self: RequestData, minimalRole: userModel.Role) !void {
+ if (self.User == null) return errors.Unauthorized;
+ if (@intFromEnum(self.User.?.Role) < @intFromEnum(minimalRole))
+ return errors.Forbidden;
+ }
};
pub const Handler = struct {
pub fn dispatch(_: *Handler, action: httpz.Action(*RequestData), req: *httpz.Request, res: *httpz.Response) !void {
var data = try RequestData.Init(req);
// std.debug.print("Data: {any}\n", .{ data });
- try action(&data, req, res);
+ action(&data, req, res) catch |err| {
+ switch (err) {
+ errors.Forbidden => {
+ res.setStatus(.forbidden);
+ },
+ errors.Unauthorized => {
+ res.setStatus(.unauthorized);
+ },
+ else => {
+ return err;
+ }
+ }
+ };
std.debug.print("{any} {s}: {d}\n", .{req.method, req.url.raw, res.status});
}
};
diff --git a/backend/src/Models/RangedWeapon.zig b/backend/src/Models/RangedWeapon.zig
index 36e750e..9b65409 100644
--- a/backend/src/Models/RangedWeapon.zig
+++ b/backend/src/Models/RangedWeapon.zig
@@ -18,6 +18,9 @@ pub const RangedWeaponType = struct {
CreatedAt: i64,
UpdatedAt: i64,
+ // The source of the weapom. For example, corebook or an adventure
+ Origin: []const u8,
+
pub fn GetCompactNotation(self: *RangedWeaponType, separator: u8) ![]const u8 {
var b: [64]u8 = undefined;
return try std.fmt.bufPrint(&b,
@@ -52,6 +55,7 @@ pub const RangedWeaponType = struct {
.Reliability = try row.get([]const u8, 10),
.CreatedAt = try row.get(i64, 11),
.UpdatedAt = try row.get(i64, 12),
+ .Origin = try row.get([]const u8, 13),
};
}
@@ -72,6 +76,7 @@ pub const RangedWeaponType = struct {
.Reliability = try allocator.dupe(u8, try row.get([]const u8, 10)),
.CreatedAt = try row.get(i64, 11),
.UpdatedAt = try row.get(i64, 12),
+ .Origin = try allocator.dupe(u8, try row.get([]const u8, 13)),
};
}
@@ -92,6 +97,7 @@ pub const RequestBody = struct {
NumberOfShots: u31,
RateOfFire: u32,
Reliability: []const u8,
+ Origin: []const u8,
pub fn ToModel(this: RequestBody) RangedWeaponType {
return RangedWeaponType {
@@ -107,7 +113,8 @@ pub const RequestBody = struct {
.RateOfFire = this.RateOfFire,
.Reliability = this.Reliability,
.CreatedAt = 0,
- .UpdatedAt = 0
+ .UpdatedAt = 0,
+ .Origin = this.Origin,
};
}
};
@@ -129,6 +136,7 @@ fn getTestType() RangedWeaponType {
.Reliability = "ST",
.CreatedAt = 0,
.UpdatedAt = 0,
+ .Origin = "corebook",
};
}
@@ -153,6 +161,7 @@ test "Request to model" {
.NumberOfShots = 8,
.RateOfFire = 2,
.Reliability = "ST",
+ .Origin = "corebook"
};
const expected = getTestType();