This commit is contained in:
Sina Blattmann 2023-03-23 15:46:37 +01:00
commit 05dc389eaf
22 changed files with 154 additions and 85 deletions

View File

@ -2,6 +2,7 @@ using InnovEnergy.App.Backend.Database;
using InnovEnergy.App.Backend.DataTypes; using InnovEnergy.App.Backend.DataTypes;
using InnovEnergy.App.Backend.DataTypes.Methods; using InnovEnergy.App.Backend.DataTypes.Methods;
using InnovEnergy.App.Backend.Relations; using InnovEnergy.App.Backend.Relations;
using InnovEnergy.Lib.Utils;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
namespace InnovEnergy.App.Backend.Controllers; namespace InnovEnergy.App.Backend.Controllers;
@ -113,7 +114,7 @@ public class Controller : ControllerBase
.Ancestors() .Ancestors()
.SelectMany(f => f.UsersWithDirectAccess() .SelectMany(f => f.UsersWithDirectAccess()
.Where(u => u.IsDescendantOf(user)) .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(); .ToList();
} }
@ -151,7 +152,7 @@ public class Controller : ControllerBase
.Ancestors() .Ancestors()
.SelectMany(f => f.UsersWithDirectAccess() .SelectMany(f => f.UsersWithDirectAccess()
.Where(u => u.IsDescendantOf(user)) .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(); .ToList();
} }
@ -170,8 +171,8 @@ public class Controller : ControllerBase
return folder; return folder;
} }
[HttpGet(nameof(GetAllChildUsers))] [HttpGet(nameof(GetAllDirectChildUsers))]
public ActionResult<IEnumerable<User>> GetAllChildUsers(Token authToken) public ActionResult<IEnumerable<User>> GetAllDirectChildUsers(Token authToken)
{ {
var user = Db.GetSession(authToken)?.User; var user = Db.GetSession(authToken)?.User;
if (user == null) if (user == null)
@ -180,6 +181,17 @@ public class Controller : ControllerBase
return user.ChildUsers().ToList(); return user.ChildUsers().ToList();
} }
[HttpGet(nameof(GetAllChildUsers))]
public ActionResult<IEnumerable<User>> GetAllChildUsers(Token authToken)
{
var user = Db.GetSession(authToken)?.User;
if (user == null)
return Unauthorized();
return user.DescendantUsers().ToList();
}
[HttpGet(nameof(GetAllInstallations))] [HttpGet(nameof(GetAllInstallations))]
public ActionResult<IEnumerable<Installation>> GetAllInstallations(Token authToken) public ActionResult<IEnumerable<Installation>> GetAllInstallations(Token authToken)
{ {
@ -188,6 +200,8 @@ public class Controller : ControllerBase
if (user is null) if (user is null)
return Unauthorized(); return Unauthorized();
user.AccessibleInstallations().ForEach(i => i.FillOrderNumbers());
return user.AccessibleInstallations().ToList(); return user.AccessibleInstallations().ToList();
} }
@ -318,8 +332,6 @@ public class Controller : ControllerBase
if (!session.Update(updatedUser)) if (!session.Update(updatedUser))
return Unauthorized(); return Unauthorized();
updatedUser.Password = ""; // TODO: generic sanitize return values
return updatedUser; return updatedUser;
} }

View File

@ -1,6 +1,11 @@
using SQLite;
namespace InnovEnergy.App.Backend.DataTypes; 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 //Deleted Things need to have AT LEAST every property that normal things have
//Todo "Restore" Function? -k

View File

@ -1,7 +1,10 @@
using SQLite;
namespace InnovEnergy.App.Backend.DataTypes; namespace InnovEnergy.App.Backend.DataTypes;
//Deleted Things need to have AT LEAST every property that normal things have //Deleted Things need to have AT LEAST every property that normal things have
public class DeletedInstallation : Installation public class DeletedInstallation : Installation
{ {
[PrimaryKey]
public override Int64 Id { get; set; }
} }

View File

@ -7,4 +7,7 @@ namespace InnovEnergy.App.Backend.DataTypes;
public class DeletedUser : User public class DeletedUser : User
{ {
public override String Name { get; set; } = null!; public override String Name { get; set; } = null!;
[PrimaryKey]
public override Int64 Id { get; set; }
} }

View File

@ -1,3 +1,5 @@
using SQLite;
namespace InnovEnergy.App.Backend.DataTypes; namespace InnovEnergy.App.Backend.DataTypes;
@ -8,7 +10,7 @@ public class Installation : TreeNode
public String Country { get; set; } = ""; public String Country { get; set; } = "";
// TODO: make relation // TODO: make relation
public String OrderNumbers { get; set; } = ""; [Ignore] public IReadOnlyList<String> OrderNumbers { get; set; } = Array.Empty<String>();
public Double Lat { get; set; } public Double Lat { get; set; }
public Double Long { get; set; } public Double Long { get; set; }
@ -16,4 +18,5 @@ public class Installation : TreeNode
public String S3Bucket { get; set; } = ""; public String S3Bucket { get; set; } = "";
public String S3Url { get; set; } = ""; public String S3Url { get; set; } = "";
} }

View File

@ -112,6 +112,19 @@ public static class InstallationMethods
return Db.Installations.Any(i => i.Id == installation.Id); return Db.Installations.Any(i => i.Id == installation.Id);
} }
public static IReadOnlyList<String> GetOrderNumbers(this Installation installation)
{
return Db.OrderNumber2Installation
.Where(i => i.InstallationId == installation.Id)
.Select(i => i.OrderNumber)
.ToReadOnlyList<String>();
}
public static Installation FillOrderNumbers(this Installation installation)
{
installation.OrderNumbers = installation.GetOrderNumbers();
return installation;
}
} }

View File

@ -98,6 +98,30 @@ public static class SessionMethods
var user = session?.User; var user = session?.User;
var original = Db.GetInstallationById(installation?.Id); 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 return user is not null
&& installation is not null && installation is not null
@ -118,7 +142,6 @@ public static class SessionMethods
&& user.HasWriteAccess && user.HasWriteAccess
&& user.HasAccessTo(installation) && user.HasAccessTo(installation)
&& Db.Create(installation.ToDeletedInstallation()) && Db.Create(installation.ToDeletedInstallation())
// && installation.DeleteBucket().Result // TODO
&& Db.Delete(installation); && Db.Delete(installation);
} }

View File

@ -29,17 +29,13 @@ public static class UserMethods
.DirectlyAccessibleFolders() .DirectlyAccessibleFolders()
.SelectMany(f => f.DescendantFolders().Prepend(f)) .SelectMany(f => f.DescendantFolders().Prepend(f))
.Distinct(); .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<TreeNode> AccessibleFoldersAndInstallations(this User user) public static IEnumerable<TreeNode> AccessibleFoldersAndInstallations(this User user)
{ {
var folders = user.AccessibleFolders() as IEnumerable<TreeNode>; var folders = user.AccessibleFolders() as IEnumerable<TreeNode>;
user.AccessibleInstallations().ForEach(i => i.FillOrderNumbers());
var installations = user.AccessibleInstallations(); var installations = user.AccessibleInstallations();
return folders.Concat(installations); return folders.Concat(installations);
@ -203,20 +199,4 @@ public static class UserMethods
return user; return user;
} }
// TODO?
private static Boolean IsValidEmail(String email)
{
try
{
var emailAddress = new MailAddress(email);
}
catch
{
return false;
}
return true;
}
} }

View File

@ -5,7 +5,7 @@ namespace InnovEnergy.App.Backend.DataTypes;
public abstract partial class TreeNode public abstract partial class TreeNode
{ {
[PrimaryKey, AutoIncrement] [PrimaryKey, AutoIncrement]
public Int64 Id { get; set; } public virtual Int64 Id { get; set; }
public virtual String Name { get; set; } = ""; // overridden by User (unique) public virtual String Name { get; set; } = ""; // overridden by User (unique)
public String Information { get; set; } = ""; // unstructured random info public String Information { get; set; } = ""; // unstructured random info

View File

@ -13,8 +13,4 @@ public class User : TreeNode
[Unique] [Unique]
public override String Name { get; set; } = null!; public override String Name { get; set; } = null!;
// TODO: must reset pwd
} }

View File

@ -52,4 +52,9 @@ public static partial class Db
{ {
return Connection.Insert(folderAccess) > 0; return Connection.Insert(folderAccess) > 0;
} }
public static Boolean Create(OrderNumber2Installation o2i)
{
return Connection.Insert(o2i) > 0;
}
} }

View File

@ -25,6 +25,7 @@ public static partial class Db
public static TableQuery<DeletedUser> DeletedUsers => Connection.Table<DeletedUser>(); public static TableQuery<DeletedUser> DeletedUsers => Connection.Table<DeletedUser>();
public static TableQuery<FolderAccess> FolderAccess => Connection.Table<FolderAccess>(); public static TableQuery<FolderAccess> FolderAccess => Connection.Table<FolderAccess>();
public static TableQuery<InstallationAccess> InstallationAccess => Connection.Table<InstallationAccess>(); public static TableQuery<InstallationAccess> InstallationAccess => Connection.Table<InstallationAccess>();
public static TableQuery<OrderNumber2Installation> OrderNumber2Installation => Connection.Table<OrderNumber2Installation>();
public static void Init() public static void Init()
@ -47,6 +48,7 @@ public static partial class Db
Connection.CreateTable<FolderAccess>(); Connection.CreateTable<FolderAccess>();
Connection.CreateTable<InstallationAccess>(); Connection.CreateTable<InstallationAccess>();
Connection.CreateTable<Session>(); Connection.CreateTable<Session>();
Connection.CreateTable<OrderNumber2Installation>();
}); });
Observable.Interval(TimeSpan.FromDays(0.5)) Observable.Interval(TimeSpan.FromDays(0.5))
@ -98,4 +100,5 @@ public static partial class Db
.Select(i => i.RenewS3BucketUrl()) .Select(i => i.RenewS3BucketUrl())
.WhenAll(); .WhenAll();
} }
} }

View File

@ -18,12 +18,12 @@ public static partial class Db
public static Installation? GetInstallationById(Int64? id) public static Installation? GetInstallationById(Int64? id)
{ {
return Installations return Installations
.FirstOrDefault(i => i.Id == id); .FirstOrDefault(i => i.Id == id)
.FillOrderNumbers();
} }
public static User? GetUserById(Int64? id) public static User? GetUserById(Int64? id)
{ {
//TODO Sanitize Password here
return Users return Users
.FirstOrDefault(u => u.Id == id) .FirstOrDefault(u => u.Id == id)
.HidePassword(); .HidePassword();
@ -31,7 +31,6 @@ public static partial class Db
public static User? GetUserByName(String userName) public static User? GetUserByName(String userName)
{ {
//TODO Sanitize Password here
return Users return Users
.FirstOrDefault(u => u.Name == userName) .FirstOrDefault(u => u.Name == userName)
.HidePassword(); .HidePassword();
@ -39,7 +38,6 @@ public static partial class Db
public static User? GetUserWithPasswordByName(String userName) public static User? GetUserWithPasswordByName(String userName)
{ {
//TODO Sanitize Password here
return Users return Users
.FirstOrDefault(u => u.Name == userName); .FirstOrDefault(u => u.Name == userName);
} }

View File

@ -1,32 +1,34 @@
using System.Diagnostics.CodeAnalysis;
using InnovEnergy.App.Backend.DataTypes; using InnovEnergy.App.Backend.DataTypes;
using MailKit.Net.Smtp; using MailKit.Net.Smtp;
using MimeKit; using MimeKit;
using JsonSerializer = System.Text.Json.JsonSerializer;
namespace InnovEnergy.App.Backend.Mailer; namespace InnovEnergy.App.Backend.Mailer;
public static class Mailer public static class Mailer
{ {
[UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "<Pending>")]
public static Boolean SendVerificationMessage (User emailRecipientUser) public static Boolean SendVerificationMessage (User emailRecipientUser)
{ {
var config = JsonSerializer.Deserialize<SmptConfig>(File.OpenRead("./Resources/smtpConfig.json"))!;
var email = new MimeMessage(); var email = new MimeMessage();
email.From.Add(new MailboxAddress("InnovEnergy", "noreply@innov.energy")); 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.Subject = "Create a new password for your Innovenergy-Account";
email.Body = new TextPart(MimeKit.Text.TextFormat.Plain) { email.Body = new TextPart(MimeKit.Text.TextFormat.Plain) {
Text = "Dear " + emailRecipientUser.Name + "\n Please create a new password for your Innovenergy-account." + Text = "Dear " + emailRecipientUser.Name + "\n Please create a new password for your Innovenergy-account." +
"\n To do this just login at https://HEEEEELP" "\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 using var smtp = new SmtpClient();
smtp.Authenticate("fern95@ethereal.email", "dYKVnc4RQNEFckHaNV"); smtp.Connect(config.Url, config.Port, false);
smtp.Authenticate(config.Username, config.Password);
smtp.Send(email); smtp.Send(email);
smtp.Disconnect(true); smtp.Disconnect(true);
}
return true; return true;
} }

View File

@ -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;
}

View File

@ -0,0 +1,10 @@
using SQLite;
namespace InnovEnergy.App.Backend.Relations;
public class OrderNumber2Installation : Relation<String, Int64>
{
[Indexed] public String OrderNumber { get => Left ; set => Left = value;}
[Indexed] public Int64 InstallationId { get => Right; set => Right = value;}
}

View File

@ -0,0 +1,6 @@
{
"Url": "smtp.ethereal.email",
"Port": 587,
"Username": "fern95@ethereal.email",
"Password": "dYKVnc4RQNEFckHaNV"
}

View File

@ -1,19 +1,12 @@
using System.Text.Json; using System.Diagnostics.CodeAnalysis;
using static System.IO.File; using static System.IO.File;
using static System.Text.Json.JsonSerializer; using static System.Text.Json.JsonSerializer;
namespace InnovEnergy.App.Backend.S3; 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 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<S3Cmd>(OpenRead("./Resources/s3ReadOnlyKey.json"))!; public static S3Cmd ReadOnly => Deserialize<S3Cmd>(OpenRead("./Resources/s3ReadOnlyKey.json"))!;
public static S3Cmd ReadWrite => Deserialize<S3Cmd>(OpenRead("./Resources/s3ReadWriteKey.json"))!; public static S3Cmd ReadWrite => Deserialize<S3Cmd>(OpenRead("./Resources/s3ReadWriteKey.json"))!;
} }

Binary file not shown.

View File

@ -34,6 +34,7 @@
<s:Boolean x:Key="/Default/UserDictionary/Words/=resultset/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/UserDictionary/Words/=resultset/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Salimax/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/UserDictionary/Words/=Salimax/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=signurl/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/UserDictionary/Words/=signurl/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Smpt/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Trumpf/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/UserDictionary/Words/=Trumpf/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=ttyusb/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/UserDictionary/Words/=ttyusb/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=tupled/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/UserDictionary/Words/=tupled/@EntryIndexedValue">True</s:Boolean>