whole bunch of shit
- admin routes - developer status and bots - probably a few other tweaks i forgot
This commit is contained in:
parent
b627ab383f
commit
c68451cf07
21 changed files with 874 additions and 34 deletions
11
CollabVMAuthServer/Bot.cs
Normal file
11
CollabVMAuthServer/Bot.cs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
namespace Computernewb.CollabVMAuthServer;
|
||||||
|
|
||||||
|
public class Bot
|
||||||
|
{
|
||||||
|
public uint Id { get; set; }
|
||||||
|
public string Username { get; set; }
|
||||||
|
public string Token { get; set; }
|
||||||
|
public Rank Rank { get; set; }
|
||||||
|
public string Owner { get; set; }
|
||||||
|
public DateTime Created { get; set; }
|
||||||
|
}
|
|
@ -37,7 +37,9 @@ public class Database
|
||||||
password_reset_code CHAR(8) DEFAULT NULL,
|
password_reset_code CHAR(8) DEFAULT NULL,
|
||||||
cvm_rank INT UNSIGNED NOT NULL DEFAULT 1,
|
cvm_rank INT UNSIGNED NOT NULL DEFAULT 1,
|
||||||
banned BOOLEAN NOT NULL DEFAULT 0,
|
banned BOOLEAN NOT NULL DEFAULT 0,
|
||||||
registration_ip VARBINARY(16) NOT NULL
|
registration_ip VARBINARY(16) NOT NULL,
|
||||||
|
created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
developer BOOLEAN NOT NULL DEFAULT 0
|
||||||
);
|
);
|
||||||
""";
|
""";
|
||||||
await cmd.ExecuteNonQueryAsync();
|
await cmd.ExecuteNonQueryAsync();
|
||||||
|
@ -62,6 +64,18 @@ public class Database
|
||||||
)
|
)
|
||||||
""";
|
""";
|
||||||
await cmd.ExecuteNonQueryAsync();
|
await cmd.ExecuteNonQueryAsync();
|
||||||
|
cmd.CommandText = """
|
||||||
|
CREATE TABLE IF NOT EXISTS bots (
|
||||||
|
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
username VARCHAR(20) NOT NULL UNIQUE KEY,
|
||||||
|
token CHAR(64) NOT NULL UNIQUE KEY,
|
||||||
|
cvm_rank INT UNSIGNED NOT NULL DEFAULT 1,
|
||||||
|
owner VARCHAR(20) NOT NULL,
|
||||||
|
created TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
FOREIGN KEY (owner) REFERENCES users(username) ON UPDATE CASCADE ON DELETE CASCADE
|
||||||
|
)
|
||||||
|
""";
|
||||||
|
await cmd.ExecuteNonQueryAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<User?> GetUser(string? username = null, string? email = null)
|
public async Task<User?> GetUser(string? username = null, string? email = null)
|
||||||
|
@ -96,7 +110,9 @@ public class Database
|
||||||
PasswordResetCode = reader.IsDBNull("password_reset_code") ? null : reader.GetString("password_reset_code"),
|
PasswordResetCode = reader.IsDBNull("password_reset_code") ? null : reader.GetString("password_reset_code"),
|
||||||
Rank = (Rank)reader.GetUInt32("cvm_rank"),
|
Rank = (Rank)reader.GetUInt32("cvm_rank"),
|
||||||
Banned = reader.GetBoolean("banned"),
|
Banned = reader.GetBoolean("banned"),
|
||||||
RegistrationIP = new IPAddress(reader.GetFieldValue<byte[]>("registration_ip"))
|
RegistrationIP = new IPAddress(reader.GetFieldValue<byte[]>("registration_ip")),
|
||||||
|
Joined = reader.GetDateTime("created"),
|
||||||
|
Developer = reader.GetBoolean("developer")
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -230,7 +246,7 @@ public class Database
|
||||||
await cmd.ExecuteNonQueryAsync();
|
await cmd.ExecuteNonQueryAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task UpdateUser(string username, string? newUsername = null, string? newPassword = null, string? newEmail = null)
|
public async Task UpdateUser(string username, string? newUsername = null, string? newPassword = null, string? newEmail = null, int? newRank = null, bool? developer = null)
|
||||||
{
|
{
|
||||||
await using var db = new MySqlConnection(connectionString);
|
await using var db = new MySqlConnection(connectionString);
|
||||||
await db.OpenAsync();
|
await db.OpenAsync();
|
||||||
|
@ -251,6 +267,17 @@ public class Database
|
||||||
updates.Add("email = @newEmail");
|
updates.Add("email = @newEmail");
|
||||||
cmd.Parameters.AddWithValue("@newEmail", newEmail);
|
cmd.Parameters.AddWithValue("@newEmail", newEmail);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (newRank != null)
|
||||||
|
{
|
||||||
|
updates.Add("cvm_rank = @newRank");
|
||||||
|
cmd.Parameters.AddWithValue("@newRank", newRank);
|
||||||
|
}
|
||||||
|
if (developer != null)
|
||||||
|
{
|
||||||
|
updates.Add("developer = @developer");
|
||||||
|
cmd.Parameters.AddWithValue("@developer", developer);
|
||||||
|
}
|
||||||
cmd.CommandText = $"UPDATE users SET {string.Join(", ", updates)} WHERE username = @username";
|
cmd.CommandText = $"UPDATE users SET {string.Join(", ", updates)} WHERE username = @username";
|
||||||
cmd.Parameters.AddWithValue("@username", username);
|
cmd.Parameters.AddWithValue("@username", username);
|
||||||
await cmd.ExecuteNonQueryAsync();
|
await cmd.ExecuteNonQueryAsync();
|
||||||
|
@ -307,4 +334,148 @@ public class Database
|
||||||
cmd.Parameters.AddWithValue("@username", username);
|
cmd.Parameters.AddWithValue("@username", username);
|
||||||
await cmd.ExecuteNonQueryAsync();
|
await cmd.ExecuteNonQueryAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<User[]> ListUsers(string? filterUsername = null, string orderBy = "id", bool descending = false)
|
||||||
|
{
|
||||||
|
await using var db = new MySqlConnection(connectionString);
|
||||||
|
await db.OpenAsync();
|
||||||
|
await using var cmd = db.CreateCommand();
|
||||||
|
var where = new List<string>();
|
||||||
|
if (filterUsername != null)
|
||||||
|
{
|
||||||
|
where.Add("username LIKE @filterUsername");
|
||||||
|
cmd.Parameters.AddWithValue("@filterUsername", filterUsername);
|
||||||
|
}
|
||||||
|
cmd.CommandText = $"SELECT * FROM users {(where.Count > 0 ? "WHERE" : "")} {string.Join(" AND ", where)} ORDER BY {orderBy} {(descending ? "DESC" : "ASC")}";
|
||||||
|
await using var reader = await cmd.ExecuteReaderAsync();
|
||||||
|
var users = new List<User>();
|
||||||
|
while (await reader.ReadAsync())
|
||||||
|
{
|
||||||
|
users.Add(new User
|
||||||
|
{
|
||||||
|
Id = reader.GetUInt32("id"),
|
||||||
|
Username = reader.GetString("username"),
|
||||||
|
Password = reader.GetString("password"),
|
||||||
|
Email = reader.GetString("email"),
|
||||||
|
DateOfBirth = reader.GetDateOnly("date_of_birth"),
|
||||||
|
EmailVerified = reader.GetBoolean("email_verified"),
|
||||||
|
EmailVerificationCode = reader.IsDBNull("email_verification_code") ? null : reader.GetString("email_verification_code"),
|
||||||
|
PasswordResetCode = reader.IsDBNull("password_reset_code") ? null : reader.GetString("password_reset_code"),
|
||||||
|
Rank = (Rank)reader.GetUInt32("cvm_rank"),
|
||||||
|
Banned = reader.GetBoolean("banned"),
|
||||||
|
RegistrationIP = new IPAddress(reader.GetFieldValue<byte[]>("registration_ip")),
|
||||||
|
Joined = reader.GetDateTime("created"),
|
||||||
|
Developer = reader.GetBoolean("developer")
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return users.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task CreateBot(string username, string token, string owner)
|
||||||
|
{
|
||||||
|
await using var db = new MySqlConnection(connectionString);
|
||||||
|
await db.OpenAsync();
|
||||||
|
await using var cmd = db.CreateCommand();
|
||||||
|
cmd.CommandText = "INSERT INTO bots (username, token, owner) VALUES (@username, @token, @owner)";
|
||||||
|
cmd.Parameters.AddWithValue("@username", username);
|
||||||
|
cmd.Parameters.AddWithValue("@token", token);
|
||||||
|
cmd.Parameters.AddWithValue("@owner", owner);
|
||||||
|
await cmd.ExecuteNonQueryAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Bot[]> ListBots(string? owner = null)
|
||||||
|
{
|
||||||
|
await using var db = new MySqlConnection(connectionString);
|
||||||
|
await db.OpenAsync();
|
||||||
|
await using var cmd = db.CreateCommand();
|
||||||
|
var where = new List<string>();
|
||||||
|
if (owner != null)
|
||||||
|
{
|
||||||
|
where.Add("owner = @owner");
|
||||||
|
cmd.Parameters.AddWithValue("@owner", owner);
|
||||||
|
}
|
||||||
|
cmd.CommandText = $"SELECT * FROM bots {(where.Count > 0 ? "WHERE" : "")} {string.Join(" AND ", where)}";
|
||||||
|
await using var reader = await cmd.ExecuteReaderAsync();
|
||||||
|
var bots = new List<Bot>();
|
||||||
|
while (await reader.ReadAsync())
|
||||||
|
{
|
||||||
|
bots.Add(new Bot
|
||||||
|
{
|
||||||
|
Id = reader.GetUInt32("id"),
|
||||||
|
Username = reader.GetString("username"),
|
||||||
|
Token = reader.GetString("token"),
|
||||||
|
Rank = (Rank)reader.GetUInt32("cvm_rank"),
|
||||||
|
Owner = reader.GetString("owner"),
|
||||||
|
Created = reader.GetDateTime("created")
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return bots.ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task UpdateBot(string username, string? newUsername = null, string? newToken = null, int? newRank = null)
|
||||||
|
{
|
||||||
|
await using var db = new MySqlConnection(connectionString);
|
||||||
|
await db.OpenAsync();
|
||||||
|
await using var cmd = db.CreateCommand();
|
||||||
|
var updates = new List<string>();
|
||||||
|
if (newUsername != null)
|
||||||
|
{
|
||||||
|
updates.Add("username = @username");
|
||||||
|
cmd.Parameters.AddWithValue("@username", newUsername);
|
||||||
|
}
|
||||||
|
if (newToken != null)
|
||||||
|
{
|
||||||
|
updates.Add("token = @token");
|
||||||
|
cmd.Parameters.AddWithValue("@token", newToken);
|
||||||
|
}
|
||||||
|
if (newRank != null)
|
||||||
|
{
|
||||||
|
updates.Add("cvm_rank = @rank");
|
||||||
|
cmd.Parameters.AddWithValue("@rank", newRank);
|
||||||
|
}
|
||||||
|
cmd.CommandText = $"UPDATE bots SET {string.Join(", ", updates)} WHERE username = @username";
|
||||||
|
cmd.Parameters.AddWithValue("@username", username);
|
||||||
|
await cmd.ExecuteNonQueryAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task DeleteBots(string owner)
|
||||||
|
{
|
||||||
|
await using var db = new MySqlConnection(connectionString);
|
||||||
|
await db.OpenAsync();
|
||||||
|
await using var cmd = db.CreateCommand();
|
||||||
|
cmd.CommandText = "DELETE FROM bots WHERE owner = @owner";
|
||||||
|
cmd.Parameters.AddWithValue("@owner", owner);
|
||||||
|
await cmd.ExecuteNonQueryAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Bot?> GetBot(string? username = null, string? token = null)
|
||||||
|
{
|
||||||
|
if (username == null && token == null)
|
||||||
|
throw new ArgumentException("username or token must be provided");
|
||||||
|
await using var db = new MySqlConnection(connectionString);
|
||||||
|
await db.OpenAsync();
|
||||||
|
await using var cmd = db.CreateCommand();
|
||||||
|
if (username != null)
|
||||||
|
{
|
||||||
|
cmd.CommandText = "SELECT * FROM bots WHERE username = @username";
|
||||||
|
cmd.Parameters.AddWithValue("@username", username);
|
||||||
|
}
|
||||||
|
else if (token != null)
|
||||||
|
{
|
||||||
|
cmd.CommandText = "SELECT * FROM bots WHERE token = @token";
|
||||||
|
cmd.Parameters.AddWithValue("@token", token);
|
||||||
|
}
|
||||||
|
await using var reader = await cmd.ExecuteReaderAsync();
|
||||||
|
if (!await reader.ReadAsync())
|
||||||
|
return null;
|
||||||
|
return new Bot
|
||||||
|
{
|
||||||
|
Id = reader.GetUInt32("id"),
|
||||||
|
Username = reader.GetString("username"),
|
||||||
|
Token = reader.GetString("token"),
|
||||||
|
Rank = (Rank)reader.GetUInt32("cvm_rank"),
|
||||||
|
Owner = reader.GetString("owner"),
|
||||||
|
Created = reader.GetDateTime("created")
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
309
CollabVMAuthServer/HTTP/AdminRoutes.cs
Normal file
309
CollabVMAuthServer/HTTP/AdminRoutes.cs
Normal file
|
@ -0,0 +1,309 @@
|
||||||
|
using System.Text.Json;
|
||||||
|
using Computernewb.CollabVMAuthServer.HTTP.Payloads;
|
||||||
|
using Computernewb.CollabVMAuthServer.HTTP.Responses;
|
||||||
|
|
||||||
|
namespace Computernewb.CollabVMAuthServer.HTTP;
|
||||||
|
|
||||||
|
public static class AdminRoutes
|
||||||
|
{
|
||||||
|
public static void RegisterRoutes(IEndpointRouteBuilder app)
|
||||||
|
{
|
||||||
|
app.MapPost("/api/v1/admin/users", (Delegate)HandleAdminUsers);
|
||||||
|
app.MapPost("/api/v1/admin/updateuser", (Delegate)HandleAdminUpdateUser);
|
||||||
|
app.MapPost("/api/v1/admin/updatebot", (Delegate)HandleAdminUpdateBot);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task<IResult> HandleAdminUpdateBot(HttpContext context)
|
||||||
|
{
|
||||||
|
// Check payload
|
||||||
|
if (context.Request.ContentType != "application/json")
|
||||||
|
{
|
||||||
|
context.Response.StatusCode = 400;
|
||||||
|
return Results.Json(new AdminUpdateBotResponse
|
||||||
|
{
|
||||||
|
success = false,
|
||||||
|
error = "Invalid request body"
|
||||||
|
}, Utilities.JsonSerializerOptions);
|
||||||
|
}
|
||||||
|
AdminUpdateBotPayload? payload;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
payload = await context.Request.ReadFromJsonAsync<AdminUpdateBotPayload>();
|
||||||
|
}
|
||||||
|
catch (JsonException ex)
|
||||||
|
{
|
||||||
|
Utilities.Log(LogLevel.DEBUG, $"Failed to parse JSON: {ex.Message}");
|
||||||
|
context.Response.StatusCode = 400;
|
||||||
|
return Results.Json(new AdminUpdateBotResponse
|
||||||
|
{
|
||||||
|
success = false,
|
||||||
|
error = "Invalid request body"
|
||||||
|
}, Utilities.JsonSerializerOptions);
|
||||||
|
}
|
||||||
|
if (payload == null || string.IsNullOrWhiteSpace(payload.token) || string.IsNullOrWhiteSpace(payload.username))
|
||||||
|
{
|
||||||
|
context.Response.StatusCode = 400;
|
||||||
|
return Results.Json(new AdminUpdateBotResponse
|
||||||
|
{
|
||||||
|
success = false,
|
||||||
|
error = "Invalid request body"
|
||||||
|
}, Utilities.JsonSerializerOptions);
|
||||||
|
}
|
||||||
|
// Check token
|
||||||
|
var session = await Program.Database.GetSession(payload.token);
|
||||||
|
if (session == null || Utilities.IsSessionExpired(session))
|
||||||
|
{
|
||||||
|
context.Response.StatusCode = 400;
|
||||||
|
return Results.Json(new AdminUpdateBotResponse
|
||||||
|
{
|
||||||
|
success = false,
|
||||||
|
error = "Invalid session"
|
||||||
|
}, Utilities.JsonSerializerOptions);
|
||||||
|
}
|
||||||
|
// Check rank
|
||||||
|
var user = await Program.Database.GetUser(session.Username)
|
||||||
|
?? throw new Exception("Could not lookup user from session");
|
||||||
|
if (user.Rank != Rank.Admin)
|
||||||
|
{
|
||||||
|
context.Response.StatusCode = 403;
|
||||||
|
return Results.Json(new AdminUsersResponse
|
||||||
|
{
|
||||||
|
success = false,
|
||||||
|
error = "Insufficient permissions"
|
||||||
|
}, Utilities.JsonSerializerOptions);
|
||||||
|
}
|
||||||
|
// Check target bot
|
||||||
|
var targetBot = await Program.Database.GetBot(payload.username);
|
||||||
|
if (targetBot == null)
|
||||||
|
{
|
||||||
|
context.Response.StatusCode = 400;
|
||||||
|
return Results.Json(new AdminUpdateBotResponse
|
||||||
|
{
|
||||||
|
success = false,
|
||||||
|
error = "Bot not found"
|
||||||
|
}, Utilities.JsonSerializerOptions);
|
||||||
|
}
|
||||||
|
// Make sure at least one field is being updated
|
||||||
|
if (payload.rank == null)
|
||||||
|
{
|
||||||
|
context.Response.StatusCode = 400;
|
||||||
|
return Results.Json(new AdminUpdateBotResponse
|
||||||
|
{
|
||||||
|
success = false,
|
||||||
|
error = "No fields to update"
|
||||||
|
}, Utilities.JsonSerializerOptions);
|
||||||
|
}
|
||||||
|
// Check rank
|
||||||
|
int? rank = payload.rank;
|
||||||
|
if (rank != null && rank < 1 || rank > 3)
|
||||||
|
{
|
||||||
|
context.Response.StatusCode = 400;
|
||||||
|
return Results.Json(new AdminUpdateBotResponse
|
||||||
|
{
|
||||||
|
success = false,
|
||||||
|
error = "Invalid rank"
|
||||||
|
}, Utilities.JsonSerializerOptions);
|
||||||
|
}
|
||||||
|
// Update rank
|
||||||
|
await Program.Database.UpdateBot(targetBot.Username, newRank: payload.rank);
|
||||||
|
return Results.Json(new AdminUpdateBotResponse
|
||||||
|
{
|
||||||
|
success = true
|
||||||
|
}, Utilities.JsonSerializerOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task<IResult> HandleAdminUpdateUser(HttpContext context)
|
||||||
|
{
|
||||||
|
// Check payload
|
||||||
|
if (context.Request.ContentType != "application/json")
|
||||||
|
{
|
||||||
|
context.Response.StatusCode = 400;
|
||||||
|
return Results.Json(new AdminUpdateUserResponse
|
||||||
|
{
|
||||||
|
success = false,
|
||||||
|
error = "Invalid request body"
|
||||||
|
}, Utilities.JsonSerializerOptions);
|
||||||
|
}
|
||||||
|
AdminUpdateUserPayload? payload;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
payload = await context.Request.ReadFromJsonAsync<AdminUpdateUserPayload>();
|
||||||
|
}
|
||||||
|
catch (JsonException ex)
|
||||||
|
{
|
||||||
|
Utilities.Log(LogLevel.DEBUG, $"Failed to parse JSON: {ex.Message}");
|
||||||
|
context.Response.StatusCode = 400;
|
||||||
|
return Results.Json(new AdminUpdateUserResponse
|
||||||
|
{
|
||||||
|
success = false,
|
||||||
|
error = "Invalid request body"
|
||||||
|
}, Utilities.JsonSerializerOptions);
|
||||||
|
}
|
||||||
|
if (payload == null || string.IsNullOrWhiteSpace(payload.token) || string.IsNullOrWhiteSpace(payload.username))
|
||||||
|
{
|
||||||
|
context.Response.StatusCode = 400;
|
||||||
|
return Results.Json(new AdminUpdateUserResponse
|
||||||
|
{
|
||||||
|
success = false,
|
||||||
|
error = "Invalid request body"
|
||||||
|
}, Utilities.JsonSerializerOptions);
|
||||||
|
}
|
||||||
|
// Check token
|
||||||
|
var session = await Program.Database.GetSession(payload.token);
|
||||||
|
if (session == null || Utilities.IsSessionExpired(session))
|
||||||
|
{
|
||||||
|
context.Response.StatusCode = 400;
|
||||||
|
return Results.Json(new AdminUpdateUserResponse
|
||||||
|
{
|
||||||
|
success = false,
|
||||||
|
error = "Invalid session"
|
||||||
|
}, Utilities.JsonSerializerOptions);
|
||||||
|
}
|
||||||
|
// Check rank
|
||||||
|
var user = await Program.Database.GetUser(session.Username)
|
||||||
|
?? throw new Exception("Could not lookup user from session");
|
||||||
|
if (user.Rank != Rank.Admin)
|
||||||
|
{
|
||||||
|
context.Response.StatusCode = 403;
|
||||||
|
return Results.Json(new AdminUsersResponse
|
||||||
|
{
|
||||||
|
success = false,
|
||||||
|
error = "Insufficient permissions"
|
||||||
|
}, Utilities.JsonSerializerOptions);
|
||||||
|
}
|
||||||
|
// Check target user
|
||||||
|
var targetUser = await Program.Database.GetUser(payload.username);
|
||||||
|
if (targetUser == null)
|
||||||
|
{
|
||||||
|
context.Response.StatusCode = 400;
|
||||||
|
return Results.Json(new AdminUpdateUserResponse
|
||||||
|
{
|
||||||
|
success = false,
|
||||||
|
error = "User not found"
|
||||||
|
}, Utilities.JsonSerializerOptions);
|
||||||
|
}
|
||||||
|
// Check rank
|
||||||
|
int? rank = payload.rank;
|
||||||
|
if (rank != null && rank < 1 || rank > 3)
|
||||||
|
{
|
||||||
|
context.Response.StatusCode = 400;
|
||||||
|
return Results.Json(new AdminUpdateUserResponse
|
||||||
|
{
|
||||||
|
success = false,
|
||||||
|
error = "Invalid rank"
|
||||||
|
}, Utilities.JsonSerializerOptions);
|
||||||
|
}
|
||||||
|
// Check developer
|
||||||
|
bool? developer = payload.developer;
|
||||||
|
// Update rank
|
||||||
|
await Program.Database.UpdateUser(targetUser.Username, newRank: payload.rank, developer: developer);
|
||||||
|
if (developer == false)
|
||||||
|
{
|
||||||
|
await Program.Database.DeleteBots(targetUser.Username);
|
||||||
|
}
|
||||||
|
return Results.Json(new AdminUpdateUserResponse
|
||||||
|
{
|
||||||
|
success = true
|
||||||
|
}, Utilities.JsonSerializerOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task<IResult> HandleAdminUsers(HttpContext context)
|
||||||
|
{
|
||||||
|
// Check payload
|
||||||
|
if (context.Request.ContentType != "application/json")
|
||||||
|
{
|
||||||
|
context.Response.StatusCode = 400;
|
||||||
|
return Results.Json(new AdminUsersResponse
|
||||||
|
{
|
||||||
|
success = false,
|
||||||
|
error = "Invalid request body"
|
||||||
|
}, Utilities.JsonSerializerOptions);
|
||||||
|
}
|
||||||
|
AdminUsersPayload? payload;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
payload = await context.Request.ReadFromJsonAsync<AdminUsersPayload>();
|
||||||
|
}
|
||||||
|
catch (JsonException ex)
|
||||||
|
{
|
||||||
|
Utilities.Log(LogLevel.DEBUG, $"Failed to parse JSON: {ex.Message}");
|
||||||
|
context.Response.StatusCode = 400;
|
||||||
|
return Results.Json(new AdminUsersResponse
|
||||||
|
{
|
||||||
|
success = false,
|
||||||
|
error = "Invalid request body"
|
||||||
|
}, Utilities.JsonSerializerOptions);
|
||||||
|
}
|
||||||
|
if (payload == null || string.IsNullOrWhiteSpace(payload.token) || payload.page < 1 || payload.resultsPerPage < 1)
|
||||||
|
{
|
||||||
|
context.Response.StatusCode = 400;
|
||||||
|
return Results.Json(new AdminUsersResponse
|
||||||
|
{
|
||||||
|
success = false,
|
||||||
|
error = "Invalid request body"
|
||||||
|
}, Utilities.JsonSerializerOptions);
|
||||||
|
}
|
||||||
|
// Check token
|
||||||
|
var session = await Program.Database.GetSession(payload.token);
|
||||||
|
if (session == null || Utilities.IsSessionExpired(session))
|
||||||
|
{
|
||||||
|
context.Response.StatusCode = 400;
|
||||||
|
return Results.Json(new AdminUsersResponse
|
||||||
|
{
|
||||||
|
success = false,
|
||||||
|
error = "Invalid session"
|
||||||
|
}, Utilities.JsonSerializerOptions);
|
||||||
|
}
|
||||||
|
// Check rank
|
||||||
|
var user = await Program.Database.GetUser(session.Username)
|
||||||
|
?? throw new Exception("Could not lookup user from session");
|
||||||
|
if (user.Rank != Rank.Admin)
|
||||||
|
{
|
||||||
|
context.Response.StatusCode = 403;
|
||||||
|
return Results.Json(new AdminUsersResponse
|
||||||
|
{
|
||||||
|
success = false,
|
||||||
|
error = "Insufficient permissions"
|
||||||
|
}, Utilities.JsonSerializerOptions);
|
||||||
|
}
|
||||||
|
// Validate orderBy
|
||||||
|
if (payload.orderBy != null && !new string[] { "id", "username", "email", "date_of_birth", "cvm_rank", "banned", "created" }.Contains(payload.orderBy))
|
||||||
|
{
|
||||||
|
context.Response.StatusCode = 400;
|
||||||
|
return Results.Json(new AdminUsersResponse
|
||||||
|
{
|
||||||
|
success = false,
|
||||||
|
error = "Invalid orderBy"
|
||||||
|
}, Utilities.JsonSerializerOptions);
|
||||||
|
}
|
||||||
|
// Get users
|
||||||
|
string? filterUsername = null;
|
||||||
|
if (payload.filterUsername != null)
|
||||||
|
{
|
||||||
|
filterUsername = "%" + payload.filterUsername
|
||||||
|
.Replace("%", "!%")
|
||||||
|
.Replace("!", "!!")
|
||||||
|
.Replace("_", "!_")
|
||||||
|
.Replace("[", "![") + "%";
|
||||||
|
}
|
||||||
|
var users = (await Program.Database.ListUsers(filterUsername, payload.orderBy ?? "id", payload.orderByDescending)).Select(user => new AdminUser
|
||||||
|
{
|
||||||
|
id = user.Id,
|
||||||
|
username = user.Username,
|
||||||
|
email = user.Email,
|
||||||
|
rank = (int)user.Rank,
|
||||||
|
banned = user.Banned,
|
||||||
|
dateOfBirth = user.DateOfBirth.ToString("yyyy-MM-dd"),
|
||||||
|
dateJoined = user.Joined.ToString("yyyy-MM-dd HH:mm:ss"),
|
||||||
|
registrationIp = user.RegistrationIP.ToString(),
|
||||||
|
developer = user.Developer
|
||||||
|
}).ToArray();
|
||||||
|
var page = users.Skip((payload.page - 1) * payload.resultsPerPage).Take(payload.resultsPerPage).ToArray();
|
||||||
|
return Results.Json(new AdminUsersResponse
|
||||||
|
{
|
||||||
|
success = true,
|
||||||
|
users = page,
|
||||||
|
totalPageCount = (int)Math.Ceiling(users.Length / (double)payload.resultsPerPage)
|
||||||
|
}, Utilities.JsonSerializerOptions);
|
||||||
|
}
|
||||||
|
}
|
196
CollabVMAuthServer/HTTP/DeveloperRoutes.cs
Normal file
196
CollabVMAuthServer/HTTP/DeveloperRoutes.cs
Normal file
|
@ -0,0 +1,196 @@
|
||||||
|
using System.Text.Json;
|
||||||
|
using Computernewb.CollabVMAuthServer.HTTP.Payloads;
|
||||||
|
using Computernewb.CollabVMAuthServer.HTTP.Responses;
|
||||||
|
|
||||||
|
namespace Computernewb.CollabVMAuthServer.HTTP;
|
||||||
|
|
||||||
|
public static class DeveloperRoutes
|
||||||
|
{
|
||||||
|
public static void RegisterRoutes(IEndpointRouteBuilder app)
|
||||||
|
{
|
||||||
|
app.MapPost("/api/v1/bots/create", (Delegate)HandleCreateBot);
|
||||||
|
app.MapPost("/api/v1/bots/list", (Delegate)HandleListBots);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task<IResult> HandleListBots(HttpContext context)
|
||||||
|
{
|
||||||
|
// Check payload
|
||||||
|
if (context.Request.ContentType != "application/json")
|
||||||
|
{
|
||||||
|
context.Response.StatusCode = 400;
|
||||||
|
return Results.Json(new ListBotsResponse
|
||||||
|
{
|
||||||
|
success = false,
|
||||||
|
error = "Invalid request body"
|
||||||
|
}, Utilities.JsonSerializerOptions);
|
||||||
|
}
|
||||||
|
ListBotsPayload? payload;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
payload = await context.Request.ReadFromJsonAsync<ListBotsPayload>();
|
||||||
|
}
|
||||||
|
catch (JsonException ex)
|
||||||
|
{
|
||||||
|
Utilities.Log(LogLevel.DEBUG, $"Failed to parse JSON: {ex.Message}");
|
||||||
|
context.Response.StatusCode = 400;
|
||||||
|
return Results.Json(new ListBotsResponse
|
||||||
|
{
|
||||||
|
success = false,
|
||||||
|
error = "Invalid request body"
|
||||||
|
}, Utilities.JsonSerializerOptions);
|
||||||
|
}
|
||||||
|
if (payload == null || string.IsNullOrWhiteSpace(payload.token) || payload.resultsPerPage <= 0 ||
|
||||||
|
payload.page <= 0)
|
||||||
|
{
|
||||||
|
context.Response.StatusCode = 400;
|
||||||
|
return Results.Json(new ListBotsResponse
|
||||||
|
{
|
||||||
|
success = false,
|
||||||
|
error = "Invalid request body"
|
||||||
|
}, Utilities.JsonSerializerOptions);
|
||||||
|
}
|
||||||
|
// Check token
|
||||||
|
var session = await Program.Database.GetSession(payload.token);
|
||||||
|
if (session == null || Utilities.IsSessionExpired(session))
|
||||||
|
{
|
||||||
|
context.Response.StatusCode = 400;
|
||||||
|
return Results.Json(new ListBotsResponse
|
||||||
|
{
|
||||||
|
success = false,
|
||||||
|
error = "Invalid session"
|
||||||
|
}, Utilities.JsonSerializerOptions);
|
||||||
|
}
|
||||||
|
// Check developer status
|
||||||
|
var user = await Program.Database.GetUser(session.Username) ??
|
||||||
|
throw new Exception("Unable to get user from session");
|
||||||
|
if (!user.Developer && user.Rank != Rank.Admin)
|
||||||
|
{
|
||||||
|
context.Response.StatusCode = 403;
|
||||||
|
return Results.Json(new CreateBotResponse
|
||||||
|
{
|
||||||
|
success = false,
|
||||||
|
error = "You must be an approved developer to create and manage bots."
|
||||||
|
}, Utilities.JsonSerializerOptions);
|
||||||
|
}
|
||||||
|
// owner can only be specified by admins
|
||||||
|
if (payload.owner != null && user.Rank != Rank.Admin)
|
||||||
|
{
|
||||||
|
context.Response.StatusCode = 403;
|
||||||
|
return Results.Json(new ListBotsResponse
|
||||||
|
{
|
||||||
|
success = false,
|
||||||
|
error = "Insufficient permissions"
|
||||||
|
}, Utilities.JsonSerializerOptions);
|
||||||
|
}
|
||||||
|
// Get bots
|
||||||
|
// If the user is not an admin, they can only see their own bots
|
||||||
|
var bots = (await Program.Database.ListBots(payload.owner ?? (user.Rank == Rank.Admin ? null : user.Username))).Select(bot => new ListBot
|
||||||
|
{
|
||||||
|
id = (int)bot.Id,
|
||||||
|
username = bot.Username,
|
||||||
|
rank = (int)bot.Rank,
|
||||||
|
owner = bot.Owner,
|
||||||
|
created = bot.Created.ToString("yyyy-MM-dd HH:mm:ss")
|
||||||
|
|
||||||
|
});
|
||||||
|
var page = bots.Skip((payload.page - 1) * payload.resultsPerPage).Take(payload.resultsPerPage).ToArray();
|
||||||
|
return Results.Json(new ListBotsResponse
|
||||||
|
{
|
||||||
|
success = true,
|
||||||
|
totalPageCount = (int)Math.Ceiling(bots.Count() / (double)payload.resultsPerPage),
|
||||||
|
bots = page
|
||||||
|
}, Utilities.JsonSerializerOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static async Task<IResult> HandleCreateBot(HttpContext context)
|
||||||
|
{
|
||||||
|
// Check payload
|
||||||
|
if (context.Request.ContentType != "application/json")
|
||||||
|
{
|
||||||
|
context.Response.StatusCode = 400;
|
||||||
|
return Results.Json(new CreateBotResponse
|
||||||
|
{
|
||||||
|
success = false,
|
||||||
|
error = "Invalid request body"
|
||||||
|
}, Utilities.JsonSerializerOptions);
|
||||||
|
}
|
||||||
|
CreateBotPayload? payload;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
payload = await context.Request.ReadFromJsonAsync<CreateBotPayload>();
|
||||||
|
}
|
||||||
|
catch (JsonException ex)
|
||||||
|
{
|
||||||
|
Utilities.Log(LogLevel.DEBUG, $"Failed to parse JSON: {ex.Message}");
|
||||||
|
context.Response.StatusCode = 400;
|
||||||
|
return Results.Json(new CreateBotResponse
|
||||||
|
{
|
||||||
|
success = false,
|
||||||
|
error = "Invalid request body"
|
||||||
|
}, Utilities.JsonSerializerOptions);
|
||||||
|
}
|
||||||
|
if (payload == null || string.IsNullOrWhiteSpace(payload.token) || string.IsNullOrWhiteSpace(payload.username))
|
||||||
|
{
|
||||||
|
context.Response.StatusCode = 400;
|
||||||
|
return Results.Json(new CreateBotResponse
|
||||||
|
{
|
||||||
|
success = false,
|
||||||
|
error = "Invalid request body"
|
||||||
|
}, Utilities.JsonSerializerOptions);
|
||||||
|
}
|
||||||
|
// Check token
|
||||||
|
var session = await Program.Database.GetSession(payload.token);
|
||||||
|
if (session == null || Utilities.IsSessionExpired(session))
|
||||||
|
{
|
||||||
|
context.Response.StatusCode = 400;
|
||||||
|
return Results.Json(new CreateBotResponse
|
||||||
|
{
|
||||||
|
success = false,
|
||||||
|
error = "Invalid session"
|
||||||
|
}, Utilities.JsonSerializerOptions);
|
||||||
|
}
|
||||||
|
// Check developer status
|
||||||
|
var user = await Program.Database.GetUser(session.Username) ??
|
||||||
|
throw new Exception("Unable to get user from session");
|
||||||
|
if (!user.Developer)
|
||||||
|
{
|
||||||
|
context.Response.StatusCode = 403;
|
||||||
|
return Results.Json(new CreateBotResponse
|
||||||
|
{
|
||||||
|
success = false,
|
||||||
|
error = "You must be an approved developer to create and manage bots."
|
||||||
|
}, Utilities.JsonSerializerOptions);
|
||||||
|
}
|
||||||
|
// Check bot username
|
||||||
|
if (await Program.Database.GetBot(payload.username) != null ||
|
||||||
|
await Program.Database.GetUser(payload.username) != null)
|
||||||
|
{
|
||||||
|
context.Response.StatusCode = 400;
|
||||||
|
return Results.Json(new CreateBotResponse
|
||||||
|
{
|
||||||
|
success = false,
|
||||||
|
error = "That username is taken."
|
||||||
|
}, Utilities.JsonSerializerOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Utilities.ValidateUsername(payload.username))
|
||||||
|
{
|
||||||
|
context.Response.StatusCode = 400;
|
||||||
|
return Results.Json(new CreateBotResponse
|
||||||
|
{
|
||||||
|
success = false,
|
||||||
|
error =
|
||||||
|
"Usernames can contain only numbers, letters, spaces, dashes, underscores, and dots, and must be between 3 and 20 characters."
|
||||||
|
}, Utilities.JsonSerializerOptions);
|
||||||
|
}
|
||||||
|
// Generate token
|
||||||
|
string token = Utilities.RandomString(64);
|
||||||
|
// Create bot
|
||||||
|
await Program.Database.CreateBot(payload.username, token, user.Username);
|
||||||
|
return Results.Json(new CreateBotResponse
|
||||||
|
{
|
||||||
|
success = true,
|
||||||
|
token = token
|
||||||
|
}, Utilities.JsonSerializerOptions);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
namespace Computernewb.CollabVMAuthServer.HTTP.Payloads;
|
||||||
|
|
||||||
|
public class AdminUpdateBotPayload
|
||||||
|
{
|
||||||
|
public string token { get; set; }
|
||||||
|
public string username { get; set; }
|
||||||
|
public int? rank { get; set; }
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
namespace Computernewb.CollabVMAuthServer.HTTP.Payloads;
|
||||||
|
|
||||||
|
public class AdminUpdateUserPayload
|
||||||
|
{
|
||||||
|
public string token { get; set; }
|
||||||
|
public string username { get; set; }
|
||||||
|
public int? rank { get; set; }
|
||||||
|
public bool? developer { get; set; } = null;
|
||||||
|
}
|
11
CollabVMAuthServer/HTTP/Payloads/AdminUsersPayload.cs
Normal file
11
CollabVMAuthServer/HTTP/Payloads/AdminUsersPayload.cs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
namespace Computernewb.CollabVMAuthServer.HTTP.Payloads;
|
||||||
|
|
||||||
|
public class AdminUsersPayload
|
||||||
|
{
|
||||||
|
public string token { get; set; }
|
||||||
|
public int resultsPerPage { get; set; }
|
||||||
|
public int page { get; set; }
|
||||||
|
public string? filterUsername { get; set; }
|
||||||
|
public string? orderBy { get; set; }
|
||||||
|
public bool orderByDescending { get; set; } = false;
|
||||||
|
}
|
7
CollabVMAuthServer/HTTP/Payloads/CreateBotPayload.cs
Normal file
7
CollabVMAuthServer/HTTP/Payloads/CreateBotPayload.cs
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
namespace Computernewb.CollabVMAuthServer.HTTP.Payloads;
|
||||||
|
|
||||||
|
public class CreateBotPayload
|
||||||
|
{
|
||||||
|
public string token { get; set; }
|
||||||
|
public string username { get; set; }
|
||||||
|
}
|
9
CollabVMAuthServer/HTTP/Payloads/ListBotsPayload.cs
Normal file
9
CollabVMAuthServer/HTTP/Payloads/ListBotsPayload.cs
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
namespace Computernewb.CollabVMAuthServer.HTTP.Payloads;
|
||||||
|
|
||||||
|
public class ListBotsPayload
|
||||||
|
{
|
||||||
|
public string token { get; set; }
|
||||||
|
public int resultsPerPage { get; set; }
|
||||||
|
public int page { get; set; }
|
||||||
|
public string? owner { get; set; }
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
namespace Computernewb.CollabVMAuthServer.HTTP.Responses;
|
||||||
|
|
||||||
|
public class AdminUpdateBotResponse
|
||||||
|
{
|
||||||
|
public bool success { get; set; }
|
||||||
|
public string? error { get; set; }
|
||||||
|
}
|
|
@ -0,0 +1,7 @@
|
||||||
|
namespace Computernewb.CollabVMAuthServer.HTTP.Responses;
|
||||||
|
|
||||||
|
public class AdminUpdateUserResponse
|
||||||
|
{
|
||||||
|
public bool success { get; set; }
|
||||||
|
public string? error { get; set; }
|
||||||
|
}
|
22
CollabVMAuthServer/HTTP/Responses/AdminUsersResponse.cs
Normal file
22
CollabVMAuthServer/HTTP/Responses/AdminUsersResponse.cs
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
namespace Computernewb.CollabVMAuthServer.HTTP.Responses;
|
||||||
|
|
||||||
|
public class AdminUsersResponse
|
||||||
|
{
|
||||||
|
public bool success { get; set; }
|
||||||
|
public string? error { get; set; }
|
||||||
|
public int? totalPageCount { get; set; } = null;
|
||||||
|
public AdminUser[]? users { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class AdminUser
|
||||||
|
{
|
||||||
|
public uint id { get; set; }
|
||||||
|
public string username { get; set; }
|
||||||
|
public string email { get; set; }
|
||||||
|
public int rank { get; set; }
|
||||||
|
public bool banned { get; set; }
|
||||||
|
public string dateOfBirth { get; set; }
|
||||||
|
public string dateJoined { get; set; }
|
||||||
|
public string registrationIp { get; set; }
|
||||||
|
public bool developer { get; set; }
|
||||||
|
}
|
8
CollabVMAuthServer/HTTP/Responses/CreateBotResponse.cs
Normal file
8
CollabVMAuthServer/HTTP/Responses/CreateBotResponse.cs
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
namespace Computernewb.CollabVMAuthServer.HTTP.Responses;
|
||||||
|
|
||||||
|
public class CreateBotResponse
|
||||||
|
{
|
||||||
|
public bool success { get; set; }
|
||||||
|
public string? error { get; set; }
|
||||||
|
public string? token { get; set; }
|
||||||
|
}
|
10
CollabVMAuthServer/HTTP/Responses/ListBot.cs
Normal file
10
CollabVMAuthServer/HTTP/Responses/ListBot.cs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
namespace Computernewb.CollabVMAuthServer.HTTP.Responses;
|
||||||
|
|
||||||
|
public class ListBot
|
||||||
|
{
|
||||||
|
public int id { get; set; }
|
||||||
|
public string username { get; set; }
|
||||||
|
public int rank { get; set; }
|
||||||
|
public string owner { get; set; }
|
||||||
|
public string created { get; set; }
|
||||||
|
}
|
9
CollabVMAuthServer/HTTP/Responses/ListBotsResponse.cs
Normal file
9
CollabVMAuthServer/HTTP/Responses/ListBotsResponse.cs
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
namespace Computernewb.CollabVMAuthServer.HTTP.Responses;
|
||||||
|
|
||||||
|
public class ListBotsResponse
|
||||||
|
{
|
||||||
|
public bool success { get; set; }
|
||||||
|
public string? error { get; set; }
|
||||||
|
public int? totalPageCount { get; set; } = null;
|
||||||
|
public ListBot[]? bots { get; set; }
|
||||||
|
}
|
|
@ -8,4 +8,5 @@ public class LoginResponse
|
||||||
public bool? verificationRequired { get; set; }
|
public bool? verificationRequired { get; set; }
|
||||||
public string? email { get; set; }
|
public string? email { get; set; }
|
||||||
public string? username { get; set; }
|
public string? username { get; set; }
|
||||||
|
public int rank { get; set; }
|
||||||
}
|
}
|
|
@ -7,4 +7,5 @@ public class SessionResponse
|
||||||
public bool banned { get; set; } = false;
|
public bool banned { get; set; } = false;
|
||||||
public string? username { get; set; }
|
public string? username { get; set; }
|
||||||
public string? email { get; set; }
|
public string? email { get; set; }
|
||||||
|
public int rank { get; set; }
|
||||||
}
|
}
|
|
@ -269,8 +269,7 @@ public static class Routes
|
||||||
}, Utilities.JsonSerializerOptions);
|
}, Utilities.JsonSerializerOptions);
|
||||||
}
|
}
|
||||||
// Make sure username isn't taken
|
// Make sure username isn't taken
|
||||||
var _user = await Program.Database.GetUser(payload.username);
|
if (await Program.Database.GetUser(payload.username) != null || await Program.Database.GetBot(payload.username) != null)
|
||||||
if (_user != null)
|
|
||||||
{
|
{
|
||||||
context.Response.StatusCode = 400;
|
context.Response.StatusCode = 400;
|
||||||
return Results.Json(new RegisterResponse
|
return Results.Json(new RegisterResponse
|
||||||
|
@ -462,7 +461,7 @@ public static class Routes
|
||||||
}, Utilities.JsonSerializerOptions);
|
}, Utilities.JsonSerializerOptions);
|
||||||
}
|
}
|
||||||
// Check if session is expired
|
// Check if session is expired
|
||||||
if (DateTime.Now > session.LastUsed.AddDays(Program.Config.Accounts.SessionExpiryDays))
|
if (Utilities.IsSessionExpired(session))
|
||||||
{
|
{
|
||||||
return Results.Json(new SessionResponse
|
return Results.Json(new SessionResponse
|
||||||
{
|
{
|
||||||
|
@ -477,7 +476,8 @@ public static class Routes
|
||||||
success = true,
|
success = true,
|
||||||
banned = user.Banned,
|
banned = user.Banned,
|
||||||
username = user.Username,
|
username = user.Username,
|
||||||
email = user.Email
|
email = user.Email,
|
||||||
|
rank = (int)user.Rank
|
||||||
}, Utilities.JsonSerializerOptions);
|
}, Utilities.JsonSerializerOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -549,47 +549,80 @@ public static class Routes
|
||||||
}, Utilities.JsonSerializerOptions);
|
}, Utilities.JsonSerializerOptions);
|
||||||
}
|
}
|
||||||
// Check if session is valid
|
// Check if session is valid
|
||||||
var session = await Program.Database.GetSession(payload.sessionToken);
|
if (payload.sessionToken.Length == 32)
|
||||||
if (session == null)
|
|
||||||
{
|
{
|
||||||
|
// User
|
||||||
|
var session = await Program.Database.GetSession(payload.sessionToken);
|
||||||
|
if (session == null)
|
||||||
|
{
|
||||||
|
return Results.Json(new JoinResponse
|
||||||
|
{
|
||||||
|
success = true,
|
||||||
|
clientSuccess = false,
|
||||||
|
error = "Invalid session",
|
||||||
|
}, Utilities.JsonSerializerOptions);
|
||||||
|
}
|
||||||
|
// Check if session is expired
|
||||||
|
if (DateTime.Now > session.LastUsed.AddDays(Program.Config.Accounts.SessionExpiryDays))
|
||||||
|
{
|
||||||
|
return Results.Json(new JoinResponse
|
||||||
|
{
|
||||||
|
success = true,
|
||||||
|
clientSuccess = false,
|
||||||
|
error = "Invalid session",
|
||||||
|
}, Utilities.JsonSerializerOptions);
|
||||||
|
}
|
||||||
|
// Check if banned
|
||||||
|
var user = await Program.Database.GetUser(session.Username)
|
||||||
|
?? throw new Exception("User not found in database (something is very wrong)");
|
||||||
|
if (user.Banned)
|
||||||
|
{
|
||||||
|
return Results.Json(new JoinResponse
|
||||||
|
{
|
||||||
|
success = true,
|
||||||
|
clientSuccess = false,
|
||||||
|
error = "You are banned",
|
||||||
|
}, Utilities.JsonSerializerOptions);
|
||||||
|
}
|
||||||
|
// Update session
|
||||||
|
await Program.Database.UpdateSessionLastUsed(session.Token, IPAddress.Parse(payload.ip));
|
||||||
return Results.Json(new JoinResponse
|
return Results.Json(new JoinResponse
|
||||||
{
|
{
|
||||||
success = true,
|
success = true,
|
||||||
clientSuccess = false,
|
clientSuccess = true,
|
||||||
error = "Invalid session",
|
username = session.Username,
|
||||||
|
rank = user.Rank
|
||||||
}, Utilities.JsonSerializerOptions);
|
}, Utilities.JsonSerializerOptions);
|
||||||
}
|
} else if (payload.sessionToken.Length == 64)
|
||||||
// Check if session is expired
|
|
||||||
if (DateTime.Now > session.LastUsed.AddDays(Program.Config.Accounts.SessionExpiryDays))
|
|
||||||
{
|
{
|
||||||
|
// Bot
|
||||||
|
var bot = await Program.Database.GetBot(token: payload.sessionToken);
|
||||||
|
if (bot == null)
|
||||||
|
{
|
||||||
|
return Results.Json(new JoinResponse
|
||||||
|
{
|
||||||
|
success = true,
|
||||||
|
clientSuccess = false,
|
||||||
|
error = "Invalid session",
|
||||||
|
}, Utilities.JsonSerializerOptions);
|
||||||
|
}
|
||||||
return Results.Json(new JoinResponse
|
return Results.Json(new JoinResponse
|
||||||
{
|
{
|
||||||
success = true,
|
success = true,
|
||||||
clientSuccess = false,
|
clientSuccess = true,
|
||||||
error = "Invalid session",
|
username = bot.Username,
|
||||||
|
rank = bot.Rank
|
||||||
}, Utilities.JsonSerializerOptions);
|
}, Utilities.JsonSerializerOptions);
|
||||||
}
|
}
|
||||||
// Check if banned
|
else
|
||||||
var user = await Program.Database.GetUser(session.Username)
|
|
||||||
?? throw new Exception("User not found in database (something is very wrong)");
|
|
||||||
if (user.Banned)
|
|
||||||
{
|
{
|
||||||
|
context.Response.StatusCode = 400;
|
||||||
return Results.Json(new JoinResponse
|
return Results.Json(new JoinResponse
|
||||||
{
|
{
|
||||||
success = true,
|
success = false,
|
||||||
clientSuccess = false,
|
error = "Invalid session"
|
||||||
error = "You are banned",
|
|
||||||
}, Utilities.JsonSerializerOptions);
|
}, Utilities.JsonSerializerOptions);
|
||||||
}
|
}
|
||||||
// Update session
|
|
||||||
await Program.Database.UpdateSessionLastUsed(session.Token, IPAddress.Parse(payload.ip));
|
|
||||||
return Results.Json(new JoinResponse
|
|
||||||
{
|
|
||||||
success = true,
|
|
||||||
clientSuccess = true,
|
|
||||||
username = session.Username,
|
|
||||||
rank = user.Rank
|
|
||||||
}, Utilities.JsonSerializerOptions);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static async Task<IResult> HandleLogin(HttpContext context)
|
private static async Task<IResult> HandleLogin(HttpContext context)
|
||||||
|
@ -689,6 +722,7 @@ public static class Routes
|
||||||
verificationRequired = true,
|
verificationRequired = true,
|
||||||
email = user.Email,
|
email = user.Email,
|
||||||
username = user.Username,
|
username = user.Username,
|
||||||
|
rank = (int)user.Rank
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// Check max sessions
|
// Check max sessions
|
||||||
|
@ -706,7 +740,8 @@ public static class Routes
|
||||||
success = true,
|
success = true,
|
||||||
token = token,
|
token = token,
|
||||||
username = user.Username,
|
username = user.Username,
|
||||||
email = user.Email
|
email = user.Email,
|
||||||
|
rank = (int)user.Rank
|
||||||
}, Utilities.JsonSerializerOptions);
|
}, Utilities.JsonSerializerOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -876,7 +911,7 @@ public static class Routes
|
||||||
}
|
}
|
||||||
// Make sure username isn't taken
|
// Make sure username isn't taken
|
||||||
var user = await Program.Database.GetUser(payload.username);
|
var user = await Program.Database.GetUser(payload.username);
|
||||||
if (user != null)
|
if (user != null || await Program.Database.GetBot(payload.username) != null)
|
||||||
{
|
{
|
||||||
context.Response.StatusCode = 400;
|
context.Response.StatusCode = 400;
|
||||||
return Results.Json(new RegisterResponse
|
return Results.Json(new RegisterResponse
|
||||||
|
|
|
@ -74,6 +74,8 @@ public class Program
|
||||||
app.Lifetime.ApplicationStarted.Register(() => Utilities.Log(LogLevel.INFO, $"Webserver listening on {Config.HTTP.Host}:{Config.HTTP.Port}"));
|
app.Lifetime.ApplicationStarted.Register(() => Utilities.Log(LogLevel.INFO, $"Webserver listening on {Config.HTTP.Host}:{Config.HTTP.Port}"));
|
||||||
// Register routes
|
// Register routes
|
||||||
Routes.RegisterRoutes(app);
|
Routes.RegisterRoutes(app);
|
||||||
|
AdminRoutes.RegisterRoutes(app);
|
||||||
|
DeveloperRoutes.RegisterRoutes(app);
|
||||||
app.Run();
|
app.Run();
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -15,6 +15,8 @@ public class User
|
||||||
public Rank Rank { get; set; }
|
public Rank Rank { get; set; }
|
||||||
public bool Banned { get; set; }
|
public bool Banned { get; set; }
|
||||||
public IPAddress RegistrationIP { get; set; }
|
public IPAddress RegistrationIP { get; set; }
|
||||||
|
public DateTime Joined { get; set; }
|
||||||
|
public bool Developer { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum Rank : uint
|
public enum Rank : uint
|
||||||
|
|
|
@ -120,4 +120,9 @@ public static class Utilities
|
||||||
}
|
}
|
||||||
else return ctx.Connection.RemoteIpAddress;
|
else return ctx.Connection.RemoteIpAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static bool IsSessionExpired(Session session)
|
||||||
|
{
|
||||||
|
return DateTime.Now > session.LastUsed.AddDays(Program.Config.Accounts.SessionExpiryDays);
|
||||||
|
}
|
||||||
}
|
}
|
Loading…
Reference in a new issue