Init
This commit is contained in:
parent
6f8679ceb1
commit
0406903aa7
15
.gitignore
vendored
15
.gitignore
vendored
@ -8,6 +8,8 @@
|
||||
.idea/**/usage.statistics.xml
|
||||
.idea/**/dictionaries
|
||||
.idea/**/shelf
|
||||
.idea/**/.idea/**
|
||||
.idea/**/.idea/
|
||||
|
||||
# AWS User-specific
|
||||
.idea/**/aws.xml
|
||||
@ -77,3 +79,16 @@ fabric.properties
|
||||
# Android studio 3.1+ serialized cache file
|
||||
.idea/caches/build_file_checksums.ser
|
||||
|
||||
# Build results
|
||||
[Dd]ebug/
|
||||
[Dd]ebugPublic/
|
||||
[Rr]elease/
|
||||
[Rr]eleases/
|
||||
x64/
|
||||
x86/
|
||||
[Aa][Rr][Mm]/
|
||||
[Aa][Rr][Mm]64/
|
||||
bld/
|
||||
[Bb]in/
|
||||
[Oo]bj/
|
||||
[Ll]og/
|
||||
|
16
LuskiServer.sln
Normal file
16
LuskiServer.sln
Normal file
@ -0,0 +1,16 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LuskiServer", "LuskiServer\LuskiServer.csproj", "{80FFB3F8-03AC-450C-AA89-0F01A681AFEC}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{80FFB3F8-03AC-450C-AA89-0F01A681AFEC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{80FFB3F8-03AC-450C-AA89-0F01A681AFEC}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{80FFB3F8-03AC-450C-AA89-0F01A681AFEC}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{80FFB3F8-03AC-450C-AA89-0F01A681AFEC}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
EndGlobal
|
29
LuskiServer/Classes/ActionFilters/TokenFilterAttribute.cs
Normal file
29
LuskiServer/Classes/ActionFilters/TokenFilterAttribute.cs
Normal file
@ -0,0 +1,29 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.DataAnnotations;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
|
||||
namespace LuskiServer.Classes.ActionFilters;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Method)]
|
||||
public class TokenFilterAttribute : Attribute, IActionFilter
|
||||
{
|
||||
public void OnActionExecuting(ActionExecutingContext context)
|
||||
{
|
||||
//token check here
|
||||
Console.WriteLine("Token Check");
|
||||
}
|
||||
|
||||
public void OnActionExecuted(ActionExecutedContext context)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
private string name;
|
||||
public double verstion;
|
||||
|
||||
public TokenFilterAttribute()
|
||||
{
|
||||
name = "token";
|
||||
verstion = 1.0;
|
||||
}
|
||||
}
|
29
LuskiServer/Classes/AppConfig.cs
Normal file
29
LuskiServer/Classes/AppConfig.cs
Normal file
@ -0,0 +1,29 @@
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace LuskiServer.Classes;
|
||||
|
||||
public class AppConfig
|
||||
{
|
||||
[JsonInclude]
|
||||
[JsonPropertyName("address")]
|
||||
public string Address { get; set; } = "127.0.0.1";
|
||||
[JsonInclude]
|
||||
[JsonPropertyName("database")]
|
||||
public string Database { get; set; } = "Some database name";
|
||||
[JsonInclude]
|
||||
[JsonPropertyName("username")]
|
||||
public string Username { get; set; } = "Some postgresql username";
|
||||
[JsonInclude]
|
||||
[JsonPropertyName("password")]
|
||||
public string Password { get; set; } = "Some postgresql password";
|
||||
[JsonInclude]
|
||||
[JsonPropertyName("connection_name")]
|
||||
public string CustomeName { get; set; } = "Luski Server";
|
||||
}
|
||||
|
||||
[JsonSerializable(typeof(AppConfig))]
|
||||
[JsonSourceGenerationOptions(WriteIndented = true, DefaultIgnoreCondition = JsonIgnoreCondition.Never, GenerationMode = JsonSourceGenerationMode.Default)]
|
||||
internal partial class AppConfigContext : JsonSerializerContext
|
||||
{
|
||||
|
||||
}
|
10
LuskiServer/Classes/Commands/Command.cs
Normal file
10
LuskiServer/Classes/Commands/Command.cs
Normal file
@ -0,0 +1,10 @@
|
||||
using LuskiServer.Enums;
|
||||
|
||||
namespace LuskiServer.Classes.Commands;
|
||||
|
||||
public class Command
|
||||
{
|
||||
public string Name { get; set; } = default!;
|
||||
public long ServiceID { get; set; } = default!;
|
||||
public CommandServiceType Type { get; set; }
|
||||
}
|
144
LuskiServer/Classes/EXT.cs
Normal file
144
LuskiServer/Classes/EXT.cs
Normal file
@ -0,0 +1,144 @@
|
||||
using System.Diagnostics;
|
||||
using System.Net.Mime;
|
||||
using System.Text;
|
||||
using LuskiServer.Classes.TableDef;
|
||||
using LuskiServer.Enums;
|
||||
using LuskiServer.Interfaces;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace LuskiServer.Classes;
|
||||
|
||||
public static class EXT
|
||||
{/*
|
||||
public static bool CanTokenRequest<TResult>(this ControllerBase Base, out long id, out TResult? result) where TResult : HTTPResponse, new()
|
||||
{
|
||||
if (Base.Request.Headers.ContainsKey("token"))
|
||||
{
|
||||
return ValidateToke(Base, out id, out result);
|
||||
}
|
||||
result = Base.ShowError<TResult>(ErrorCode.MissingToken);
|
||||
id = 0;
|
||||
return false;
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
public static string GetNumberString(this ServerPermissions enu)
|
||||
{
|
||||
return ((long)(enu)).ToString();
|
||||
}
|
||||
|
||||
public static bool CanTokenRequest(this ControllerBase Base, out long id, out IActionResult? result)
|
||||
{
|
||||
if (Base.Request.Headers.ContainsKey("token"))
|
||||
{
|
||||
return ValidateToke(Base, out id, out result);
|
||||
}
|
||||
result = Base.ShowError(ErrorCode.MissingToken, "You did not provide a token");
|
||||
id = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
public static IActionResult ShowError(this ControllerBase Base, Exception Error, bool LogInDB = true)
|
||||
{
|
||||
if (LogInDB)
|
||||
{
|
||||
Tables.Logs.Insert(
|
||||
Logs.ID.CreateParameter(Luski.Snowflake.GenerateSnowflake(WorkerId.Log).ID),
|
||||
Logs.Type.CreateParameter(LogType.Error),
|
||||
Logs.Message.CreateParameter(Error.ToString()));
|
||||
}
|
||||
|
||||
return Base.ShowError(ErrorCode.ServerError, Error.Message);
|
||||
}
|
||||
|
||||
public static IActionResult ShowError(this ControllerBase Base, ErrorCode code, string Error)
|
||||
{
|
||||
return Base.StatusCode(403, new HTTPResponse()
|
||||
{
|
||||
error = code,
|
||||
error_message = Error
|
||||
});
|
||||
}
|
||||
/*
|
||||
public static TReturn ShowError<TReturn>(this ControllerBase Base, ErrorCode code, Exception? Error, bool LogInDB = true) where TReturn : HTTPResponse, new()
|
||||
{
|
||||
if (Error is null) return new TReturn()
|
||||
{
|
||||
error = ErrorCode.ServerError,
|
||||
error_message = "Something Went wrong in the error handler."
|
||||
};
|
||||
|
||||
try
|
||||
{
|
||||
StackTrace st = new(Error, true);
|
||||
StackFrame? frame = st.GetFrame(0);
|
||||
string? controler = Base.RouteData.Values["controller"]?.ToString();
|
||||
string Line = "'Unknown'";
|
||||
if (frame is not null) Line = frame.GetFileLineNumber().ToString();
|
||||
string msg = $"In '{controler}Controller.cs' on line '{Line}' through error '{Error.Message}' and ST'{st}' and ERROR:\n'{Error}' FRAM:\n '{frame}'";
|
||||
//if (LogInDB) Log(msg, LogType.Error);
|
||||
return Base.ShowError<TReturn>(code, Error.Message);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(ex);
|
||||
return new TReturn()
|
||||
{
|
||||
error = ErrorCode.ServerError,
|
||||
error_message = "Something Went wrong in the error handler"
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public static TReturn ShowError<TReturn>(this ControllerBase Base, ErrorCode code, string? Error = null) where TReturn : HTTPResponse, new()
|
||||
{
|
||||
Base.Response.ContentType = "application/json";
|
||||
switch (code)
|
||||
{
|
||||
case ErrorCode.Forbidden or ErrorCode.InvalidHeader or ErrorCode.InvalidToken or ErrorCode.MissingToken or ErrorCode.MissingToken or ErrorCode.MissingHeader:
|
||||
Base.Response.StatusCode = StatusCodes.Status403Forbidden;
|
||||
break;
|
||||
}
|
||||
TReturn hTTPResponse = new()
|
||||
{
|
||||
error = code,
|
||||
error_message = Error
|
||||
};
|
||||
return hTTPResponse;
|
||||
}
|
||||
*/
|
||||
private static bool CheckToken(string Token, ref long ID)
|
||||
{
|
||||
try
|
||||
{
|
||||
string id = Encoding.UTF8.GetString(Convert.FromBase64String(Token.Split('.')[0]));
|
||||
string? tok = Tables.Users.Read(Users.Token, Users.ID.CreateParameter(long.Parse(id)));
|
||||
if (!string.IsNullOrWhiteSpace(tok) && tok == Token)
|
||||
{
|
||||
ID = long.Parse(id);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool ValidateToke(ControllerBase Base, out long Id, out IActionResult? result)
|
||||
{
|
||||
long id = 0;
|
||||
if (CheckToken(Base.Request.Headers["token"].First()!, ref id))
|
||||
{
|
||||
result = null;
|
||||
Id = id;
|
||||
return true;
|
||||
}
|
||||
Id = id;
|
||||
result = Base.ShowError(ErrorCode.InvalidToken, (string)null!);
|
||||
return false;
|
||||
}
|
||||
}
|
17
LuskiServer/Classes/HTTPResponse.cs
Normal file
17
LuskiServer/Classes/HTTPResponse.cs
Normal file
@ -0,0 +1,17 @@
|
||||
using System.Text.Json.Serialization;
|
||||
using LuskiServer.Enums;
|
||||
|
||||
namespace LuskiServer.Classes;
|
||||
|
||||
public class HTTPResponse
|
||||
{
|
||||
public ErrorCode? error { get; set; } = default!;
|
||||
public string? error_message { get; set; } = default!;
|
||||
}
|
||||
|
||||
|
||||
[JsonSerializable(typeof(HTTPResponse))]
|
||||
internal partial class HTTPResponseContext : JsonSerializerContext
|
||||
{
|
||||
|
||||
}
|
6
LuskiServer/Classes/Login.cs
Normal file
6
LuskiServer/Classes/Login.cs
Normal file
@ -0,0 +1,6 @@
|
||||
namespace LuskiServer.Classes;
|
||||
|
||||
public class Login
|
||||
{
|
||||
public string login_token { get; set; } = default!;
|
||||
}
|
397
LuskiServer/Classes/Luski.cs
Normal file
397
LuskiServer/Classes/Luski.cs
Normal file
@ -0,0 +1,397 @@
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization.Metadata;
|
||||
using LuskiServer.Enums;
|
||||
using ServerDatabase;
|
||||
|
||||
namespace LuskiServer.Classes;
|
||||
|
||||
public static class Luski
|
||||
{
|
||||
public static Database Database = null!;
|
||||
|
||||
public static TResult GetSettings<TResult>(string path, JsonTypeInfo<TResult> TypeInfo, bool EndOnError = false) where TResult : new()
|
||||
{
|
||||
TResult? @out;
|
||||
if (!File.Exists(path))
|
||||
{
|
||||
@out = new();
|
||||
try
|
||||
{
|
||||
File.WriteAllText(path, JsonSerializer.Serialize(@out, TypeInfo));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine("cant write file at '{0}'. make sure the premisions are set", path);
|
||||
if (EndOnError) Environment.Exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
string Fil = "";
|
||||
try
|
||||
{
|
||||
Fil = File.ReadAllText(path);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine("cant read file at '{0}'. make sure the premisions are set", path);
|
||||
if (EndOnError) Environment.Exit(0);
|
||||
}
|
||||
@out = JsonSerializer.Deserialize(Fil, TypeInfo);
|
||||
if (@out is null)
|
||||
{
|
||||
@out = new();
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
@out = new();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
File.WriteAllText(path, JsonSerializer.Serialize(@out, TypeInfo));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine("cant write file at '{0}'. make sure the premisions are set", path);
|
||||
if (EndOnError) Environment.Exit(0);
|
||||
}
|
||||
return @out;
|
||||
}
|
||||
|
||||
public static AppConfig Config = null!;
|
||||
|
||||
public static class Info
|
||||
{
|
||||
public static class Routes
|
||||
{
|
||||
public static class Default
|
||||
{
|
||||
public const string Base = "v{version:apiVersion}/[controller]";
|
||||
public const string BaseID = $"{Base}/{{id?}}";
|
||||
public const string Action = $"{Base}/{{action?}}";
|
||||
public const string ActionID = $"{Action}/{{id?}}";
|
||||
}
|
||||
}
|
||||
|
||||
public static readonly DateTime Epoch = new(2023, 1, 1, 0, 0, 0, 0);
|
||||
|
||||
public static long Timestamp
|
||||
{
|
||||
get
|
||||
{
|
||||
double ts = Math.Round(DateTime.Now.Subtract(Epoch).TotalMilliseconds, 0);
|
||||
return long.Parse(ts.ToString().Replace(".", string.Empty));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class Snowflake
|
||||
{
|
||||
public Snowflake(long ID)
|
||||
{
|
||||
this.ID = ID;
|
||||
Increment = (ushort)((ID << 52) >> 52);
|
||||
Worker_ID = (ushort)((ID << 47) >> 59);
|
||||
Server_ID = (ushort)((ID << 42) >> 59);
|
||||
Timestamp = ID >> 22;
|
||||
}
|
||||
|
||||
public static Snowflake GenerateSnowflake(WorkerId workerID)
|
||||
{
|
||||
i++;
|
||||
if (i > 4096) i = 0;
|
||||
return new Snowflake((((((Info.Timestamp << 5) | (ushort)0) << 5) | (ushort)workerID) << 12) | i);
|
||||
}
|
||||
|
||||
public long ID { get; }
|
||||
public long Timestamp { get; }
|
||||
public ushort Worker_ID { get; }
|
||||
public ushort Server_ID { get; }
|
||||
public ushort Increment { get; }
|
||||
|
||||
private static ushort i = 0;
|
||||
}
|
||||
|
||||
public static class Encryption
|
||||
{
|
||||
public class AES
|
||||
{
|
||||
public static byte[] Encrypt(byte[] data, string Password)
|
||||
{
|
||||
byte[] salt = RandomNumberGenerator.GetBytes(100);
|
||||
byte[] passwordBytes = Encoding.UTF8.GetBytes(Password);
|
||||
Rfc2898DeriveBytes key = new(passwordBytes, salt, 50000);
|
||||
byte[] encrypted;
|
||||
|
||||
using Aes aesAlg = Aes.Create();
|
||||
aesAlg.KeySize = 256;
|
||||
aesAlg.BlockSize = 128;
|
||||
aesAlg.Padding = PaddingMode.PKCS7;
|
||||
aesAlg.Key = key.GetBytes(aesAlg.KeySize / 8);
|
||||
aesAlg.IV = key.GetBytes(aesAlg.BlockSize / 8);
|
||||
|
||||
ICryptoTransform encryptor = aesAlg.CreateEncryptor(aesAlg.Key, aesAlg.IV);
|
||||
|
||||
using MemoryStream msEncrypt = new();
|
||||
msEncrypt.Write(salt, 0, salt.Length);
|
||||
using CryptoStream csEncrypt = new(msEncrypt, encryptor, CryptoStreamMode.Write);
|
||||
csEncrypt.Write(data, 0, data.Length);
|
||||
csEncrypt.Dispose();
|
||||
encrypted = msEncrypt.ToArray();
|
||||
return encrypted;
|
||||
}
|
||||
|
||||
public static byte[] Decrypt(byte[] data, string Password)
|
||||
{
|
||||
byte[] salt = new byte[100];
|
||||
using MemoryStream fsCrypt = new(data);
|
||||
fsCrypt.Read(salt, 0, salt.Length);
|
||||
byte[] passwordBytes = Encoding.UTF8.GetBytes(Password);
|
||||
Rfc2898DeriveBytes key = new(passwordBytes, salt, 50000);
|
||||
byte[] decrypted = new byte[data.Length - salt.Length];
|
||||
|
||||
using Aes aesAlg = Aes.Create();
|
||||
aesAlg.KeySize = 256;
|
||||
aesAlg.BlockSize = 128;
|
||||
aesAlg.Padding = PaddingMode.PKCS7;
|
||||
aesAlg.Key = key.GetBytes(aesAlg.KeySize / 8);
|
||||
aesAlg.IV = key.GetBytes(aesAlg.BlockSize / 8);
|
||||
|
||||
ICryptoTransform encryptor = aesAlg.CreateDecryptor(aesAlg.Key, aesAlg.IV);
|
||||
|
||||
using CryptoStream csEncrypt = new(fsCrypt, encryptor, CryptoStreamMode.Read);
|
||||
MemoryStream fsOut = new();
|
||||
int read;
|
||||
byte[] buffer = new byte[data.Length];
|
||||
while ((read = csEncrypt.Read(buffer, 0, buffer.Length)) > 0)
|
||||
{
|
||||
fsOut.Write(buffer, 0, read);
|
||||
}
|
||||
csEncrypt.Dispose();
|
||||
fsCrypt.Dispose();
|
||||
decrypted = fsOut.ToArray();
|
||||
fsOut.Dispose();
|
||||
return decrypted;
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] CalculateHash(string text, byte[] salt, int iteration)
|
||||
{
|
||||
Rfc2898DeriveBytes? pbkdf2 = new(text, salt, iteration);
|
||||
return pbkdf2.GetBytes(64);
|
||||
}
|
||||
|
||||
public static byte[] Hash(byte[] data, byte[]? salt = null)
|
||||
{
|
||||
using SHA256 sha = SHA256.Create();
|
||||
if (salt is null) return sha.ComputeHash(data);
|
||||
else return sha.ComputeHash(Combine(data, salt));
|
||||
}
|
||||
|
||||
internal const int PasswordVersion = 0;
|
||||
|
||||
internal static byte[] RemotePasswordEncrypt(string Base64Password, byte[] salt, int PasswordVersion = PasswordVersion)
|
||||
{
|
||||
return PasswordVersion switch
|
||||
{
|
||||
0 => Hash(Decrypt(Convert.FromBase64String(Base64Password), Keys.PrivateKey), salt),
|
||||
_ => throw new ArgumentException("The value provided was not accepted", nameof(PasswordVersion)),
|
||||
};
|
||||
}
|
||||
|
||||
public static class Keys
|
||||
{
|
||||
private static readonly RSACryptoServiceProvider RSA = new(4096);
|
||||
private static RSAParameters? Priv = null;
|
||||
private static string? DBPriv = null;
|
||||
private static string? Pub = null;
|
||||
internal static RSAParameters PrivateKey
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Priv == null)
|
||||
{
|
||||
Priv = RSA.ExportParameters(true);
|
||||
Pub = RSA.ToXmlString(false);
|
||||
using RSACryptoServiceProvider rsa = new(4096);
|
||||
rsa.FromXmlString(PrivateKeyString);
|
||||
DBPriv = rsa.ToXmlString(true);
|
||||
}
|
||||
return (RSAParameters)Priv;
|
||||
}
|
||||
}
|
||||
|
||||
public static string PrivateKeyString
|
||||
{
|
||||
get
|
||||
{
|
||||
return "<RSAKeyValue><Modulus>2pR655ycDWZuQiPWlcpvloFo2lmcxZJ88nlwNANOri8y+jCrqYvJLLjHW/PHBzDSx/wHEL6GYm/FIPdywgcHzi1a5tuDcdRBrKKqQU/mTW3z2d38H+BJlUCQT/Dai5SXX52p6ZSQBj2Tb4cHZSv3/6vLh1dtJdHYW+RlnadGCKoLJNBF/FQFPBgRkuP69XfAnyM8RTLzzQAcwdmw00pHOdoYV0Ue70MOF3Vv1InnxmB4uOPW6MK45NPH/1u1NkVcp3OZWRAv3xJHfS5CAwtDddnxe4a9f/grvV0lxx4ZCYRLgoe5Lv+0NVIcL+oXdJUI9geyZpfxDpdjdPRc8IA+t+0yyUHTEjyQ3focmFc8fNkmiaxji5sNvG1pWq8Q8MaqgIk1gjRSKJTnWU9N54BMB4pYgRXdVAj/jPS23tgPlcDZNMKvhzIWnzPKEA5CXICPa5DbNYfUbX4DGZlhFv6xkpMMx6jx6WPqIq+XmfaUq6NG6/E/KjbsKzTRk5C4Jfk6doiP5HSdXwP9+yXb1XRus736YoiIABuoCnj8h7OD7+ux31PcCP9PXFayMMxfxZglhw7GhT7MlNb5E9c6ELTqk7exLVxxXG8VL/RpfgyiUzliOjQIM4xyRGBCTXWbr0kRLoW0T9PzGFJzfu2smxSEdmSR9bkC7/i5T3msYa2RiSk=</Modulus><Exponent>AQAB</Exponent><P>5coLKzyBRhzAi8myKb6neZJTxQ2MgQAtXmWCT0xr1xl7wfJ1oQ2q5EQ/FKKD6ejbAyJoJ0deE9dcWUNaxvUmIa9tqHoqxpwr4dBu0IWY+9ZjbS9AmPEzJF0pDqA6NZ5ctxUZ00WzG+swepfjXOPuXqmCX1aV32zJPBN2fo2nrozOdZDqMUB3AeFu4B8zVJKYEXTzVRLnpvY7GdH2xdL/3XvHKwfnrbGv+ScHuNBP1t4oRbNTGLU6eiyK5YbpSL895HrvZYjrg9fkKQDfL0xGQLJOw/agCbwLNlSLhfBSNOfS6B4ehsMurR71Ckpg+kfzy87DssHk6oUrqFD8EcNSqw==</P><Q>84MfGPF7ivPs4hNj7CcVTh8XgOj25kb4j7X+LXzmNTnBtQ/nO9V2j0NUTg/R5gkwSFEyBIJB9lq/wsuebfS22jmxg7mwgVHFFl7JyqcRaiYY1Dl1HKTKgeCsuY7yP5WQCLfYbQ1G7tcgtcQzlPV86W2pgWsVcxcq8gqbk3k4GbPd30kPmfCPxxtxIhyoQxqPxx/FV12PeN9z+G2A70nPDwtkZ2MiiVj/RttTTPrijd5KjV6zS4jDHeYgfO3NZUviyJ8YvhCKB4ttcjM77BV8HdRgBg4XHO+NwDsMukNvAYaPDEfK42UIhne7CZJ/il8h/4OKxRzfFtAnTx/pQ8Jzew==</Q><DP>CnHzrgRzD9/QtMn3SkR7UmBfZG6oO1jptwfAM6CSqlVjNb6ysB5x7SxY/bQhcOl/wxW2TErHMPmyHfCc2Lxd/lv+DRF4jkydBge2cc4Q1Sm6nUTvl8QnAfkmG58W5kcLidrwsJTTfmpjar8qu5c0x6LG5VSHPX+xagSsdzYzMBEAdYGf05tNjY1Uv+VLLQX42ZpKUUypsQIyT4smv3lG3id5NzCFzHRuPlIS3MjDSE4S4JA1L8NVJCaQLbzDL+ZZhuA7r47YvcZ7fY2nl3vNGbXBNNEqFycwD0kqim0RH4yGHrz3wEJxBbeJhe05mUbaAyKj7KU5pZtmD6GWw6vwPw==</DP><DQ>ZUnZGYr4lGe51J+0JHptRj1wjVJZwJcstLpCq7EUIHeRtzqSODUmR5j97CpwaHrR9oKvh2iW/13n/aKsl5f+pu7wg5YtcN0OWau7y+uKNtj54uyzZeK1ySgnMFfhM8mGS9oMz++B3b7mADVIL2GdP4s5wndESMcOOfdnlwQI7cf/Ne0x7Bo/89XaTRIWezMFMxJoB1sHXoOzvVXvF5lf4yYd8VMu/mpiZJq+H3sL2W7pG7yUX4rXfgxG3zAbC1NxVXm31PcUMucv8xyUhDK7mbzI5DvgKU0LbTYiqSd7eOr7fWQvZD6WOTh8OBMTsf64KYwRoMPNl7OlZigj4udzGQ==</DQ><InverseQ>Z5mTYcXZwGh4CZLG5w80GULXzmPm8UQadZJf2PHqSDSNYFdsNGvYP/H1qtn+ZHr6SGJRF2Q0E0MqBIcAZ+iZ7IaaQ+pDszXUoqBKruuLbNk4u2ClYBWjx4ziKfPzF68utOm49EN+Zh7sTOQvUlAO7STE1iuUUGZbCvNybSoH1EP8J+snMPdlIK0M2vdE/yKI7jyqD6NGPTIJYyfnvwkRDr1cv88MgNmOEed8lnTrw69Su21WL2Eh8ePSxStTKQpVMXdefpfTx7B7TrFwDSw7P22RjHc6qFktBXlixwYjuHZBv2OwBA6ii1jQzZjT/IqJFT99pmOmg2BchQwOfD5WCQ==</InverseQ><D>DG0sOZZT4YfPv+SFXWRcs1vtvUV381wwd0vZHv6LJBLx3SW+30zfpd0Rab8HsyHJnJii7DGbYTrvzup9HzPCyVbBwrslvhHDnrrBXs+EjSVcRigSc9t5Q3TKtN4WsUsZef70JcqOOj9DmQUTsg2YXp7X23y9wKHX9jgbYNBIMSjoU5z1mvKfOOXjZeGefFhlCTG5ykirHJ342eUe4jw9pWJ/8heEuonU4heJQTY1M417f4NlzeYiwyZDygGJi/7QgC1DFrxOrxS9H2GYW3pHSQrCvvOPkBgTSy42/hrOG0DmrBKRlW7wbKbw4aEiLpIPKDGS07X9Udzgc3SLA28/DB9AdX6qZSKEmf33LwK47wLEZkiLbU/IO/kb0MpSpl77TrMDrl1J+3Kgcmz7q9eF83W9519/uZgZpOSO/a/97oMKZSfLeyASVP7K1EcqK23K/wjvaP3nvSMGsiQmqCn4jItgfH5N/tcnkNz2DKz3BPcfgAClVH1MFsjDlBqYSHXIqwAFQ8SDBixrPoLoJDkygFoNIrrXqbArVYqXOAgODo2t0foENa/msPJyZ6m6UPi3bMY3gji5qjaSEcvbBmhlSVyHmumgOJ5Rl+L5dS5jAyPgybcVYKab71XQf5sm1UhA8iFWqYgyglmR/b6F9UTFi1QwZA6rgvELFv7DvqjLbmM=</D></RSAKeyValue>";
|
||||
}
|
||||
}
|
||||
|
||||
public static string PublicKey
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Pub == null)
|
||||
{
|
||||
Priv = RSA.ExportParameters(true);
|
||||
Pub = RSA.ToXmlString(false);
|
||||
using RSACryptoServiceProvider rsa = new(4096);
|
||||
rsa.FromXmlString(PrivateKeyString);
|
||||
DBPriv = rsa.ToXmlString(true);
|
||||
}
|
||||
return Pub;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static readonly UnicodeEncoding _encoder = new();
|
||||
|
||||
public static byte[] Decrypt(byte[] data, bool multithread = false)
|
||||
{
|
||||
return Decrypt(data, Keys.PrivateKey, multithread);
|
||||
}
|
||||
|
||||
public static byte[] Decrypt(byte[] data, string Key, bool multithread = false)
|
||||
{
|
||||
using RSACryptoServiceProvider rsa = new();
|
||||
rsa.FromXmlString(Key);
|
||||
return Decrypt(data, rsa.ExportParameters(true), multithread);
|
||||
}
|
||||
|
||||
public static byte[] Decrypt(byte[] EncryptedText, RSAParameters Key, bool multithread = false)
|
||||
{
|
||||
if (EncryptedText is null) throw new ArgumentNullException(nameof(EncryptedText));
|
||||
using RSACryptoServiceProvider rsa = new();
|
||||
rsa.ImportParameters(Key);
|
||||
int size = rsa.KeySize / 8;
|
||||
double x = EncryptedText.Length / (double)size;
|
||||
int bbb = int.Parse(x.ToString().Split('.')[0]);
|
||||
if (x.ToString().Contains('.')) bbb++;
|
||||
byte[]? datasplitout = Array.Empty<byte>();
|
||||
if (multithread)
|
||||
{
|
||||
byte[][]? decccc = Array.Empty<byte[]>();
|
||||
Array.Resize(ref decccc, bbb);
|
||||
int num = Convert.ToInt32(Math.Ceiling((Environment.ProcessorCount * 25) * 2.0));
|
||||
if (num == 0) num = 1;
|
||||
Parallel.For(0, bbb, new ParallelOptions()
|
||||
{
|
||||
MaxDegreeOfParallelism = num
|
||||
}, i =>
|
||||
{
|
||||
decccc[i] = rsa.Decrypt(EncryptedText.Skip(i * size).Take(size).ToArray(), false);
|
||||
});
|
||||
foreach (byte[] data in decccc)
|
||||
{
|
||||
datasplitout = Combine(datasplitout, data);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < bbb; i++)
|
||||
{
|
||||
datasplitout = Combine(datasplitout, rsa.Decrypt(EncryptedText.Skip(i * size).Take(size).ToArray(), false));
|
||||
}
|
||||
}
|
||||
return datasplitout;
|
||||
|
||||
/*
|
||||
|
||||
using (RSACryptoServiceProvider? rsa = new())
|
||||
{
|
||||
rsa.ImportParameters(Key);
|
||||
double x = ((double)EncryptedText.Length / (double)512);
|
||||
int bbb = int.Parse(x.ToString().Split('.')[0]);
|
||||
if (x.ToString().Contains('.')) bbb++;
|
||||
byte[][] datasplit = Array.Empty<byte[]>();
|
||||
byte[] datasplitout = Array.Empty<byte>();
|
||||
Array.Resize(ref datasplit, bbb);
|
||||
for (int i = 0; i < bbb; i++)
|
||||
{
|
||||
byte[] fff = EncryptedText.Skip(i * 512).Take(512).ToArray();
|
||||
datasplit[i] = fff;
|
||||
datasplitout = Combine(datasplitout, rsa.Decrypt(datasplit[i], false));
|
||||
}
|
||||
return datasplitout;
|
||||
}*/
|
||||
}
|
||||
|
||||
public static byte[] Encrypt(string Text, bool multithread = false)
|
||||
{
|
||||
using RSACryptoServiceProvider rsa = new();
|
||||
rsa.FromXmlString(Keys.PublicKey);
|
||||
return Encrypt(_encoder.GetBytes(Text), rsa.ExportParameters(false), multithread);
|
||||
}
|
||||
|
||||
public static byte[] Encrypt(string Text, string key, bool multithread = false)
|
||||
{
|
||||
using RSACryptoServiceProvider rsa = new();
|
||||
rsa.FromXmlString(key);
|
||||
return Encrypt(_encoder.GetBytes(Text), rsa.ExportParameters(false), multithread);
|
||||
}
|
||||
|
||||
public static byte[] Encrypt(byte[] data, string key, bool multithread = false)
|
||||
{
|
||||
using RSACryptoServiceProvider rsa = new();
|
||||
rsa.FromXmlString(key);
|
||||
return Encrypt(data, rsa.ExportParameters(false), multithread);
|
||||
}
|
||||
|
||||
private static byte[] Combine(byte[] first, byte[] second)
|
||||
{
|
||||
byte[] bytes = new byte[first.Length + second.Length];
|
||||
Buffer.BlockCopy(first, 0, bytes, 0, first.Length);
|
||||
Buffer.BlockCopy(second, 0, bytes, first.Length, second.Length);
|
||||
return bytes;
|
||||
}
|
||||
|
||||
public static byte[] Encrypt(string Text, RSAParameters Key, bool multithread = false)
|
||||
{
|
||||
return Encrypt(_encoder.GetBytes(Text), Key, multithread);
|
||||
}
|
||||
|
||||
public static byte[] Encrypt(byte[] data, RSAParameters Key, bool multithread = false)
|
||||
{
|
||||
using RSACryptoServiceProvider rsa = new();
|
||||
rsa.ImportParameters(Key);
|
||||
int size = rsa.KeySize / 8;
|
||||
double x = data.Length / (double)size;
|
||||
int bbb = int.Parse(x.ToString().Split('.')[0]);
|
||||
if (x.ToString().Contains('.')) bbb++;
|
||||
byte[]? datasplitout = Array.Empty<byte>();
|
||||
if (multithread)
|
||||
{
|
||||
byte[][]? decccc = Array.Empty<byte[]>();
|
||||
Array.Resize(ref decccc, bbb);
|
||||
int num = Convert.ToInt32(Math.Ceiling((Environment.ProcessorCount * 25) * 2.0));
|
||||
if (num == 0) num = 1;
|
||||
Parallel.For(0, bbb, new ParallelOptions()
|
||||
{
|
||||
MaxDegreeOfParallelism = num
|
||||
}, i =>
|
||||
{
|
||||
decccc[i] = rsa.Encrypt(data.Skip(i * size).Take(size).ToArray(), false);
|
||||
});
|
||||
foreach (byte[] dataa in decccc)
|
||||
{
|
||||
datasplitout = Combine(datasplitout, dataa);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (int i = 0; i < bbb; i++)
|
||||
{
|
||||
datasplitout = Combine(datasplitout, rsa.Encrypt(data.Skip(i * size).Take(size).ToArray(), false));
|
||||
}
|
||||
}
|
||||
return datasplitout;
|
||||
}
|
||||
}
|
||||
}
|
26
LuskiServer/Classes/PluginWarningAttribute.cs
Normal file
26
LuskiServer/Classes/PluginWarningAttribute.cs
Normal file
@ -0,0 +1,26 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace LuskiServer.Classes;
|
||||
|
||||
[AttributeUsage(AttributeTargets.Field)]
|
||||
public class PluginWarningAttribute : Attribute
|
||||
{
|
||||
public PluginWarningAttribute([DisallowNull]string Message)
|
||||
{
|
||||
_mess = Message;
|
||||
}
|
||||
|
||||
private string _mess;
|
||||
|
||||
public string Message
|
||||
{
|
||||
get
|
||||
{
|
||||
return _mess;
|
||||
}
|
||||
set
|
||||
{
|
||||
_mess = value;
|
||||
}
|
||||
}
|
||||
}
|
14
LuskiServer/Classes/TableDef/Categories.cs
Normal file
14
LuskiServer/Classes/TableDef/Categories.cs
Normal file
@ -0,0 +1,14 @@
|
||||
using ServerDatabase;
|
||||
|
||||
namespace LuskiServer.Classes.TableDef;
|
||||
|
||||
public static class Categories
|
||||
{
|
||||
public static TableColumn<long> ID { get; } = new("id", true);
|
||||
public static TableColumn<string> Name { get; } = new("name");
|
||||
public static TableColumn<string> Description { get; } = new("description");
|
||||
public static TableColumn<long[]> InnerCategories { get; } = new("inner_categories") { DefaultValue = Array.Empty<long>() };
|
||||
public static TableColumn<long[]> Channels { get; } = new("channels") { DefaultValue = Array.Empty<long>() };
|
||||
public static TableColumn<long[]> RoleOverides { get; } = new("role_overides") { DefaultValue = Array.Empty<long>() };
|
||||
public static TableColumn<long[]> UserOverides { get; } = new("member_overides") { DefaultValue = Array.Empty<long>() };
|
||||
}
|
15
LuskiServer/Classes/TableDef/Channels.cs
Normal file
15
LuskiServer/Classes/TableDef/Channels.cs
Normal file
@ -0,0 +1,15 @@
|
||||
using LuskiServer.Enums;
|
||||
using ServerDatabase;
|
||||
|
||||
namespace LuskiServer.Classes.TableDef;
|
||||
|
||||
public static class Channels
|
||||
{
|
||||
public static TableColumn<long> ID { get; } = new("id", true);
|
||||
public static TableColumn<ChannelType> Type { get; } = new("type");
|
||||
public static TableColumn<byte[]> Name { get; } = new("name");
|
||||
public static TableColumn<byte[]> Description { get; } = new("description");
|
||||
public static TableColumn<string> Key { get; } = new("key");
|
||||
public static TableColumn<long[]> RoleOverides { get; } = new("role_overides");
|
||||
public static TableColumn<long[]> UserOverides { get; } = new("member_overides");
|
||||
}
|
12
LuskiServer/Classes/TableDef/Files.cs
Normal file
12
LuskiServer/Classes/TableDef/Files.cs
Normal file
@ -0,0 +1,12 @@
|
||||
using ServerDatabase;
|
||||
|
||||
namespace LuskiServer.Classes.TableDef;
|
||||
|
||||
public static class Files
|
||||
{
|
||||
public static TableColumn<long> ID { get; } = new("id", true);
|
||||
public static TableColumn<long> Size { get; } = new("size");
|
||||
public static TableColumn<byte[]> Name { get; } = new("name");
|
||||
public static TableColumn<byte[]> Hash { get; } = new("hash");
|
||||
public static TableColumn<byte[]> Data { get; } = new("data");
|
||||
}
|
11
LuskiServer/Classes/TableDef/Logs.cs
Normal file
11
LuskiServer/Classes/TableDef/Logs.cs
Normal file
@ -0,0 +1,11 @@
|
||||
using LuskiServer.Enums;
|
||||
using ServerDatabase;
|
||||
|
||||
namespace LuskiServer.Classes.TableDef;
|
||||
|
||||
public static class Logs
|
||||
{
|
||||
public static TableColumn<long> ID { get; } = new("id", true);
|
||||
public static TableColumn<LogType> Type { get; } = new("type");
|
||||
public static TableColumn<string> Message { get; } = new("message");
|
||||
}
|
13
LuskiServer/Classes/TableDef/Messages.cs
Normal file
13
LuskiServer/Classes/TableDef/Messages.cs
Normal file
@ -0,0 +1,13 @@
|
||||
using ServerDatabase;
|
||||
|
||||
namespace LuskiServer.Classes.TableDef;
|
||||
|
||||
public static class Messages
|
||||
{
|
||||
public static TableColumn<long> ID { get; } = new("id", true);
|
||||
public static TableColumn<long> ChannelID { get; } = new("channel_id");
|
||||
public static TableColumn<long> AuthorID { get; } = new("author_id");
|
||||
public static TableColumn<long> TimeStamp { get; } = new("ts");
|
||||
public static TableColumn<byte[]> Context { get; } = new("context");
|
||||
public static TableColumn<long[]> Files { get; } = new("files");
|
||||
}
|
13
LuskiServer/Classes/TableDef/Roles.cs
Normal file
13
LuskiServer/Classes/TableDef/Roles.cs
Normal file
@ -0,0 +1,13 @@
|
||||
using LuskiServer.Enums;
|
||||
using ServerDatabase;
|
||||
|
||||
namespace LuskiServer.Classes.TableDef;
|
||||
|
||||
public static class Roles
|
||||
{
|
||||
public static TableColumn<long> ID { get; } = new("id", true);
|
||||
public static TableColumn<string> Name { get; } = new("name");
|
||||
public static TableColumn<string> Color { get; } = new("color");
|
||||
public static TableColumn<string> Description { get; } = new("description");
|
||||
public static TableColumn<ServerPermissions[]> ServerPermissions { get; } = new("server_perms");
|
||||
}
|
11
LuskiServer/Classes/TableDef/ServerRoleOverides.cs
Normal file
11
LuskiServer/Classes/TableDef/ServerRoleOverides.cs
Normal file
@ -0,0 +1,11 @@
|
||||
using LuskiServer.Enums;
|
||||
using ServerDatabase;
|
||||
|
||||
namespace LuskiServer.Classes.TableDef;
|
||||
|
||||
public static class ServerRoleOverides
|
||||
{
|
||||
public static TableColumn<long> ID { get; } = new("id", true);
|
||||
public static TableColumn<long> RoleID { get; } = new("role_id");
|
||||
public static TableColumn<string[]> Overides { get; } = new("overides");
|
||||
}
|
12
LuskiServer/Classes/TableDef/SessionTokens.cs
Normal file
12
LuskiServer/Classes/TableDef/SessionTokens.cs
Normal file
@ -0,0 +1,12 @@
|
||||
using ServerDatabase;
|
||||
|
||||
namespace LuskiServer.Classes.TableDef;
|
||||
|
||||
public static class SessionTokens
|
||||
{
|
||||
public static TableColumn<long> ID { get; } = new("id", true);
|
||||
public static TableColumn<long> AccountID { get; } = new("account_id");
|
||||
public static TableColumn<byte[]> Token { get; } = new("token");
|
||||
public static TableColumn<byte[]> AddressFilter { get; } = new("address_filter");
|
||||
public static TableColumn<DateTime> TimeFilter { get; } = new("date_filter");
|
||||
}
|
12
LuskiServer/Classes/TableDef/UserRoleOverides.cs
Normal file
12
LuskiServer/Classes/TableDef/UserRoleOverides.cs
Normal file
@ -0,0 +1,12 @@
|
||||
using LuskiServer.Enums;
|
||||
using ServerDatabase;
|
||||
|
||||
namespace LuskiServer.Classes.TableDef;
|
||||
|
||||
public static class UserRoleOverides
|
||||
{
|
||||
public static TableColumn<long> ID { get; } = new("id", true);
|
||||
public static TableColumn<long> UserID { get; } = new("user_id");
|
||||
public static TableColumn<long> ParentOverideID { get; } = new("parent_overide_id");
|
||||
public static TableColumn<string[]> Overides { get; } = new("overides");
|
||||
}
|
24
LuskiServer/Classes/TableDef/Users.cs
Normal file
24
LuskiServer/Classes/TableDef/Users.cs
Normal file
@ -0,0 +1,24 @@
|
||||
using LuskiServer.Enums;
|
||||
using ServerDatabase;
|
||||
|
||||
namespace LuskiServer.Classes.TableDef;
|
||||
|
||||
public class Users
|
||||
{
|
||||
public static TableColumn<long> ID { get; } = new("id", true);
|
||||
public static TableColumn<string> DisplayName { get; } = new("displayname");
|
||||
public static TableColumn<long> SelectedChannel { get; } = new("selected_channel");
|
||||
public static TableColumn<Status> Status { get; } = new("status");
|
||||
public static TableColumn<PictureType> PictureType { get; } = new("picture_type");
|
||||
public static TableColumn<byte[]> Picture { get; } = new("picture");
|
||||
public static TableColumn<long[]> Roles { get; } = new("roles");
|
||||
public static TableColumn<byte[]> Username { get; } = new("username");
|
||||
public static TableColumn<byte[]> Password { get; } = new("password");
|
||||
public static TableColumn<byte[]> Salt { get; } = new("salt");
|
||||
public static TableColumn<string> LoginToken { get; } = new("login_token");
|
||||
public static TableColumn<string> SessionKey { get; } = new("session_key");
|
||||
public static TableColumn<string> WSSTCP { get; } = new("wsstcp");
|
||||
public static TableColumn<string> Token { get; } = new("token");
|
||||
public static TableColumn<string[]> OfflineData { get; } = new("offline_data");
|
||||
public static TableColumn<string> OffileKey { get; } = new("offline_key");
|
||||
}
|
17
LuskiServer/Classes/Tables.cs
Normal file
17
LuskiServer/Classes/Tables.cs
Normal file
@ -0,0 +1,17 @@
|
||||
using ServerDatabase;
|
||||
|
||||
namespace LuskiServer.Classes;
|
||||
|
||||
public static class Tables
|
||||
{
|
||||
public static Table Users { get; } = new Table("users", null!);
|
||||
public static Table Roles { get; } = new Table("roles", null!);
|
||||
public static Table Logs { get; } = new Table("logs", null!);
|
||||
public static Table Files { get; } = new Table("files", null!);
|
||||
public static Table Categories { get; } = new Table("categories", null!);
|
||||
public static Table Channels { get; } = new Table("channels", null!);
|
||||
public static Table Messages { get; } = new Table("messages", null!);
|
||||
public static Table ServerRoleOverides { get; } = new Table("role_overides", null!);
|
||||
public static Table UserRoleOverides { get; } = new Table("user_overides", null!);
|
||||
public static Table SessionTokens { get; } = new Table("session_tokens", null!);
|
||||
}
|
90
LuskiServer/ConfigureSwaggerOptions.cs
Normal file
90
LuskiServer/ConfigureSwaggerOptions.cs
Normal file
@ -0,0 +1,90 @@
|
||||
using Asp.Versioning;
|
||||
|
||||
namespace LuskiServer;
|
||||
|
||||
using Asp.Versioning.ApiExplorer;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Microsoft.OpenApi.Models;
|
||||
using Swashbuckle.AspNetCore.SwaggerGen;
|
||||
using System.Text;
|
||||
|
||||
/// <summary>
|
||||
/// Configures the Swagger generation options.
|
||||
/// </summary>
|
||||
/// <remarks>This allows API versioning to define a Swagger document per API version after the
|
||||
/// <see cref="IApiVersionDescriptionProvider"/> service has been resolved from the service container.</remarks>
|
||||
public class ConfigureSwaggerOptions : IConfigureOptions<SwaggerGenOptions>
|
||||
{
|
||||
private readonly IApiVersionDescriptionProvider provider;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="ConfigureSwaggerOptions"/> class.
|
||||
/// </summary>
|
||||
/// <param name="provider">The <see cref="IApiVersionDescriptionProvider">provider</see> used to generate Swagger documents.</param>
|
||||
public ConfigureSwaggerOptions(IApiVersionDescriptionProvider provider) => this.provider = provider;
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Configure(SwaggerGenOptions options)
|
||||
{
|
||||
// add a swagger document for each discovered API version
|
||||
// note: you might choose to skip or document deprecated API versions differently
|
||||
foreach (ApiVersionDescription description in provider.ApiVersionDescriptions)
|
||||
{
|
||||
options.SwaggerDoc(description.GroupName, CreateInfoForApiVersion(description));
|
||||
}
|
||||
}
|
||||
|
||||
private static OpenApiInfo CreateInfoForApiVersion(ApiVersionDescription description)
|
||||
{
|
||||
StringBuilder text = new StringBuilder("A simple way to view nice information to understand API v" + description.ApiVersion + ".");
|
||||
OpenApiInfo info = new OpenApiInfo()
|
||||
{
|
||||
Title = "Luski Server API",
|
||||
Version = description.ApiVersion.ToString(),
|
||||
//Contact = new OpenApiContact() { Name = "Bill Mei", Email = "bill.mei@somewhere.com" },
|
||||
//License = new OpenApiLicense() { Name = "MIT", Url = new Uri( "https://opensource.org/licenses/MIT" ) }
|
||||
};
|
||||
|
||||
if (description.IsDeprecated)
|
||||
{
|
||||
text.Append(" This API version has been deprecated.");
|
||||
}
|
||||
|
||||
if (description.SunsetPolicy is { } policy)
|
||||
{
|
||||
if (policy.Date.HasValue)
|
||||
{
|
||||
text.Append(" The API will be sunset on ")
|
||||
.Append(policy.Date.Value.Date.ToShortDateString())
|
||||
.Append('.');
|
||||
}
|
||||
|
||||
if (policy.HasLinks)
|
||||
{
|
||||
text.AppendLine();
|
||||
|
||||
for (int i = 0; i < policy.Links.Count; i++)
|
||||
{
|
||||
LinkHeaderValue link = policy.Links[i];
|
||||
|
||||
if (link.Type == "text/html")
|
||||
{
|
||||
text.AppendLine();
|
||||
|
||||
if (link.Title.HasValue)
|
||||
{
|
||||
text.Append(link.Title.Value).Append(": ");
|
||||
}
|
||||
|
||||
text.Append(link.LinkTarget.OriginalString);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
info.Description = text.ToString();
|
||||
|
||||
return info;
|
||||
}
|
||||
}
|
172
LuskiServer/Controllers/v1/CreateAccountController.cs
Normal file
172
LuskiServer/Controllers/v1/CreateAccountController.cs
Normal file
@ -0,0 +1,172 @@
|
||||
using System.Net.Mime;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using Asp.Versioning;
|
||||
using LuskiServer.Classes;
|
||||
using LuskiServer.Classes.TableDef;
|
||||
using LuskiServer.Enums;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace LuskiServer.Controllers.v1;
|
||||
|
||||
[ApiVersion(1)]
|
||||
[ApiController]
|
||||
public class CreateAccountController : ControllerBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Make a post request to this endpoint for registering an account on the server.
|
||||
/// </summary>
|
||||
/// <param name="UsernameRaw">Encrypted email for account</param>
|
||||
/// <param name="PasRaw">Hashed Password for account</param>
|
||||
/// <param name="DisplayName">Plain text Username</param>
|
||||
/// <param name="KeyRaw">Plain text RSA key for server to respond with</param>
|
||||
/// <param name="Body">Raw Picture data for profile</param>
|
||||
/// <returns></returns>
|
||||
[HttpPost]
|
||||
[DisableRequestSizeLimit]
|
||||
[Consumes(MediaTypeNames.Image.Jpeg, MediaTypeNames.Image.Gif, MediaTypeNames.Image.Tiff, "image/png")]
|
||||
[Produces(MediaTypeNames.Application.Json)]
|
||||
[ProducesResponseType(typeof(Login), StatusCodes.Status201Created)]
|
||||
[ProducesResponseType(typeof(Login), StatusCodes.Status403Forbidden)]
|
||||
[Route(Luski.Info.Routes.Default.Base)]
|
||||
public IActionResult Post([FromBody]byte[] Body, [FromHeader(Name = "username")]string? UsernameRaw, [FromHeader(Name = "password")]string? PasRaw, [FromHeader(Name = "displayname")]string? DisplayName, [FromHeader(Name = "key")]string? KeyRaw)
|
||||
{
|
||||
try
|
||||
{
|
||||
PictureType pfp;
|
||||
byte[] PasBytes, Username;
|
||||
byte[] salt = new byte[100];
|
||||
try
|
||||
{
|
||||
using (RandomNumberGenerator provider = RandomNumberGenerator.Create())
|
||||
{
|
||||
provider.GetBytes(salt);
|
||||
}
|
||||
try
|
||||
{
|
||||
PasBytes = Luski.Encryption.RemotePasswordEncrypt(Luski.Encryption._encoder.GetString(Luski.Encryption.Decrypt(Convert.FromBase64String(PasRaw))), salt);
|
||||
Username = Luski.Encryption.Decrypt(Convert.FromBase64String(UsernameRaw));
|
||||
|
||||
try
|
||||
{
|
||||
byte[] g = Luski.Encryption.Encrypt("Test data to send to client", KeyRaw);
|
||||
}
|
||||
catch
|
||||
{
|
||||
return this.ShowError(ErrorCode.InvalidHeader, "The public keys you gave cant be used to encrypt data");
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
return this.ShowError(ErrorCode.InvalidHeader, "Make sure your login is encrypted with the server provided public key");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (ex.Message.Contains("not found")) return this.ShowError(ErrorCode.MissingHeader, "Missing login infermation");
|
||||
return this.ShowError(ex);
|
||||
}
|
||||
|
||||
if (CheckUsername(Username))
|
||||
{
|
||||
int num = new Random().Next(1000, 1000000000);
|
||||
int num2 = new Random().Next(1000, 1000000000);
|
||||
Luski.Snowflake id = Luski.Snowflake.GenerateSnowflake(WorkerId.CreateAccount);
|
||||
byte[] ID = Encoding.UTF8.GetBytes(id.ID.ToString());
|
||||
byte[] Timestamp = Encoding.UTF8.GetBytes(DateTime.UtcNow.ToString());
|
||||
byte[] Number = Encoding.UTF8.GetBytes(num.ToString());
|
||||
byte[] Number2 = Encoding.UTF8.GetBytes(num2.ToString());
|
||||
string Token = $"{Convert.ToBase64String(ID)}.{Convert.ToBase64String(Timestamp)}.{Convert.ToBase64String(Number)}.{Convert.ToBase64String(Number2)}";
|
||||
|
||||
pfp = GetProfilePictureType(Encoding.UTF8.GetString(Body).ToUpper());
|
||||
|
||||
Tables.Users.Insert(
|
||||
Users.ID.CreateParameter(id.ID),
|
||||
Users.DisplayName.CreateParameter(DisplayName!.Replace("'", "\'")),
|
||||
Users.SelectedChannel.CreateParameter(0), //TODO set to default
|
||||
Users.Status.CreateParameter(Status.Offline),
|
||||
Users.PictureType.CreateParameter(pfp),
|
||||
Users.Picture.CreateParameter(Body),
|
||||
Users.Roles.CreateParameter(Array.Empty<long>()),
|
||||
Users.Username.CreateParameter(Username),
|
||||
Users.Password.CreateParameter(PasBytes),
|
||||
Users.Salt.CreateParameter(salt),
|
||||
Users.LoginToken.CreateParameter(Token),
|
||||
Users.SessionKey.CreateParameter(KeyRaw),
|
||||
Users.WSSTCP.CreateParameter(string.Empty),
|
||||
Users.Token.CreateParameter(string.Empty),
|
||||
Users.OfflineData.CreateParameter(Array.Empty<string>()),
|
||||
Users.OffileKey.CreateParameter(string.Empty));
|
||||
Thread t = new(o => RegToken((string?)o));
|
||||
t.Start(Token);
|
||||
return StatusCode(201, new Login()
|
||||
{
|
||||
login_token = Token
|
||||
});
|
||||
}
|
||||
return this.ShowError(ErrorCode.Forbidden, "That email is already being used");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return this.ShowError(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static PictureType GetProfilePictureType(string headerCode)
|
||||
{
|
||||
if (headerCode.StartsWith("FFD8FFE0"))
|
||||
{
|
||||
return PictureType.jpeg;
|
||||
}
|
||||
else if (headerCode.StartsWith("49492A"))
|
||||
{
|
||||
return PictureType.tif;
|
||||
}
|
||||
else if (headerCode.StartsWith("424D"))
|
||||
{
|
||||
return PictureType.bmp;
|
||||
}
|
||||
else if (headerCode.StartsWith("GIF"))
|
||||
{
|
||||
return PictureType.gif;
|
||||
}
|
||||
else if (headerCode.Remove(0, 1).StartsWith("PNG"))
|
||||
{
|
||||
return PictureType.png;
|
||||
}
|
||||
else
|
||||
{
|
||||
return PictureType.png;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Cheaks to see Username is free
|
||||
/// </summary>
|
||||
/// <param name="Username"></param>
|
||||
/// <returns>
|
||||
/// <para>true = Good</para>
|
||||
/// <para>false = Username Taken</para>
|
||||
/// </returns>
|
||||
private static bool CheckUsername(byte[] Username)
|
||||
{
|
||||
if (Tables.Users.TryRead(Users.Username, out _, Users.Username.CreateParameter(Username))) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
private static void RegToken(string? token)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (token == null) return;
|
||||
long id = long.Parse(Encoding.UTF8.GetString(Convert.FromBase64String(token.Split('.')[0])));
|
||||
Tables.Users.Update(Users.ID, id, Users.LoginToken.CreateParameter(token));
|
||||
Thread.Sleep(30000);
|
||||
Tables.Users.Update(Users.ID, id, Users.LoginToken.CreateParameter(string.Empty));
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
67
LuskiServer/Controllers/v1/KeysController.cs
Normal file
67
LuskiServer/Controllers/v1/KeysController.cs
Normal file
@ -0,0 +1,67 @@
|
||||
using System.Net.Mime;
|
||||
using System.Text;
|
||||
using Asp.Versioning;
|
||||
using LuskiServer.Classes;
|
||||
using LuskiServer.Classes.TableDef;
|
||||
using LuskiServer.Enums;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace LuskiServer.Controllers.v1;
|
||||
|
||||
[ApiVersion(1)]
|
||||
[ApiController]
|
||||
public class KeysController : ControllerBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Return a public RSA key to encrypt information directed for the server.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
[HttpGet]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[Produces(MediaTypeNames.Application.Xml)]
|
||||
[Route(Luski.Info.Routes.Default.Base + "/PublicKey")]
|
||||
public IActionResult PublicKey()
|
||||
{
|
||||
return File(Encoding.UTF8.GetBytes(Luski.Encryption.Keys.PublicKey), "application/xml");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Make a post request to this URL to set you offline data key.
|
||||
/// </summary>
|
||||
/// <param name="token">Your Luski token for the server</param>
|
||||
/// <param name="keyy">The key you want to set for when you go offline</param>
|
||||
/// <returns></returns>
|
||||
[HttpPost]
|
||||
[Consumes(MediaTypeNames.Application.Xml)]
|
||||
[Produces(MediaTypeNames.Application.Json)]
|
||||
[ProducesResponseType(StatusCodes.Status202Accepted)]
|
||||
[ProducesResponseType(typeof(HTTPResponse), StatusCodes.Status403Forbidden)]
|
||||
[Route(Luski.Info.Routes.Default.Base + "/SetOfflineKey")]
|
||||
public IActionResult SetOfflineKey([FromBody]byte[]? keyy, [FromHeader(Name = "token")]string? token)
|
||||
{
|
||||
try
|
||||
{
|
||||
string key = "";
|
||||
if (!this.CanTokenRequest(out long ID, out IActionResult? toc) && toc != null) return toc;
|
||||
string[]? data = Tables.Users.Read(Users.OfflineData, Users.ID.CreateParameter(ID));
|
||||
if (data is not null) return this.ShowError(ErrorCode.Forbidden, "you cant change your key untill you download your data");
|
||||
|
||||
try
|
||||
{
|
||||
byte[] g = Luski.Encryption.Encrypt("Test data to send to client", key);
|
||||
}
|
||||
catch
|
||||
{
|
||||
|
||||
return this.ShowError(ErrorCode.InvalidPostData, "The key you sent the server appears to be incorect");
|
||||
}
|
||||
Tables.Users.Update(Users.ID, ID,
|
||||
Users.OffileKey.CreateParameter(key));
|
||||
return StatusCode(202);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return this.ShowError(ex);
|
||||
}
|
||||
}
|
||||
}
|
163
LuskiServer/Controllers/v1/SocketUserProfileController.cs
Normal file
163
LuskiServer/Controllers/v1/SocketUserProfileController.cs
Normal file
@ -0,0 +1,163 @@
|
||||
using System.Net.Mime;
|
||||
using System.Text;
|
||||
using Asp.Versioning;
|
||||
using LuskiServer.Classes;
|
||||
using LuskiServer.Classes.ActionFilters;
|
||||
using LuskiServer.Classes.TableDef;
|
||||
using LuskiServer.Enums;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.ModelBinding;
|
||||
|
||||
namespace LuskiServer.Controllers.v1;
|
||||
|
||||
[ApiVersion(1)]
|
||||
[ApiController]
|
||||
public class SocketUserProfileController : ControllerBase
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns the Status of a user.
|
||||
/// </summary>
|
||||
/// <param name="id">The Id of the user</param>
|
||||
/// <returns></returns>
|
||||
[HttpGet]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(HTTPResponse), StatusCodes.Status403Forbidden, MediaTypeNames.Application.Json)]
|
||||
[Produces(MediaTypeNames.Text.Plain, MediaTypeNames.Application.Json)]
|
||||
[Route(Luski.Info.Routes.Default.Base + "/Status/{id:long}")]
|
||||
public Status Status(long id)
|
||||
{
|
||||
Status status = Tables.Users.Read(Users.Status, Users.ID.CreateParameter(id));
|
||||
return status;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the Avatar for the user.
|
||||
/// </summary>
|
||||
/// <param name="id">The ID of the requested user</param>
|
||||
/// <returns></returns>
|
||||
[HttpGet]
|
||||
[ProducesResponseType(StatusCodes.Status200OK)]
|
||||
[ProducesResponseType(typeof(HTTPResponse), StatusCodes.Status403Forbidden)]
|
||||
[Produces(MediaTypeNames.Image.Jpeg, MediaTypeNames.Image.Gif, MediaTypeNames.Image.Tiff, "image/png", MediaTypeNames.Application.Json)]
|
||||
[Route(Luski.Info.Routes.Default.Base + "/Avatar/{id:long}")]
|
||||
public IActionResult? Avatar(long id)
|
||||
{
|
||||
if (Tables.Users.TryRead(Users.Picture, out byte[]? image, Users.ID.CreateParameter(id)))
|
||||
{
|
||||
return Tables.Users.Read(Users.PictureType, Users.ID.CreateParameter(id)) switch
|
||||
{
|
||||
PictureType.png => File(image, "image/png"),
|
||||
PictureType.jpeg => File(image, "image/jpeg"),
|
||||
PictureType.bmp => File(image, "image/bmp"),
|
||||
PictureType.gif => File(image, "image/gif"),
|
||||
PictureType.ico => File(image, "image/vnd.microsoft.icon"),
|
||||
PictureType.svg => File(image, "image/svg+xml"),
|
||||
PictureType.tif => File(image, "image/tiff"),
|
||||
PictureType.webp => File(image, "image/webp"),
|
||||
//should never happen
|
||||
_ => File(image, "image/png"),
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
return StatusCode(403, new HTTPResponse()
|
||||
{
|
||||
error = ErrorCode.Forbidden,
|
||||
error_message = "the user you have givven does not exist"
|
||||
});
|
||||
//return File(Encoding.UTF8.GetBytes(obj.ToString()), "application/json");
|
||||
}
|
||||
}
|
||||
|
||||
public class StatusUpdate
|
||||
{
|
||||
[BindRequired]
|
||||
public Status status { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Make a post to this endpoint to change the status of you account.
|
||||
/// </summary>
|
||||
/// <param name="SentData">Infomation required for status update.</param>
|
||||
/// <param name="token">The Token for your account.</param>
|
||||
/// <returns></returns>
|
||||
[HttpPost]
|
||||
[ProducesResponseType(StatusCodes.Status202Accepted)]
|
||||
[ProducesResponseType(typeof(HTTPResponse), StatusCodes.Status403Forbidden)]
|
||||
[Produces(MediaTypeNames.Application.Json)]
|
||||
[Consumes(typeof(StatusUpdate), MediaTypeNames.Application.Json)]
|
||||
[Route(Luski.Info.Routes.Default.Base + "/Status")]
|
||||
[TokenFilter]
|
||||
public IActionResult Status([FromBody]StatusUpdate SentData)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!this.CanTokenRequest(out long ID, out IActionResult? toc) && toc != null) return toc;
|
||||
|
||||
//dynamic? SentData = Newtonsoft.Json.JsonConvert.DeserializeObject<dynamic>(content);
|
||||
Status NewStatus = SentData.status;
|
||||
Status OldStatus = Tables.Users.Read(Users.Status, Users.ID.CreateParameter(ID));
|
||||
if (OldStatus != NewStatus && (int)NewStatus < (int)Enums.Status.MAX && (int)NewStatus >= 0)
|
||||
{
|
||||
// JObject @out = new()
|
||||
// {
|
||||
// { "id", ID },
|
||||
// { "before", (int)OldStatus },
|
||||
// { "after", (int)NewStatus },
|
||||
// { "type", (int)DataType.Status_Update }
|
||||
// };
|
||||
Tables.Users.Update(Users.ID, ID, Users.Status.CreateParameter(NewStatus));
|
||||
if (NewStatus == Enums.Status.Invisible) NewStatus = Enums.Status.Offline;
|
||||
// WSS.SendData(SendType.All, @out);
|
||||
}
|
||||
|
||||
return StatusCode(202);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return this.ShowError(ex);
|
||||
}
|
||||
}
|
||||
/*
|
||||
[HttpPost]
|
||||
[Route(Luski.Info.Route.Version1.Action)]
|
||||
public async Task<IActionResult> Activity()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!this.CanTokenRequest(out long token_id, out HTTPResponse? toc) && toc != null) return StatusCode(403, toc);
|
||||
string content;
|
||||
|
||||
using (StreamReader reader = new(Request.Body))
|
||||
{
|
||||
content = reader.ReadToEnd();
|
||||
}
|
||||
if (string.IsNullOrEmpty(content)) return StatusCode(403, this.ShowError<HTTPResponse>(ErrorCode.MissingPostData));
|
||||
dynamic? SentData = Newtonsoft.Json.JsonConvert.DeserializeObject<dynamic>(content);
|
||||
if (SentData == null) return this.ShowError<HTTPResponse>(ErrorCode.MissingPostData);
|
||||
if (string.IsNullOrEmpty((string)SentData.activity)) return this.ShowError<HTTPResponse>(ErrorCode.MissingPostData);
|
||||
string NewActivity = (string)SentData.activity;
|
||||
string OldActivity = Luski.Database.Read<string>("users", "activity", Luski.Database.CreateParameter("id", token_id));
|
||||
if (OldActivity != NewActivity)
|
||||
{
|
||||
JObject @out = new()
|
||||
{
|
||||
{ "id", token_id },
|
||||
{ "before", OldActivity },
|
||||
{ "after", NewActivity }
|
||||
};
|
||||
Luski.Database.Update("users", "id", token_id, Luski.Database.CreateParameter("activity", NewActivity));
|
||||