Added deletedFolders/installations/users for archive purposes, login is now possible without pw if no pw is set and you need to reset your pw, user hased pws should no longer be given out by the backend, moving folders and installations now have their own calls, loading s3 keys from disk on use
This commit is contained in:
parent
656b671962
commit
7d4309f3af
|
@ -3,6 +3,7 @@
|
|||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Flurl.Http" Version="3.2.4" />
|
||||
<PackageReference Include="MailKit" Version="3.6.0" />
|
||||
<PackageReference Include="Microsoft.AspNet.Identity.Core" Version="2.2.3" />
|
||||
<PackageReference Include="Microsoft.AspNet.Identity.Owin" Version="2.2.3" />
|
||||
<PackageReference Include="Microsoft.AspNet.WebApi.Core" Version="5.2.9" />
|
||||
|
@ -26,14 +27,16 @@
|
|||
<ProjectReference Include="../../Lib/Utils/Utils.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Resources" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<None Update="Resources/s3cmd.py">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Content Update="Resources\urlAndKey.json">
|
||||
<CopyToPublishDirectory>Never</CopyToPublishDirectory>
|
||||
</Content>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
@ -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<Session> 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<User> UpdatePassword(String newPassword, Token authToken)
|
||||
{
|
||||
var session = Db.GetSession(authToken);
|
||||
|
||||
return session.UpdatePassword(newPassword)
|
||||
? Ok()
|
||||
: Unauthorized();
|
||||
}
|
||||
|
||||
|
||||
|
||||
[HttpPut(nameof(UpdateInstallation))]
|
||||
public ActionResult<Installation> 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)
|
||||
{
|
||||
|
|
|
@ -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
|
|
@ -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; } = "";
|
||||
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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<User> UsersWithInheritedAccess(this Folder folder)
|
||||
{
|
||||
return folder
|
||||
|
|
|
@ -20,7 +20,8 @@ public static class InstallationMethods
|
|||
}
|
||||
|
||||
public static async Task<Boolean> 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<Folder> Ancestors(this Installation installation)
|
||||
{
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
||||
}
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"ReadOnlyS3Key": "EXO44d2979c8e570eae81ead564",
|
||||
"ReadOnlyS3Secret": "55MAqyO_FqUmh7O64VIO0egq50ERn_WIAWuc2QC44QU" ,
|
||||
"ReadWriteS3Key": "EXO87ca85e29dd412f1238f1cf0",
|
||||
"ReadWriteS3Secret": "-T9TAqy9a3-0-xj7HKsFFJOCcxfRpcnL6OW5oOrOcWU"
|
||||
}
|
|
@ -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<Dictionary<String, String>>(OpenRead("./Resources/urlAndKey.json"))?["ReadOnlyS3Key"],
|
||||
secret: Deserialize<Dictionary<String, String>>(OpenRead("./Resources/urlAndKey.json"))?["ReadOnlyS3Secret"]
|
||||
);
|
||||
|
||||
public static S3Cmd ReadWrite { get; } = new S3Cmd
|
||||
(
|
||||
key : "EXO87ca85e29dd412f1238f1cf0",
|
||||
secret: "-T9TAqy9a3-0-xj7HKsFFJOCcxfRpcnL6OW5oOrOcWU"
|
||||
key : Deserialize<Dictionary<String, String>>(OpenRead("./Resources/urlAndKey.json"))?["ReadWriteS3Key"],
|
||||
secret: Deserialize<Dictionary<String, String>>(OpenRead("./Resources/urlAndKey.json"))?["ReadWriteS3Secret"]
|
||||
);
|
||||
}
|
|
@ -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[]
|
||||
{
|
||||
|
|
Binary file not shown.
Loading…
Reference in New Issue