diff --git a/csharp/App/Backend/Controllers/Controller.cs b/csharp/App/Backend/Controllers/Controller.cs index 6cece6018..db58b240b 100644 --- a/csharp/App/Backend/Controllers/Controller.cs +++ b/csharp/App/Backend/Controllers/Controller.cs @@ -2,6 +2,7 @@ using InnovEnergy.App.Backend.Database; using InnovEnergy.App.Backend.DataTypes; using InnovEnergy.App.Backend.DataTypes.Methods; using InnovEnergy.App.Backend.Relations; +using InnovEnergy.Lib.Utils; using Microsoft.AspNetCore.Mvc; namespace InnovEnergy.App.Backend.Controllers; @@ -113,7 +114,7 @@ public class Controller : ControllerBase .Ancestors() .SelectMany(f => f.UsersWithDirectAccess() .Where(u => u.IsDescendantOf(user)) - .Select(u => new { folderId = f.Id, folderName = f.Name, user = u })) + .Select(u => new { folderId = f.Id, folderName = f.Name, user = u.HidePassword() })) .ToList(); } @@ -151,7 +152,7 @@ public class Controller : ControllerBase .Ancestors() .SelectMany(f => f.UsersWithDirectAccess() .Where(u => u.IsDescendantOf(user)) - .Select(u => new { folderId = f.Id, folderName = f.Name, user = u })) + .Select(u => new { folderId = f.Id, folderName = f.Name, user = u.HidePassword() })) .ToList(); } @@ -170,8 +171,8 @@ public class Controller : ControllerBase return folder; } - [HttpGet(nameof(GetAllChildUsers))] - public ActionResult> GetAllChildUsers(Token authToken) + [HttpGet(nameof(GetAllDirectChildUsers))] + public ActionResult> GetAllDirectChildUsers(Token authToken) { var user = Db.GetSession(authToken)?.User; if (user == null) @@ -179,7 +180,18 @@ public class Controller : ControllerBase return user.ChildUsers().ToList(); } - + + [HttpGet(nameof(GetAllChildUsers))] + public ActionResult> GetAllChildUsers(Token authToken) + { + var user = Db.GetSession(authToken)?.User; + if (user == null) + return Unauthorized(); + + return user.DescendantUsers().ToList(); + } + + [HttpGet(nameof(GetAllInstallations))] public ActionResult> GetAllInstallations(Token authToken) { @@ -188,6 +200,8 @@ public class Controller : ControllerBase if (user is null) return Unauthorized(); + user.AccessibleInstallations().ForEach(i => i.FillOrderNumbers()); + return user.AccessibleInstallations().ToList(); } @@ -317,9 +331,7 @@ public class Controller : ControllerBase if (!session.Update(updatedUser)) return Unauthorized(); - - updatedUser.Password = ""; // TODO: generic sanitize return values - + return updatedUser; } diff --git a/csharp/App/Backend/DataTypes/DeletedFolder.cs b/csharp/App/Backend/DataTypes/DeletedFolder.cs index 5fb1e5951..d25995835 100644 --- a/csharp/App/Backend/DataTypes/DeletedFolder.cs +++ b/csharp/App/Backend/DataTypes/DeletedFolder.cs @@ -1,6 +1,11 @@ +using SQLite; + namespace InnovEnergy.App.Backend.DataTypes; -public class DeletedFolder : Folder {} +public class DeletedFolder : Folder +{ + [PrimaryKey] + public override Int64 Id { get; set; } +} -//Deleted Things need to have AT LEAST every property that normal things have -//Todo "Restore" Function? -k \ No newline at end of file +//Deleted Things need to have AT LEAST every property that normal things have \ No newline at end of file diff --git a/csharp/App/Backend/DataTypes/DeletedInstallation.cs b/csharp/App/Backend/DataTypes/DeletedInstallation.cs index 9c7f3d4d4..5cd103d84 100644 --- a/csharp/App/Backend/DataTypes/DeletedInstallation.cs +++ b/csharp/App/Backend/DataTypes/DeletedInstallation.cs @@ -1,7 +1,10 @@ +using SQLite; + namespace InnovEnergy.App.Backend.DataTypes; //Deleted Things need to have AT LEAST every property that normal things have public class DeletedInstallation : Installation { - + [PrimaryKey] + public override Int64 Id { get; set; } } \ No newline at end of file diff --git a/csharp/App/Backend/DataTypes/DeletedUser.cs b/csharp/App/Backend/DataTypes/DeletedUser.cs index 7d7a5cad2..04e169dc8 100644 --- a/csharp/App/Backend/DataTypes/DeletedUser.cs +++ b/csharp/App/Backend/DataTypes/DeletedUser.cs @@ -7,4 +7,7 @@ namespace InnovEnergy.App.Backend.DataTypes; public class DeletedUser : User { public override String Name { get; set; } = null!; + + [PrimaryKey] + public override Int64 Id { get; set; } } \ No newline at end of file diff --git a/csharp/App/Backend/DataTypes/Installation.cs b/csharp/App/Backend/DataTypes/Installation.cs index f149135a3..c4e625646 100644 --- a/csharp/App/Backend/DataTypes/Installation.cs +++ b/csharp/App/Backend/DataTypes/Installation.cs @@ -1,3 +1,5 @@ +using SQLite; + namespace InnovEnergy.App.Backend.DataTypes; @@ -8,12 +10,13 @@ public class Installation : TreeNode public String Country { get; set; } = ""; // TODO: make relation - public String OrderNumbers { get; set; } = ""; + [Ignore] public IReadOnlyList OrderNumbers { get; set; } = Array.Empty(); public Double Lat { get; set; } public Double Long { get; set; } public String S3Bucket { get; set; } = ""; public String S3Url { get; set; } = ""; - + + } \ No newline at end of file diff --git a/csharp/App/Backend/DataTypes/Methods/Folder.cs b/csharp/App/Backend/DataTypes/Methods/Folder.cs index 10e8d41c2..9a7dfa766 100644 --- a/csharp/App/Backend/DataTypes/Methods/Folder.cs +++ b/csharp/App/Backend/DataTypes/Methods/Folder.cs @@ -52,8 +52,8 @@ public static class FolderMethods public static IEnumerable ChildInstallations(this Folder parent) { return Db - .Installations - .Where(f => f.ParentId == parent.Id); + .Installations + .Where(f => f.ParentId == parent.Id); } public static IEnumerable DescendantFolders(this Folder parent) diff --git a/csharp/App/Backend/DataTypes/Methods/Installation.cs b/csharp/App/Backend/DataTypes/Methods/Installation.cs index e1a0c766f..8319daa1f 100644 --- a/csharp/App/Backend/DataTypes/Methods/Installation.cs +++ b/csharp/App/Backend/DataTypes/Methods/Installation.cs @@ -111,7 +111,20 @@ public static class InstallationMethods { return Db.Installations.Any(i => i.Id == installation.Id); } - + public static IReadOnlyList GetOrderNumbers(this Installation installation) + { + return Db.OrderNumber2Installation + .Where(i => i.InstallationId == installation.Id) + .Select(i => i.OrderNumber) + .ToReadOnlyList(); + } + + public static Installation FillOrderNumbers(this Installation installation) + { + installation.OrderNumbers = installation.GetOrderNumbers(); + return installation; + } + } diff --git a/csharp/App/Backend/DataTypes/Methods/Session.cs b/csharp/App/Backend/DataTypes/Methods/Session.cs index 5bbff344c..e29e9e769 100644 --- a/csharp/App/Backend/DataTypes/Methods/Session.cs +++ b/csharp/App/Backend/DataTypes/Methods/Session.cs @@ -98,15 +98,39 @@ public static class SessionMethods var user = session?.User; var original = Db.GetInstallationById(installation?.Id); - + var originalOrderNumbers = original!.GetOrderNumbers(); + + if (!Equals(originalOrderNumbers, installation?.OrderNumbers)) + { + foreach (var orderNumber in installation!.OrderNumbers) + { + if (originalOrderNumbers.Contains(orderNumber)) continue; + var o2I = new OrderNumber2Installation + { + OrderNumber = orderNumber, + InstallationId = installation.Id + }; + Db.Create(o2I); + } + + foreach (var orderNumberOld in originalOrderNumbers) + { + if (!installation!.OrderNumbers.Contains(orderNumberOld)) + { + Db.OrderNumber2Installation.Delete(i => + i.InstallationId == installation.Id && i.OrderNumber == orderNumberOld); + } + } + } + return user is not null - && installation is not null - && original is not null - && user.HasWriteAccess - && user.HasAccessTo(installation) - && installation - .WithParentOf(original) // prevent moving - .Apply(Db.Update); + && installation is not null + && original is not null + && user.HasWriteAccess + && user.HasAccessTo(installation) + && installation + .WithParentOf(original) // prevent moving + .Apply(Db.Update); } public static Boolean Delete(this Session? session, Installation? installation) @@ -118,7 +142,6 @@ public static class SessionMethods && user.HasWriteAccess && user.HasAccessTo(installation) && Db.Create(installation.ToDeletedInstallation()) - // && installation.DeleteBucket().Result // TODO && Db.Delete(installation); } diff --git a/csharp/App/Backend/DataTypes/Methods/User.cs b/csharp/App/Backend/DataTypes/Methods/User.cs index 4851c0d3f..c56b2c62b 100644 --- a/csharp/App/Backend/DataTypes/Methods/User.cs +++ b/csharp/App/Backend/DataTypes/Methods/User.cs @@ -29,17 +29,13 @@ public static class UserMethods .DirectlyAccessibleFolders() .SelectMany(f => f.DescendantFolders().Prepend(f)) .Distinct(); - - // Distinct because the user might have direct access - // to a child folder of a folder he has already access to - // TODO shouldn't we prevent doubling permissions? -K" - // TODO yes we should -ig (still TODO) - // however we should still leave the distinct, defensive programming... } public static IEnumerable AccessibleFoldersAndInstallations(this User user) { var folders = user.AccessibleFolders() as IEnumerable; + + user.AccessibleInstallations().ForEach(i => i.FillOrderNumbers()); var installations = user.AccessibleInstallations(); return folders.Concat(installations); @@ -203,20 +199,4 @@ public static class UserMethods return user; } - - - // TODO? - private static Boolean IsValidEmail(String email) - { - try - { - var emailAddress = new MailAddress(email); - } - catch - { - return false; - } - - return true; - } } \ No newline at end of file diff --git a/csharp/App/Backend/DataTypes/TreeNode.cs b/csharp/App/Backend/DataTypes/TreeNode.cs index 796ab9cc2..b1a4a72fa 100644 --- a/csharp/App/Backend/DataTypes/TreeNode.cs +++ b/csharp/App/Backend/DataTypes/TreeNode.cs @@ -5,7 +5,7 @@ namespace InnovEnergy.App.Backend.DataTypes; public abstract partial class TreeNode { [PrimaryKey, AutoIncrement] - public Int64 Id { get; set; } + public virtual Int64 Id { get; set; } public virtual String Name { get; set; } = ""; // overridden by User (unique) public String Information { get; set; } = ""; // unstructured random info diff --git a/csharp/App/Backend/DataTypes/User.cs b/csharp/App/Backend/DataTypes/User.cs index ca075eecb..a9c6b4bdd 100644 --- a/csharp/App/Backend/DataTypes/User.cs +++ b/csharp/App/Backend/DataTypes/User.cs @@ -12,9 +12,5 @@ public class User : TreeNode [Unique] public override String Name { get; set; } = null!; - - - - // TODO: must reset pwd } \ No newline at end of file diff --git a/csharp/App/Backend/Database/Create.cs b/csharp/App/Backend/Database/Create.cs index 436ca6246..95849acaa 100644 --- a/csharp/App/Backend/Database/Create.cs +++ b/csharp/App/Backend/Database/Create.cs @@ -52,4 +52,9 @@ public static partial class Db { return Connection.Insert(folderAccess) > 0; } + + public static Boolean Create(OrderNumber2Installation o2i) + { + return Connection.Insert(o2i) > 0; + } } \ No newline at end of file diff --git a/csharp/App/Backend/Database/Db.cs b/csharp/App/Backend/Database/Db.cs index 086ca0d20..e9cb827ca 100644 --- a/csharp/App/Backend/Database/Db.cs +++ b/csharp/App/Backend/Database/Db.cs @@ -16,15 +16,16 @@ public static partial class Db private static SQLiteConnection Connection { get; } = new SQLiteConnection(DbPath); - public static TableQuery Sessions => Connection.Table(); - public static TableQuery Folders => Connection.Table(); - public static TableQuery DeletedFolders => Connection.Table(); - public static TableQuery Installations => Connection.Table(); - public static TableQuery DeletedInstallations => Connection.Table(); - public static TableQuery Users => Connection.Table(); - public static TableQuery DeletedUsers => Connection.Table(); - public static TableQuery FolderAccess => Connection.Table(); - public static TableQuery InstallationAccess => Connection.Table(); + public static TableQuery Sessions => Connection.Table(); + public static TableQuery Folders => Connection.Table(); + public static TableQuery DeletedFolders => Connection.Table(); + public static TableQuery Installations => Connection.Table(); + public static TableQuery DeletedInstallations => Connection.Table(); + public static TableQuery Users => Connection.Table(); + public static TableQuery DeletedUsers => Connection.Table(); + public static TableQuery FolderAccess => Connection.Table(); + public static TableQuery InstallationAccess => Connection.Table(); + public static TableQuery OrderNumber2Installation => Connection.Table(); public static void Init() @@ -47,6 +48,7 @@ public static partial class Db Connection.CreateTable(); Connection.CreateTable(); Connection.CreateTable(); + Connection.CreateTable(); }); Observable.Interval(TimeSpan.FromDays(0.5)) @@ -98,4 +100,5 @@ public static partial class Db .Select(i => i.RenewS3BucketUrl()) .WhenAll(); } + } \ No newline at end of file diff --git a/csharp/App/Backend/Database/Read.cs b/csharp/App/Backend/Database/Read.cs index 2cdc08d10..91e94bf7a 100644 --- a/csharp/App/Backend/Database/Read.cs +++ b/csharp/App/Backend/Database/Read.cs @@ -18,12 +18,12 @@ public static partial class Db public static Installation? GetInstallationById(Int64? id) { return Installations - .FirstOrDefault(i => i.Id == id); + .FirstOrDefault(i => i.Id == id) + .FillOrderNumbers(); } public static User? GetUserById(Int64? id) { - //TODO Sanitize Password here return Users .FirstOrDefault(u => u.Id == id) .HidePassword(); @@ -31,7 +31,6 @@ public static partial class Db public static User? GetUserByName(String userName) { - //TODO Sanitize Password here return Users .FirstOrDefault(u => u.Name == userName) .HidePassword(); @@ -39,7 +38,6 @@ public static partial class Db public static User? GetUserWithPasswordByName(String userName) { - //TODO Sanitize Password here return Users .FirstOrDefault(u => u.Name == userName); } diff --git a/csharp/App/Backend/Mailer/Mailer.cs b/csharp/App/Backend/Mailer/Mailer.cs index 3d038fe2b..096d5a549 100644 --- a/csharp/App/Backend/Mailer/Mailer.cs +++ b/csharp/App/Backend/Mailer/Mailer.cs @@ -1,32 +1,34 @@ +using System.Diagnostics.CodeAnalysis; using InnovEnergy.App.Backend.DataTypes; using MailKit.Net.Smtp; using MimeKit; +using JsonSerializer = System.Text.Json.JsonSerializer; namespace InnovEnergy.App.Backend.Mailer; public static class Mailer { - public static Boolean SendVerificationMessage (User emailRecipientUser) + [UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "")] + public static Boolean SendVerificationMessage (User emailRecipientUser) { + var config = JsonSerializer.Deserialize(File.OpenRead("./Resources/smtpConfig.json"))!; var email = new MimeMessage(); email.From.Add(new MailboxAddress("InnovEnergy", "noreply@innov.energy")); - email.To.Add(new MailboxAddress(emailRecipientUser.Name, "fern95@ethereal.email")); + email.To.Add(new MailboxAddress(emailRecipientUser.Name, emailRecipientUser.Email)); email.Subject = "Create a new password for your Innovenergy-Account"; email.Body = new TextPart(MimeKit.Text.TextFormat.Plain) { Text = "Dear " + emailRecipientUser.Name + "\n Please create a new password for your Innovenergy-account." + "\n To do this just login at https://HEEEEELP" }; - using (var smtp = new SmtpClient()) - { - smtp.Connect("smtp.ethereal.email", 587, false); + + using var smtp = new SmtpClient(); + smtp.Connect(config.Url, config.Port, false); - // Todo put me into urlAndKey.json - smtp.Authenticate("fern95@ethereal.email", "dYKVnc4RQNEFckHaNV"); + smtp.Authenticate(config.Username, config.Password); - smtp.Send(email); - smtp.Disconnect(true); - } + smtp.Send(email); + smtp.Disconnect(true); return true; } diff --git a/csharp/App/Backend/Mailer/SmptConfig.cs b/csharp/App/Backend/Mailer/SmptConfig.cs new file mode 100644 index 000000000..02b63653a --- /dev/null +++ b/csharp/App/Backend/Mailer/SmptConfig.cs @@ -0,0 +1,13 @@ +using System.Diagnostics.CodeAnalysis; + +namespace InnovEnergy.App.Backend.Mailer; + +[SuppressMessage("ReSharper", "ClassNeverInstantiated.Global")] +public class SmptConfig +{ + public String Url { get; init; } = null!; + public String Username { get; init; } = null!; + public String Password { get; init; } = null!; + public Int32 Port { get; init; } = 587; + +} \ No newline at end of file diff --git a/csharp/App/Backend/Relations/OrderNumber2Installation.cs b/csharp/App/Backend/Relations/OrderNumber2Installation.cs new file mode 100644 index 000000000..a994f7fb7 --- /dev/null +++ b/csharp/App/Backend/Relations/OrderNumber2Installation.cs @@ -0,0 +1,10 @@ +using SQLite; + +namespace InnovEnergy.App.Backend.Relations; + +public class OrderNumber2Installation : Relation +{ + [Indexed] public String OrderNumber { get => Left ; set => Left = value;} + [Indexed] public Int64 InstallationId { get => Right; set => Right = value;} + +} \ No newline at end of file diff --git a/csharp/App/Backend/Resources/smtpConfig.json b/csharp/App/Backend/Resources/smtpConfig.json new file mode 100644 index 000000000..9b4024e59 --- /dev/null +++ b/csharp/App/Backend/Resources/smtpConfig.json @@ -0,0 +1,6 @@ +{ + "Url": "smtp.ethereal.email", + "Port": 587, + "Username": "fern95@ethereal.email", + "Password": "dYKVnc4RQNEFckHaNV" +} \ No newline at end of file diff --git a/csharp/App/Backend/S3/S3Access.cs b/csharp/App/Backend/S3/S3Access.cs index b9705ee81..0084807c3 100644 --- a/csharp/App/Backend/S3/S3Access.cs +++ b/csharp/App/Backend/S3/S3Access.cs @@ -1,19 +1,12 @@ -using System.Text.Json; +using System.Diagnostics.CodeAnalysis; using static System.IO.File; using static System.Text.Json.JsonSerializer; namespace InnovEnergy.App.Backend.S3; +[SuppressMessage("Trimming", "IL2026:Members annotated with \'RequiresUnreferencedCodeAttribute\' require dynamic access otherwise can break functionality when trimming application code")] public static class S3Access { - // TODO: put these into Json files in /Resources and read them from - // there so they can be changed without recompiling - // they should be read from disk on each use, - // so the backend does not need to be restarted on change - - public static S3Cmd ReadOnly => Deserialize(OpenRead("./Resources/s3ReadOnlyKey.json"))!; - - public static S3Cmd ReadWrite => Deserialize(OpenRead("./Resources/s3ReadWriteKey.json"))!; } \ No newline at end of file diff --git a/csharp/App/Backend/db.sqlite b/csharp/App/Backend/db.sqlite index ba4d77e62..f42846685 100644 Binary files a/csharp/App/Backend/db.sqlite and b/csharp/App/Backend/db.sqlite differ diff --git a/csharp/App/OpenVpnCertificatesServer/newToken.sh b/csharp/App/OpenVpnCertificatesServer/newToken.sh new file mode 100644 index 000000000..e69de29bb diff --git a/csharp/InnovEnergy.sln.DotSettings b/csharp/InnovEnergy.sln.DotSettings index 116325457..8232b1720 100644 --- a/csharp/InnovEnergy.sln.DotSettings +++ b/csharp/InnovEnergy.sln.DotSettings @@ -34,6 +34,7 @@ True True True + True True True True