diff --git a/csharp/App/Backend/Backend.csproj b/csharp/App/Backend/Backend.csproj index f990e5c8a..78c8036ca 100644 --- a/csharp/App/Backend/Backend.csproj +++ b/csharp/App/Backend/Backend.csproj @@ -3,6 +3,7 @@ + @@ -26,14 +27,16 @@ - - - - PreserveNewest + + + Never + + + diff --git a/csharp/App/Backend/Controllers/Controller.cs b/csharp/App/Backend/Controllers/Controller.cs index 198edf861..ec0402a68 100644 --- a/csharp/App/Backend/Controllers/Controller.cs +++ b/csharp/App/Backend/Controllers/Controller.cs @@ -2,7 +2,6 @@ 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; @@ -17,11 +16,19 @@ public class Controller : ControllerBase public ActionResult Login(String username, String password) { var user = Db.GetUserByName(username); - - if (user is null || !user.VerifyPassword(password)) + + if (user is null) return Unauthorized(); + if (!(user.Password is null && user.MustResetPassword)) + { + if (!user.VerifyPassword(password)) + return Unauthorized(); + } + var session = new Session(user); + + //TODO The Frontend should check for the MustResetPassword Flag return Db.Create(session) ? session @@ -317,6 +324,18 @@ public class Controller : ControllerBase } + [HttpPut(nameof(UpdatePassword))] + public ActionResult UpdatePassword(String newPassword, Token authToken) + { + var session = Db.GetSession(authToken); + + return session.UpdatePassword(newPassword) + ? Ok() + : Unauthorized(); + } + + + [HttpPut(nameof(UpdateInstallation))] public ActionResult UpdateInstallation(Installation installation, Token authToken) { @@ -340,6 +359,26 @@ public class Controller : ControllerBase return folder; } + [HttpPut(nameof(MoveInstallation))] + public ActionResult MoveInstallation(Int64 installationId,Int64 parentId, Token authToken) + { + var session = Db.GetSession(authToken); + + return session.MoveInstallation(installationId, parentId) + ? Ok() + : Unauthorized(); + } + + [HttpPut(nameof(MoveFolder))] + public ActionResult MoveFolder(Int64 folderId,Int64 parentId, Token authToken) + { + var session = Db.GetSession(authToken); + + return session.MoveFolder(folderId, parentId) + ? Ok() + : Unauthorized(); + } + [HttpDelete(nameof(DeleteUser))] public ActionResult DeleteUser(Int64 userId, Token authToken) { diff --git a/csharp/App/Backend/DataTypes/DeletedFolder.cs b/csharp/App/Backend/DataTypes/DeletedFolder.cs new file mode 100644 index 000000000..a0193ab42 --- /dev/null +++ b/csharp/App/Backend/DataTypes/DeletedFolder.cs @@ -0,0 +1,6 @@ +namespace InnovEnergy.App.Backend.DataTypes; + +public class DeletedFolder : TreeNode {} + +//Deleted Things need to have AT LEAST every property that normal things have +//Todo "Restore" Function? -k \ No newline at end of file diff --git a/csharp/App/Backend/DataTypes/DeletedInstallation.cs b/csharp/App/Backend/DataTypes/DeletedInstallation.cs new file mode 100644 index 000000000..eb0846056 --- /dev/null +++ b/csharp/App/Backend/DataTypes/DeletedInstallation.cs @@ -0,0 +1,19 @@ +namespace InnovEnergy.App.Backend.DataTypes; + +//Deleted Things need to have AT LEAST every property that normal things have +public class DeletedInstallation : TreeNode +{ + public String Location { get; set; } = ""; + public String Region { get; set; } = ""; + public String Country { get; set; } = ""; + + // TODO: make relation + public String OrderNumbers { get; set; } = ""; + + 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/DeletedUser.cs b/csharp/App/Backend/DataTypes/DeletedUser.cs new file mode 100644 index 000000000..4f82c06a8 --- /dev/null +++ b/csharp/App/Backend/DataTypes/DeletedUser.cs @@ -0,0 +1,22 @@ +using SQLite; + +namespace InnovEnergy.App.Backend.DataTypes; + + +//Deleted Things need to have AT LEAST every property that normal things have + +public class DeletedUser : TreeNode +{ + public String Email { get; set; } = null!; + public Boolean HasWriteAccess { get; set; } = false; + public Boolean MustResetPassword { get; set; } = false; + public String Language { get; set; } = null!; + public String Password { get; set; } = null!; + + [Unique] + public override String Name { get; set; } = null!; + + + + // TODO: must reset pwd +} \ 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 ee93eeab8..10e8d41c2 100644 --- a/csharp/App/Backend/DataTypes/Methods/Folder.cs +++ b/csharp/App/Backend/DataTypes/Methods/Folder.cs @@ -23,6 +23,17 @@ public static class FolderMethods .NotNull(); } + public static DeletedFolder ToDeletedFolder(this Folder folder) + { + var deletedFolder = new DeletedFolder(); + foreach (var property in folder.GetType().GetProperties()) + { + property.SetValue(deletedFolder,property.GetValue(folder)); + } + + return deletedFolder; + } + public static IEnumerable UsersWithInheritedAccess(this Folder folder) { return folder diff --git a/csharp/App/Backend/DataTypes/Methods/Installation.cs b/csharp/App/Backend/DataTypes/Methods/Installation.cs index d9fc72728..3dc908d09 100644 --- a/csharp/App/Backend/DataTypes/Methods/Installation.cs +++ b/csharp/App/Backend/DataTypes/Methods/Installation.cs @@ -20,7 +20,8 @@ public static class InstallationMethods } public static async Task RenewS3BucketUrl(this Installation installation, TimeSpan validity) - { + { + installation.S3Url = installation.S3Url = await S3Access.ReadOnly.SignUrl(installation.BucketName(), validity); return Db.Update(installation); } @@ -61,6 +62,17 @@ public static class InstallationMethods .SelectMany(f => f.UsersWithDirectAccess()) .NotNull(); } + + public static DeletedInstallation ToDeletedInstallation(this Installation installation) + { + var deletedInstallation = new DeletedInstallation(); + foreach (var property in installation.GetType().GetProperties()) + { + property.SetValue(deletedInstallation ,property.GetValue(installation)); + } + + return deletedInstallation; + } public static IEnumerable Ancestors(this Installation installation) { diff --git a/csharp/App/Backend/DataTypes/Methods/Session.cs b/csharp/App/Backend/DataTypes/Methods/Session.cs index 5d5d2b4e7..5bbff344c 100644 --- a/csharp/App/Backend/DataTypes/Methods/Session.cs +++ b/csharp/App/Backend/DataTypes/Methods/Session.cs @@ -32,6 +32,38 @@ public static class SessionMethods .WithParentOf(original) // prevent moving .Apply(Db.Update); } + + public static Boolean MoveFolder(this Session? session, Int64 folderId, Int64 parentId) + { + var user = session?.User; + var folder = Db.GetFolderById(folderId); + var parent = Db.GetFolderById(parentId); + + return user is not null + && folder is not null + && user.HasWriteAccess + && user.HasAccessTo(folder) + && user.HasAccessTo(parent) + && folder + .Do(() => folder.ParentId = parentId) + .Apply(Db.Update); + } + + public static Boolean MoveInstallation(this Session? session, Int64 installationId, Int64 parentId) + { + var user = session?.User; + var installation = Db.GetInstallationById(installationId); + var parent = Db.GetFolderById(parentId); + + return user is not null + && installation is not null + && user.HasWriteAccess + && user.HasAccessTo(installation) + && user.HasAccessTo(parent) + && installation + .Do(() => installation.ParentId = parentId) + .Apply(Db.Update); + } public static Boolean Delete(this Session? session, Folder? folder) { @@ -41,6 +73,7 @@ public static class SessionMethods && folder is not null && user.HasWriteAccess && user.HasAccessTo(folder) + && Db.Create(folder.ToDeletedFolder()) && Db.Delete(folder); } @@ -84,21 +117,27 @@ public static class SessionMethods && installation is not null && user.HasWriteAccess && user.HasAccessTo(installation) - // && installation.DeleteBucket().Result // TODO + && Db.Create(installation.ToDeletedInstallation()) + // && installation.DeleteBucket().Result // TODO && Db.Delete(installation); } public static Boolean Create(this Session? session, User? newUser) { var sessionUser = session?.User; + + return sessionUser is not null + && newUser is not null + && sessionUser.HasWriteAccess + && newUser + .WithParent(sessionUser) + .Do(() => newUser.Password = newUser.SaltAndHashPassword(newUser.Password)) + .Do(() => newUser.MustResetPassword = true) + .Apply(Db.Create) + && Mailer.Mailer.SendVerificationMessage(newUser); + + //Send Email to new user to verify email and set password - return sessionUser is not null - && newUser is not null - && sessionUser.HasWriteAccess - && newUser - .WithParent(sessionUser) - .Do(() => newUser.Password = newUser.SaltAndHashPassword(newUser.Password)) - .Apply(Db.Create); } public static Boolean Update(this Session? session, User? editedUser) @@ -116,6 +155,16 @@ public static class SessionMethods .WithPasswordOf(originalUser) .Apply(Db.Update); } + + public static Boolean UpdatePassword(this Session? session, String? newPassword) + { + var sessionUser = session?.User; + + return sessionUser is not null + && sessionUser + .Do(() => sessionUser.Password = sessionUser.SaltAndHashPassword(newPassword)) + .Apply(Db.Update); + } public static Boolean Delete(this Session? session, User? userToDelete) { @@ -124,7 +173,8 @@ public static class SessionMethods return sessionUser is not null && userToDelete is not null && sessionUser.HasWriteAccess - && sessionUser.HasAccessTo(userToDelete) + && sessionUser.HasAccessTo(userToDelete) + && Db.Create(sessionUser.ToDeletedUser()) && Db.Delete(userToDelete); } diff --git a/csharp/App/Backend/DataTypes/Methods/User.cs b/csharp/App/Backend/DataTypes/Methods/User.cs index 5907b7c1e..4851c0d3f 100644 --- a/csharp/App/Backend/DataTypes/Methods/User.cs +++ b/csharp/App/Backend/DataTypes/Methods/User.cs @@ -97,9 +97,10 @@ public static class UserMethods public static Boolean VerifyPassword(this User user, String password) { - return user.Password == user.SaltAndHashPassword(password); + return Db.GetUserWithPasswordByName(user.Name)?.Password == user.SaltAndHashPassword(password); } + public static String SaltAndHashPassword(this User user, String password) { var dataToHash = $"{password}{user.Salt()}"; @@ -183,10 +184,27 @@ public static class UserMethods user.Password = other.Password; return user; } + + public static DeletedUser ToDeletedUser(this User user) + { + var deletedUser = new DeletedUser(); + foreach (var property in user.GetType().GetProperties()) + { + property.SetValue(deletedUser,property.GetValue(user)); + } + + return deletedUser; + } + + public static User? HidePassword(this User? user) + { + if(user is not null) + user.Password = ""; + + return user; + } + - - - // TODO? private static Boolean IsValidEmail(String email) { diff --git a/csharp/App/Backend/DataTypes/User.cs b/csharp/App/Backend/DataTypes/User.cs index 449285fe3..ca075eecb 100644 --- a/csharp/App/Backend/DataTypes/User.cs +++ b/csharp/App/Backend/DataTypes/User.cs @@ -6,11 +6,15 @@ public class User : TreeNode { public String Email { get; set; } = null!; public Boolean HasWriteAccess { get; set; } = false; + public Boolean MustResetPassword { get; set; } = false; public String Language { get; set; } = null!; public String Password { get; set; } = null!; [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 40b40cf2c..436ca6246 100644 --- a/csharp/App/Backend/Database/Create.cs +++ b/csharp/App/Backend/Database/Create.cs @@ -13,15 +13,30 @@ public static partial class Db return Connection.Insert(installation) > 0; } + public static Boolean Create(DeletedInstallation installation) + { + return Connection.Insert(installation) > 0; + } + public static Boolean Create(Folder folder) { return Connection.Insert(folder) > 0; } + public static Boolean Create(DeletedFolder folder) + { + return Connection.Insert(folder) > 0; + } + public static Boolean Create(User user) { return Connection.Insert(user) > 0; } + + public static Boolean Create(DeletedUser user) + { + return Connection.Insert(user) > 0; + } public static Boolean Create(Session session) { diff --git a/csharp/App/Backend/Database/Read.cs b/csharp/App/Backend/Database/Read.cs index 5a4eae7bb..2cdc08d10 100644 --- a/csharp/App/Backend/Database/Read.cs +++ b/csharp/App/Backend/Database/Read.cs @@ -1,5 +1,7 @@ using InnovEnergy.App.Backend.DataTypes; +using InnovEnergy.App.Backend.DataTypes.Methods; using InnovEnergy.App.Backend.Relations; +using InnovEnergy.Lib.Utils; namespace InnovEnergy.App.Backend.Database; @@ -21,14 +23,25 @@ public static partial class Db public static User? GetUserById(Int64? id) { + //TODO Sanitize Password here return Users - .FirstOrDefault(u => u.Id == id); + .FirstOrDefault(u => u.Id == id) + .HidePassword(); } public static User? GetUserByName(String userName) { + //TODO Sanitize Password here return Users - .FirstOrDefault(u => u.Name == userName); + .FirstOrDefault(u => u.Name == userName) + .HidePassword(); + } + + public static User? GetUserWithPasswordByName(String userName) + { + //TODO Sanitize Password here + return Users + .FirstOrDefault(u => u.Name == userName); } public static Session? GetSession(String token) diff --git a/csharp/App/Backend/Mailer/Mailer.cs b/csharp/App/Backend/Mailer/Mailer.cs new file mode 100644 index 000000000..b64a01a17 --- /dev/null +++ b/csharp/App/Backend/Mailer/Mailer.cs @@ -0,0 +1,35 @@ +using System; +using InnovEnergy.App.Backend.DataTypes; +using MailKit.Net.Smtp; +using MailKit; +using MimeKit; + +namespace InnovEnergy.App.Backend.Mailer; +public static class Mailer + { + public static Boolean SendVerificationMessage (User emailRecipientUser) + { + var email = new MimeMessage(); + + email.From.Add(new MailboxAddress("InnovEnergy", "noreply@innov.energy")); + email.To.Add(new MailboxAddress(emailRecipientUser.Name, "fern95@ethereal.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); + + // Todo put me into urlAndKey.json + smtp.Authenticate("fern95@ethereal.email", "dYKVnc4RQNEFckHaNV"); + + smtp.Send(email); + smtp.Disconnect(true); + } + + return true; + } + } diff --git a/csharp/App/Backend/Resources/urlAndKey.json b/csharp/App/Backend/Resources/urlAndKey.json new file mode 100644 index 000000000..137c8e690 --- /dev/null +++ b/csharp/App/Backend/Resources/urlAndKey.json @@ -0,0 +1,6 @@ +{ + "ReadOnlyS3Key": "EXO44d2979c8e570eae81ead564", + "ReadOnlyS3Secret": "55MAqyO_FqUmh7O64VIO0egq50ERn_WIAWuc2QC44QU" , + "ReadWriteS3Key": "EXO87ca85e29dd412f1238f1cf0", + "ReadWriteS3Secret": "-T9TAqy9a3-0-xj7HKsFFJOCcxfRpcnL6OW5oOrOcWU" +} \ No newline at end of file diff --git a/csharp/App/Backend/S3/S3Access.cs b/csharp/App/Backend/S3/S3Access.cs index 0fe957ad5..912997a18 100644 --- a/csharp/App/Backend/S3/S3Access.cs +++ b/csharp/App/Backend/S3/S3Access.cs @@ -1,3 +1,6 @@ +using static System.IO.File; +using static System.Text.Json.JsonSerializer; + namespace InnovEnergy.App.Backend.S3; public static class S3Access @@ -6,16 +9,16 @@ public static class S3Access // 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 { get; } = new S3Cmd ( - key : "EXO44d2979c8e570eae81ead564", - secret: "55MAqyO_FqUmh7O64VIO0egq50ERn_WIAWuc2QC44QU" + key : Deserialize>(OpenRead("./Resources/urlAndKey.json"))?["ReadOnlyS3Key"], + secret: Deserialize>(OpenRead("./Resources/urlAndKey.json"))?["ReadOnlyS3Secret"] ); public static S3Cmd ReadWrite { get; } = new S3Cmd ( - key : "EXO87ca85e29dd412f1238f1cf0", - secret: "-T9TAqy9a3-0-xj7HKsFFJOCcxfRpcnL6OW5oOrOcWU" + key : Deserialize>(OpenRead("./Resources/urlAndKey.json"))?["ReadWriteS3Key"], + secret: Deserialize>(OpenRead("./Resources/urlAndKey.json"))?["ReadWriteS3Secret"] ); } \ No newline at end of file diff --git a/csharp/App/Backend/S3/S3Cmd.cs b/csharp/App/Backend/S3/S3Cmd.cs index b8bd8f890..8b2ad3e05 100644 --- a/csharp/App/Backend/S3/S3Cmd.cs +++ b/csharp/App/Backend/S3/S3Cmd.cs @@ -8,15 +8,15 @@ public class S3Cmd { private static readonly Command Python = Cli.Wrap("python3"); - private const String S3CmdPath = "Resources/s3cmd.py"; + private const String? S3CmdPath = "Resources/s3cmd.py"; private const String S3Prefix = "s3://"; - private String[] DefaultArgs { get; } + private String?[] DefaultArgs { get; } // ReSharper disable StringLiteralTypo // ReSharper enable StringLiteralTypo - public S3Cmd(String key, String secret) + public S3Cmd(String? key, String? secret) { DefaultArgs = new[] { diff --git a/csharp/App/Backend/db.sqlite b/csharp/App/Backend/db.sqlite index 5ed0b4ad0..d79310ab5 100644 Binary files a/csharp/App/Backend/db.sqlite and b/csharp/App/Backend/db.sqlite differ