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.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<IEnumerable<User>> GetAllChildUsers(Token authToken)
[HttpGet(nameof(GetAllDirectChildUsers))]
public ActionResult<IEnumerable<User>> GetAllDirectChildUsers(Token authToken)
{
var user = Db.GetSession(authToken)?.User;
if (user == null)
@ -180,6 +181,17 @@ public class Controller : ControllerBase
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))]
public ActionResult<IEnumerable<Installation>> 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();
}
@ -318,8 +332,6 @@ public class Controller : ControllerBase
if (!session.Update(updatedUser))
return Unauthorized();
updatedUser.Password = ""; // TODO: generic sanitize return values
return updatedUser;
}

View File

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

View File

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

View File

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

View File

@ -1,3 +1,5 @@
using SQLite;
namespace InnovEnergy.App.Backend.DataTypes;
@ -8,7 +10,7 @@ public class Installation : TreeNode
public String Country { get; set; } = "";
// 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 Long { get; set; }
@ -16,4 +18,5 @@ public class Installation : TreeNode
public String S3Bucket { get; set; } = "";
public String S3Url { get; set; } = "";
}

View File

@ -52,8 +52,8 @@ public static class FolderMethods
public static IEnumerable<Installation> ChildInstallations(this Folder parent)
{
return Db
.Installations
.Where(f => f.ParentId == parent.Id);
.Installations
.Where(f => f.ParentId == parent.Id);
}
public static IEnumerable<Folder> DescendantFolders(this Folder parent)

View File

@ -112,6 +112,19 @@ public static class InstallationMethods
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,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);
}

View File

@ -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<TreeNode> AccessibleFoldersAndInstallations(this User user)
{
var folders = user.AccessibleFolders() as IEnumerable<TreeNode>;
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;
}
}

View File

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

View File

@ -13,8 +13,4 @@ public class User : TreeNode
[Unique]
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;
}
public static Boolean Create(OrderNumber2Installation o2i)
{
return Connection.Insert(o2i) > 0;
}
}

View File

@ -16,15 +16,16 @@ public static partial class Db
private static SQLiteConnection Connection { get; } = new SQLiteConnection(DbPath);
public static TableQuery<Session> Sessions => Connection.Table<Session>();
public static TableQuery<Folder> Folders => Connection.Table<Folder>();
public static TableQuery<DeletedFolder> DeletedFolders => Connection.Table<DeletedFolder>();
public static TableQuery<Installation> Installations => Connection.Table<Installation>();
public static TableQuery<DeletedInstallation> DeletedInstallations => Connection.Table<DeletedInstallation>();
public static TableQuery<User> Users => Connection.Table<User>();
public static TableQuery<DeletedUser> DeletedUsers => Connection.Table<DeletedUser>();
public static TableQuery<FolderAccess> FolderAccess => Connection.Table<FolderAccess>();
public static TableQuery<InstallationAccess> InstallationAccess => Connection.Table<InstallationAccess>();
public static TableQuery<Session> Sessions => Connection.Table<Session>();
public static TableQuery<Folder> Folders => Connection.Table<Folder>();
public static TableQuery<DeletedFolder> DeletedFolders => Connection.Table<DeletedFolder>();
public static TableQuery<Installation> Installations => Connection.Table<Installation>();
public static TableQuery<DeletedInstallation> DeletedInstallations => Connection.Table<DeletedInstallation>();
public static TableQuery<User> Users => Connection.Table<User>();
public static TableQuery<DeletedUser> DeletedUsers => Connection.Table<DeletedUser>();
public static TableQuery<FolderAccess> FolderAccess => Connection.Table<FolderAccess>();
public static TableQuery<InstallationAccess> InstallationAccess => Connection.Table<InstallationAccess>();
public static TableQuery<OrderNumber2Installation> OrderNumber2Installation => Connection.Table<OrderNumber2Installation>();
public static void Init()
@ -47,6 +48,7 @@ public static partial class Db
Connection.CreateTable<FolderAccess>();
Connection.CreateTable<InstallationAccess>();
Connection.CreateTable<Session>();
Connection.CreateTable<OrderNumber2Installation>();
});
Observable.Interval(TimeSpan.FromDays(0.5))
@ -98,4 +100,5 @@ public static partial class Db
.Select(i => i.RenewS3BucketUrl())
.WhenAll();
}
}

View File

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

View File

@ -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 = "<Pending>")]
public static Boolean SendVerificationMessage (User emailRecipientUser)
{
var config = JsonSerializer.Deserialize<SmptConfig>(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);
// Todo put me into urlAndKey.json
smtp.Authenticate("fern95@ethereal.email", "dYKVnc4RQNEFckHaNV");
using var smtp = new SmtpClient();
smtp.Connect(config.Url, config.Port, false);
smtp.Send(email);
smtp.Disconnect(true);
}
smtp.Authenticate(config.Username, config.Password);
smtp.Send(email);
smtp.Disconnect(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.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<S3Cmd>(OpenRead("./Resources/s3ReadOnlyKey.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/=Salimax/@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/=ttyusb/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=tupled/@EntryIndexedValue">True</s:Boolean>