Merge branch 'main' of https://git.innov.energy/Innovenergy/git_trunk
This commit is contained in:
commit
05dc389eaf
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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; }
|
||||
}
|
|
@ -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; }
|
||||
}
|
|
@ -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; } = "";
|
||||
|
||||
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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
|
||||
|
||||
|
|
|
@ -13,8 +13,4 @@ public class User : TreeNode
|
|||
[Unique]
|
||||
public override String Name { get; set; } = null!;
|
||||
|
||||
|
||||
|
||||
// TODO: must reset pwd
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
}
|
|
@ -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;}
|
||||
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"Url": "smtp.ethereal.email",
|
||||
"Port": 587,
|
||||
"Username": "fern95@ethereal.email",
|
||||
"Password": "dYKVnc4RQNEFckHaNV"
|
||||
}
|
|
@ -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.
|
@ -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>
|
||||
|
|
Loading…
Reference in New Issue