Fix multiple reset password emails Bug

This commit is contained in:
ig 2023-10-26 14:09:38 +02:00
parent 6e7d337d92
commit 76099131c2
14 changed files with 193 additions and 437 deletions

View File

@ -7,6 +7,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="AWSSDK.S3" Version="3.7.205.17" />
<PackageReference Include="Flurl.Http" Version="3.2.4" /> <PackageReference Include="Flurl.Http" Version="3.2.4" />
<PackageReference Include="Hellang.Middleware.ProblemDetails" Version="6.5.1" /> <PackageReference Include="Hellang.Middleware.ProblemDetails" Version="6.5.1" />
<PackageReference Include="Microsoft.AspNet.Identity.Core" Version="2.2.3" /> <PackageReference Include="Microsoft.AspNet.Identity.Core" Version="2.2.3" />
@ -34,213 +35,20 @@
<ItemGroup> <ItemGroup>
<ProjectReference Include="../../Lib/Utils/Utils.csproj" /> <ProjectReference Include="../../Lib/Utils/Utils.csproj" />
<ProjectReference Include="../../Lib/Mailer/Mailer.csproj" /> <ProjectReference Include="../../Lib/Mailer/Mailer.csproj" />
<ProjectReference Include="..\..\Lib\S3Utils\S3Utils.csproj" /> <ProjectReference Include="../../Lib/S3Utils/S3Utils.csproj" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Update="Resources/s3cmd.py"> <None Update="Resources/s3cmd.py">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None> </None>
<None Remove="DbBackups\db-1692621279.sqlite" />
<None Remove="DbBackups\db-1692620662.sqlite" />
<None Remove="DbBackups\db-1692620973.sqlite" />
<None Remove="DbBackups\db-1692620165.sqlite" />
<None Remove="DbBackups\db-1692620475.sqlite" />
<None Remove="DbBackups\db-1692619622.sqlite" />
<None Remove="DbBackups\db-1692620266.sqlite" />
<None Remove="DbBackups\db-1692618414.sqlite" />
<None Remove="DbBackups\db-1692618326.sqlite" />
<None Remove="DbBackups\db-1692612258.sqlite" />
<None Remove="DbBackups\db-1692619963.sqlite" />
<None Remove="DbBackups\db-1692620296.sqlite" />
<None Remove="DbBackups\db-1692618125.sqlite" />
<None Remove="DbBackups\db-1692621869.sqlite" />
<None Remove="DbBackups\db-1692621699.sqlite" />
<None Remove="DbBackups\db-1692611631.sqlite" />
<None Remove="DbBackups\db-1692627958.sqlite" />
<None Remove="DbBackups\db-1692715302.sqlite" />
<None Remove="DbBackups\db-1692715647.sqlite" />
<None Remove="DbBackups\db-1692715652.sqlite" />
<None Remove="DbBackups\db-1692884061.sqlite" />
<None Remove="DbBackups\db-1692884224.sqlite" />
<None Remove="DbBackups\db-1692884244.sqlite" />
<None Remove="DbBackups\db-1692884524.sqlite" />
<None Remove="DbBackups\db-1692884642.sqlite" />
<None Remove="DbBackups\db-1692885117.sqlite" />
<None Remove="DbBackups\db-1692885781.sqlite" />
<None Remove="DbBackups\db-1692885908.sqlite" />
<None Remove="DbBackups\db-1692889590.sqlite" />
<None Remove="DbBackups\db-1692889640.sqlite" />
<None Remove="DbBackups\db-1692890673.sqlite" />
<None Remove="DbBackups\db-1692890981.sqlite" />
<None Remove="DbBackups\db-1692891081.sqlite" />
<None Remove="DbBackups\db-1692891207.sqlite" />
<None Remove="DbBackups\db-1692891239.sqlite" />
<None Remove="DbBackups\db-1692891540.sqlite" />
<None Remove="DbBackups\db-1692891640.sqlite" />
<None Remove="DbBackups\db-1692947517.sqlite" />
<None Remove="DbBackups\db-1692951226.sqlite" />
<None Remove="DbBackups\db-1692956795.sqlite" />
<None Remove="DbBackups\db-1692957809.sqlite" />
<None Remove="DbBackups\db-1692958545.sqlite" />
<None Remove="DbBackups\db-1692965093.sqlite" />
<None Remove="DbBackups\db-1692965087.sqlite" />
<None Remove="DbBackups\db-1692965105.sqlite" />
<None Remove="DbBackups\db-1692965660.sqlite" />
<None Remove="DbBackups\db-1692965676.sqlite" />
<None Remove="DbBackups\db-1693813301.sqlite" />
<None Remove="DbBackups\db-1693815606.sqlite" />
<None Remove="DbBackups\db-1693813151.sqlite" />
<None Remove="DbBackups\db-1693388471.sqlite" />
<None Remove="DbBackups\db-1693385306.sqlite" />
<None Remove="DbBackups\db-1693236515.sqlite" />
<None Remove="DbBackups\db-1693230148.sqlite" />
<None Remove="DbBackups\db-1693301572.sqlite" />
<None Remove="DbBackups\db-1693388748.sqlite" />
<None Remove="DbBackups\db-1693388189.sqlite" />
<None Remove="DbBackups\db-1693297768.sqlite" />
<None Remove="DbBackups\db-1693299939.sqlite" />
<None Remove="DbBackups\db-1693229044.sqlite" />
<None Remove="DbBackups\db-1693225598.sqlite" />
<None Remove="DbBackups\db-1693300771.sqlite" />
<None Remove="DbBackups\db-1693212155.sqlite" />
<None Remove="DbBackups\db-1693225325.sqlite" />
<None Remove="DbBackups\db-1693212119.sqlite" />
<None Remove="DbBackups\db-1693211833.sqlite" />
<None Remove="DbBackups\db-1693392099.sqlite" />
<None Remove="DbBackups\db-1693392147.sqlite" />
<None Remove="DbBackups\db-1693391476.sqlite" />
<None Remove="DbBackups\db-1693395914.sqlite" />
<None Remove="DbBackups\db-1693394644.sqlite" />
<None Remove="DbBackups\db-1693389535.sqlite" />
<None Remove="DbBackups\db-1693389069.sqlite" />
<None Remove="DbBackups\db-1693812839.sqlite" />
<None Remove="DbBackups\db-1693389121.sqlite" />
<None Remove="DbBackups\db-1693390948.sqlite" />
<None Remove="DbBackups\db-1693401522.sqlite" />
<None Remove="DbBackups\db-1693299943.sqlite" />
<None Remove="DbBackups\db-1693230582.sqlite" />
<None Remove="DbBackups\db-1693388417.sqlite" />
<None Remove="DbBackups\db-1693238297.sqlite" />
<None Remove="DbBackups\db-1693228621.sqlite" />
<None Remove="DbBackups\db-1693388588.sqlite" />
<None Remove="DbBackups\db-1693823647.sqlite" />
<None Remove="DbBackups\db-1693390865.sqlite" />
<None Remove="DbBackups\db-1693395143.sqlite" />
<None Remove="DbBackups\db-1693823298.sqlite" />
<None Remove="DbBackups\db-1693389080.sqlite" />
<None Remove="DbBackups\db-1693390583.sqlite" />
<None Remove="DbBackups\db-1693391825.sqlite" />
<None Remove="DbBackups\db-1693391007.sqlite" />
<None Remove="DbBackups\db-1693390368.sqlite" />
<None Remove="DbBackups\db-1693391131.sqlite" />
<None Remove="DbBackups\db-1693392465.sqlite" />
<None Remove="DbBackups\db-1693820540.sqlite" />
<None Remove="DbBackups\db-1693389837.sqlite" />
<None Remove="DbBackups\db-1693394858.sqlite" />
<None Remove="DbBackups\db-1693395207.sqlite" />
<None Remove="DbBackups\db-1693815792.sqlite" />
<None Remove="DbBackups\db-1693389713.sqlite" />
<None Remove="DbBackups\db-1693391988.sqlite" />
<None Remove="DbBackups\db-1693389959.sqlite" />
<None Remove="DbBackups\db-1693395856.sqlite" />
<None Remove="DbBackups\db-1693488424.sqlite" />
<None Remove="DbBackups\db-1693474277.sqlite" />
<None Remove="DbBackups\db-1693482868.sqlite" />
<None Remove="DbBackups\db-1693472646.sqlite" />
<None Remove="DbBackups\db-1693470245.sqlite" />
<None Remove="DbBackups\db-1693470249.sqlite" />
<None Remove="DbBackups\db-1693469782.sqlite" />
<None Remove="DbBackups\db-1693497578.sqlite" />
<None Remove="DbBackups\db-1693474069.sqlite" />
<None Remove="DbBackups\db-1693499184.sqlite" />
<None Remove="DbBackups\db-1693395013.sqlite" />
<None Remove="DbBackups\db-1693398191.sqlite" />
<None Remove="DbBackups\db-1693394891.sqlite" />
<None Remove="DbBackups\db-1693390733.sqlite" />
<None Remove="DbBackups\db-1693389451.sqlite" />
<None Remove="DbBackups\db-1693390182.sqlite" />
<None Remove="DbBackups\db-1693811965.sqlite" />
<None Remove="DbBackups\db-1692966481.sqlite" />
<None Remove="DbBackups\db-1692969227.sqlite" />
<None Remove="DbBackups\db-1692966381.sqlite" />
<None Remove="DbBackups\db-1692967853.sqlite" />
<None Remove="DbBackups\db-1693820304.sqlite" />
<None Remove="DbBackups\db-1692967068.sqlite" />
<None Remove="DbBackups\db-1693822619.sqlite" />
<None Remove="DbBackups\db-1692969899.sqlite" />
<None Remove="DbBackups\db-1693820664.sqlite" />
<None Remove="DbBackups\db-1692966501.sqlite" />
<None Remove="DbBackups\db-1693820595.sqlite" />
<None Remove="DbBackups\db-1692966495.sqlite" />
<None Remove="DbBackups\db-1692967782.sqlite" />
<None Remove="DbBackups\db-1693820327.sqlite" />
<None Remove="DbBackups\db-1692966486.sqlite" />
<None Remove="DbBackups\db-1692965827.sqlite" />
<None Remove="DbBackups\db-1693581684.sqlite" />
<None Remove="DbBackups\db-1692966088.sqlite" />
<None Remove="DbBackups\db-1693207198.sqlite" />
<None Remove="DbBackups\db-1693214346.sqlite" />
<None Remove="DbBackups\db-1692979283.sqlite" />
<None Remove="DbBackups\db-1692979039.sqlite" />
<None Remove="DbBackups\db-1692979326.sqlite" />
<None Remove="DbBackups\db-1693210467.sqlite" />
<None Remove="DbBackups\db-1692970330.sqlite" />
<None Remove="DbBackups\db-1692979087.sqlite" />
<None Remove="DbBackups\db-1692971615.sqlite" />
<None Remove="DbBackups\db-1692970282.sqlite" />
<None Remove="DbBackups\db-1692966463.sqlite" />
<None Remove="DbBackups\db-1692971631.sqlite" />
<None Remove="DbBackups\db-1692967061.sqlite" />
<None Remove="DbBackups\db-1692968130.sqlite" />
<None Remove="DbBackups\db-1692969863.sqlite" />
<None Remove="DbBackups\db-1693574723.sqlite" />
<None Remove="DbBackups\db-1692969234.sqlite" />
<None Remove="DbBackups\db-1693574756.sqlite" />
<None Remove="DbBackups\db-1693572655.sqlite" />
<None Remove="DbBackups\db-1693572839.sqlite" />
<None Remove="DbBackups\db-1693563487.sqlite" />
<None Remove="DbBackups\db-1693571800.sqlite" />
<None Remove="DbBackups\db-1693554456.sqlite" />
<None Remove="DbBackups\db-1693571859.sqlite" />
<None Remove="DbBackups\db-1693571699.sqlite" />
<None Remove="DbBackups\db-1693571694.sqlite" />
<None Remove="DbBackups\db-1693555209.sqlite" />
<None Remove="DbBackups\db-1693571639.sqlite" />
<None Remove="DbBackups\db-1693465491.sqlite" />
<None Remove="DbBackups\db-1693571738.sqlite" />
<None Remove="DbBackups\db-1693465474.sqlite" />
<None Remove="DbBackups\db-1693498493.sqlite" />
<None Remove="DbBackups\db-1693575085.sqlite" />
<None Remove="DbBackups\db-1693574598.sqlite" />
<None Remove="DbBackups\db-1693575136.sqlite" />
<None Remove="DbBackups\db-1693574755.sqlite" />
<None Remove="DbBackups\db-1693574962.sqlite" />
<None Remove="DbBackups\db-1693822770.sqlite" />
<None Remove="DbBackups\db-1693822650.sqlite" />
<None Remove="DbBackups\db-1693822757.sqlite" />
<None Remove="DbBackups\db-1693822641.sqlite" />
<None Remove="DbBackups\db-1693823785.sqlite" />
<None Remove="DbBackups\db-1693823723.sqlite" />
<None Remove="DbBackups\db-1693836773.sqlite" />
<None Remove="DbBackups\db-1693837575.sqlite" />
<None Remove="DbBackups\db-1693837906.sqlite" />
<None Remove="DbBackups\db-1693838013.sqlite" />
<None Remove="DbBackups\db-1693837691.sqlite" />
<None Remove="DbBackups\db-1693837666.sqlite" />
<None Remove="DbBackups\db-1693838039.sqlite" />
<None Remove="DbBackups\db-1693838563.sqlite" />
<None Remove="DbBackups\db-1693838248.sqlite" />
<None Remove="DbBackups\db-1693838578.sqlite" />
<None Remove="DbBackups\db-1693839416.sqlite" />
<None Remove="DbBackups\db-1693839492.sqlite" />
<None Remove="DbBackups\db-1694156276.sqlite" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<Content Update="Resources\urlAndKey.json"> <Content Update="Resources/urlAndKey.json">
<CopyToPublishDirectory>Never</CopyToPublishDirectory> <CopyToPublishDirectory>Never</CopyToPublishDirectory>
</Content> </Content>
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -1,5 +1,3 @@
using System.Runtime.InteropServices.ComTypes;
using System.Text.Json.Nodes;
using InnovEnergy.App.Backend.Database; 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;
@ -21,17 +19,12 @@ public class Controller : ControllerBase
var user = Db.GetUserByEmail(username); var user = Db.GetUserByEmail(username);
if (user is null) if (user is null)
{ throw new Exceptions(400, "Null User Exception", "Must provide a user to log in as.", Request.Path.Value!);
throw new Exceptions(400,"Null User Exception", "Must provide a user to log in as.", Request.Path.Value!);
}
if (!(user.Password.IsNullOrEmpty() && user.MustResetPassword)) if (!(user.Password.IsNullOrEmpty() && user.MustResetPassword) && !user.VerifyPassword(password))
{ {
if (!user.VerifyPassword(password)) //return Unauthorized("No Password set");
{ throw new Exceptions(401, "Wrong Password Exception", "Please try again.", Request.Path.Value!);
//return Unauthorized("No Password set");
throw new Exceptions(401,"Wrong Password Exception", "Please try again.", Request.Path.Value!);
}
} }
var session = new Session(user.HidePassword().HideParentIfUserHasNoAccessToParent(user)); var session = new Session(user.HidePassword().HideParentIfUserHasNoAccessToParent(user));
@ -67,7 +60,9 @@ public class Controller : ControllerBase
if (user is null || !session.HasAccessTo(user)) if (user is null || !session.HasAccessTo(user))
return Unauthorized(); return Unauthorized();
return user.HidePassword().HideParentIfUserHasNoAccessToParent(session); return user
.HidePassword()
.HideParentIfUserHasNoAccessToParent(session);
} }
@ -102,10 +97,10 @@ public class Controller : ControllerBase
return Unauthorized(); return Unauthorized();
return installation return installation
.UsersWithDirectAccess() .UsersWithDirectAccess()
.Where(u => u.IsDescendantOf(user)) .Where(u => u.IsDescendantOf(user))
.Select(u => u.HidePassword()) .Select(u => u.HidePassword())
.ToList(); .ToList();
} }
[HttpGet(nameof(GetUsersWithInheritedAccessToInstallation))] [HttpGet(nameof(GetUsersWithInheritedAccessToInstallation))]
@ -121,11 +116,11 @@ public class Controller : ControllerBase
return Unauthorized(); return Unauthorized();
return installation return installation
.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.HidePassword() })) .Select(u => new { folderId = f.Id, folderName = f.Name, user = u.HidePassword() }))
.ToList(); .ToList();
} }
[HttpGet(nameof(GetUsersWithDirectAccessToFolder))] [HttpGet(nameof(GetUsersWithDirectAccessToFolder))]
@ -255,11 +250,11 @@ public class Controller : ControllerBase
[HttpPost(nameof(CreateUser))] [HttpPost(nameof(CreateUser))]
public ActionResult<User> CreateUser([FromBody] User newUser, Token authToken) public async Task<ActionResult<User>> CreateUser([FromBody] User newUser, Token authToken)
{ {
var create = Db.GetSession(authToken).Create(newUser); var create = Db.GetSession(authToken).Create(newUser);
return create && Db.SendNewUserEmail(newUser) return create && await Db.SendNewUserEmail(newUser)
? newUser.HidePassword() ? newUser.HidePassword()
: Unauthorized() ; : Unauthorized() ;
} }
@ -326,8 +321,8 @@ public class Controller : ControllerBase
var user = Db.GetUserById(installationAccess.UserId); var user = Db.GetUserById(installationAccess.UserId);
return session.GrantUserAccessTo(user, installation) return session.GrantUserAccessTo(user, installation)
? Ok() ? Ok()
: Unauthorized(); : Unauthorized();
} }
[HttpPost(nameof(RevokeUserAccessToInstallation))] [HttpPost(nameof(RevokeUserAccessToInstallation))]
@ -461,18 +456,19 @@ public class Controller : ControllerBase
} }
[HttpPost(nameof(ResetPasswordRequest))] [HttpPost(nameof(ResetPasswordRequest))]
public ActionResult<IEnumerable<Object>> ResetPasswordRequest(String username) public async Task<ActionResult<IEnumerable<Object>>> ResetPasswordRequest(String email)
{ {
var user = Db.GetUserByEmail(username); var user = Db.GetUserByEmail(email);
if (user is null) if (user is null)
return Unauthorized(); return Unauthorized();
var session = new Session(user.HidePassword().HideParentIfUserHasNoAccessToParent(user)); var session = new Session(user.HidePassword().HideParentIfUserHasNoAccessToParent(user));
var res = Db.Create(session); var success = Db.Create(session);
return res && Db.SendPasswordResetEmail(user, session.Token)
? Ok() return success && await Db.SendPasswordResetEmail(user, session.Token)
: Unauthorized(); ? Ok()
: Unauthorized();
} }

View File

@ -1,6 +1,6 @@
using System.Net.Mail;
using System.Security.Cryptography; using System.Security.Cryptography;
using InnovEnergy.App.Backend.Database; using InnovEnergy.App.Backend.Database;
using InnovEnergy.Lib.Mailer;
using InnovEnergy.Lib.Utils; using InnovEnergy.Lib.Utils;
using Convert = System.Convert; using Convert = System.Convert;
using static System.Text.Encoding; using static System.Text.Encoding;
@ -91,13 +91,14 @@ public static class UserMethods
.Skip(1); // skip self .Skip(1); // skip self
} }
public static Boolean VerifyPassword(this User user, String password) public static Boolean VerifyPassword(this User user, String? password)
{ {
return Db.GetUserByEmail(user.Email)?.Password == user.SaltAndHashPassword(password); return password is not null
&& Db.GetUserByEmail(user.Email)?.Password == user.SaltAndHashPassword(password);
} }
public static String? SaltAndHashPassword(this User user, String password) public static String SaltAndHashPassword(this User user, String password)
{ {
var dataToHash = $"{password}{user.Salt()}"; var dataToHash = $"{password}{user.Salt()}";
@ -150,8 +151,10 @@ public static class UserMethods
if (installation is null) if (installation is null)
return false; return false;
return user.HasDirectAccessTo(installation) || return user.HasDirectAccessTo(installation)
installation.Ancestors().Any(user.HasDirectAccessTo); || installation
.Ancestors()
.Any(user.HasDirectAccessTo);
} }
public static Boolean HasAccessTo(this User user, User? other) public static Boolean HasAccessTo(this User user, User? other)
@ -159,10 +162,8 @@ public static class UserMethods
if (other is null) if (other is null)
return false; return false;
if (other.Id == user.Id) return other.Id == user.Id
return true; || other
return other
.Ancestors() .Ancestors()
.Contains(user); .Contains(user);
} }
@ -172,9 +173,9 @@ public static class UserMethods
return other?.Type switch return other?.Type switch
{ {
"installation" => user.HasAccessTo((Installation)other), "installation" => user.HasAccessTo((Installation)other),
"user" => user.HasAccessTo((User)other), "user" => user.HasAccessTo((User)other),
"folder" => user.HasAccessTo((Folder)other), "folder" => user.HasAccessTo((Folder)other),
_ => false _ => false
}; };
} }
@ -183,13 +184,11 @@ public static class UserMethods
return other?.Type switch return other?.Type switch
{ {
"Installation" => user.HasAccessTo(Db.GetFolderById(other.ParentId)), "Installation" => user.HasAccessTo(Db.GetFolderById(other.ParentId)),
"User" => user.HasAccessTo(Db.GetUserById(other.ParentId)), "User" => user.HasAccessTo(Db.GetUserById(other.ParentId)),
"Folder" => user.HasAccessTo(Db.GetFolderById(other.ParentId)), "Folder" => user.HasAccessTo(Db.GetFolderById(other.ParentId)),
_ => false _ => false
}; };
} }
private static String Salt(this User user) private static String Salt(this User user)
{ {
@ -211,4 +210,34 @@ public static class UserMethods
return user; return user;
} }
public static Task SendEmail(this User user, String subject, String body)
{
return Mailer.Send(user.Name, user.Email, subject, body);
}
public static Task SendPasswordResetEmail(this User user, String token)
{
const String subject = "Reset the password of your InnovEnergy-Account";
const String resetLink = "https://monitor.innov.energy/api/ResetPassword"; // TODO: move to settings file
var body = $"Dear {user.Name}\n" +
$"To reset your password " +
$"please open this link:{resetLink}?token={token}";
return user.SendEmail(subject, body);
}
public static Task SendNewUserWelcomeMessage(this User user)
{
const String subject = "Your new InnovEnergy-Account";
var resetLink = $"https://monitor.innov.energy/?username={user.Email}"; // TODO: move to settings file
var body = $"Dear {user.Name}\n" +
$"To set your password and log in to your " +
$"Innovenergy-Account open this link:{resetLink}";
return user.SendEmail(subject, body);
}
} }

View File

@ -1,12 +1,11 @@
using System.Reactive.Concurrency; using System.Reactive.Concurrency;
using System.Reactive.Linq; using System.Reactive.Linq;
using CliWrap;
using CliWrap.Buffered;
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.S3Utils; using InnovEnergy.Lib.S3Utils;
using InnovEnergy.Lib.S3Utils.DataTypes; using InnovEnergy.Lib.S3Utils.DataTypes;
using InnovEnergy.Lib.Utils;
using SQLite; using SQLite;
using SQLiteConnection = SQLite.SQLiteConnection; using SQLiteConnection = SQLite.SQLiteConnection;
@ -38,35 +37,15 @@ public static partial class Db
memoryConnection.CreateTable<InstallationAccess>(); memoryConnection.CreateTable<InstallationAccess>();
memoryConnection.CreateTable<Session>(); memoryConnection.CreateTable<Session>();
memoryConnection.CreateTable<OrderNumber2Installation>(); memoryConnection.CreateTable<OrderNumber2Installation>();
fileConnection.Table<Session> ().ForEach(memoryConnection.Insert);
fileConnection.Table<Folder> ().ForEach(memoryConnection.Insert);
fileConnection.Table<Installation> ().ForEach(memoryConnection.Insert);
fileConnection.Table<User> ().ForEach(memoryConnection.Insert);
fileConnection.Table<FolderAccess> ().ForEach(memoryConnection.Insert);
fileConnection.Table<InstallationAccess> ().ForEach(memoryConnection.Insert);
fileConnection.Table<OrderNumber2Installation>().ForEach(memoryConnection.Insert);
foreach (var obj in fileConnection.Table<Session>())
{
memoryConnection.Insert(obj);
}
foreach (var obj in fileConnection.Table<Folder>())
{
memoryConnection.Insert(obj);
}
foreach (var obj in fileConnection.Table<Installation>())
{
memoryConnection.Insert(obj);
}
foreach (var obj in fileConnection.Table<User>())
{
memoryConnection.Insert(obj);
}
foreach (var obj in fileConnection.Table<FolderAccess>())
{
memoryConnection.Insert(obj);
}
foreach (var obj in fileConnection.Table<InstallationAccess>())
{
memoryConnection.Insert(obj);
}
foreach (var obj in fileConnection.Table<OrderNumber2Installation>())
{
memoryConnection.Insert(obj);
}
return memoryConnection; return memoryConnection;
}))(); }))();
@ -152,34 +131,53 @@ public static partial class Db
private static async Task UpdateS3Urls() private static async Task UpdateS3Urls()
{ {
var regions = Installations var regions = Installations
.Select(i => i.S3Region) .Select(i => i.S3Region)
.Distinct().ToList(); .Distinct()
.ToList();
const String provider = "exo.io"; const String provider = "exo.io";
foreach (var region in regions) foreach (var region in regions)
{ {
var bucketList = await new S3Region($"https://{region}.{provider}", ExoCmd.S3Creds!).ListAllBuckets(); var s3Region = new S3Region($"https://{region}.{provider}", ExoCmd.S3Creds!);
var bucketList = await s3Region.ListAllBuckets();
foreach (var bucket in bucketList.Buckets) var installations = from bucket in bucketList.Buckets
from installation in Installations
where installation.BucketName() == bucket.BucketName
select installation;
foreach (var installation in installations)
{ {
foreach (var installation in Installations) await installation.RenewS3Credentials();
{
if (installation.BucketName() == bucket.BucketName)
{
await installation.RenewS3Credentials();
}
}
} }
} }
} }
public static Boolean SendPasswordResetEmail(User user, String sessionToken) public static async Task<Boolean> SendPasswordResetEmail(User user, String sessionToken)
{ {
return Email.Email.SendPasswordResetMessage(user, sessionToken); try
{
await user.SendPasswordResetEmail(sessionToken);
return true;
}
catch
{
return false;
}
} }
public static Boolean SendNewUserEmail(User user) public static async Task<Boolean> SendNewUserEmail(User user)
{ {
return Email.Email.SendNewUserMessage(user); try
{
await user.SendNewUserWelcomeMessage();
return true;
}
catch
{
return false;
}
} }
public static Boolean DeleteUserPassword(User user) public static Boolean DeleteUserPassword(User user)

View File

@ -1,5 +1,4 @@
using InnovEnergy.App.Backend.DataTypes; using InnovEnergy.App.Backend.DataTypes;
using InnovEnergy.App.Backend.DataTypes.Methods;
using InnovEnergy.App.Backend.Relations; using InnovEnergy.App.Backend.Relations;
@ -26,10 +25,10 @@ public static partial class Db
.FirstOrDefault(u => u.Id == id); .FirstOrDefault(u => u.Id == id);
} }
public static User? GetUserByEmail(String userName) public static User? GetUserByEmail(String email)
{ {
return Users return Users
.FirstOrDefault(u => u.Email == userName); .FirstOrDefault(u => u.Email == email);
} }
public static Session? GetSession(String token) public static Session? GetSession(String token)

View File

@ -1,83 +0,0 @@
using System.Diagnostics.CodeAnalysis;
using InnovEnergy.App.Backend.DataTypes;
using InnovEnergy.Lib.Mailer;
using JsonSerializer = System.Text.Json.JsonSerializer;
namespace InnovEnergy.App.Backend.Email;
public static class Email
{
[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<SmtpConfig>(File.OpenRead("./Resources/smtpConfig.json"))!;
var mailer = new Mailer();
mailer.From("InnovEnergy", "noreply@innov.energy");
mailer.To(emailRecipientUser.Name, emailRecipientUser.Email);
mailer.Subject("Create a new password for your Innovenergy-Account");
mailer.Body("Dear " + emailRecipientUser.Name +
"\n Please create a new password for your Innovenergy-account." +
"\n To do this just login at https://HEEEEELP");
return mailer.SendEmailUsingSmtpConfig(config);
}
[UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "<Pending>")]
public static Boolean SendPasswordResetMessage (User emailRecipientUser, String token)
{
var config = JsonSerializer.Deserialize<SmtpConfig>(File.OpenRead("./Resources/smtpConfig.json"))!;
//todo am I right?
const String resetLink = "https://monitor.innov.energy/api/ResetPassword";
var mailer = new Mailer();
try
{
mailer.From("InnovEnergy", "noreply@innov.energy");
mailer.To(emailRecipientUser.Name, emailRecipientUser.Email);
mailer.Subject("Reset the password of your Innovenergy-Account");
mailer.Body("Dear " + emailRecipientUser.Name
+ "\n To reset your password open this link:"
+ resetLink + "?token="
+ token);
return mailer.SendEmailUsingSmtpConfig(config);
}
catch (Exception)
{
return false;
}
}
[UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "<Pending>")]
public static Boolean SendNewUserMessage (User emailRecipientUser)
{
var config = JsonSerializer.Deserialize<SmtpConfig>(File.OpenRead("./Resources/smtpConfig.json"))!;
//todo am I right?
var resetLink = $"https://monitor.innov.energy/?username={emailRecipientUser.Email}";
var mailer = new Mailer();
try
{
mailer.From("InnovEnergy", "noreply@innov.energy");
mailer.To(emailRecipientUser.Name, emailRecipientUser.Email);
mailer.Subject("Your new Innovenergy-Account");
mailer.Body("Dear " + emailRecipientUser.Name
+ "\n To set your password and log in to your Innovenergy-Account open this link:"
+ resetLink);
return mailer.SendEmailUsingSmtpConfig(config);
}
catch (Exception)
{
return false;
}
}
}

View File

@ -3,7 +3,6 @@ using InnovEnergy.App.Backend.Database;
using Microsoft.AspNetCore.HttpOverrides; using Microsoft.AspNetCore.HttpOverrides;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Models;
using System.Net;
using InnovEnergy.Lib.Utils; using InnovEnergy.Lib.Utils;
namespace InnovEnergy.App.Backend; namespace InnovEnergy.App.Backend;
@ -20,7 +19,7 @@ public static class Program
builder.Services.AddProblemDetails(setup => builder.Services.AddProblemDetails(setup =>
{ {
//This includes the stacktrace in Development Env //This includes the stacktrace in Development Env
setup.IncludeExceptionDetails = (ctx, env) => builder.Environment.IsDevelopment() || builder.Environment.IsStaging(); setup.IncludeExceptionDetails = (_, _) => builder.Environment.IsDevelopment() || builder.Environment.IsStaging();
//This handles our Exceptions //This handles our Exceptions
setup.Map<Exceptions>(exception => new ProblemDetails setup.Map<Exceptions>(exception => new ProblemDetails
@ -43,8 +42,6 @@ public static class Program
app.Use(async (context, next) => app.Use(async (context, next) =>
{ {
var x = 2;
context.Request.WriteLine(); context.Request.WriteLine();
await next(context); await next(context);

View File

@ -14,7 +14,7 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="AWSSDK.S3" Version="3.7.203.12" /> <PackageReference Include="AWSSDK.S3" Version="3.7.205.17" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@ -1,55 +1,41 @@
using System.Diagnostics.CodeAnalysis;
using System.Text.Json;
using MailKit.Net.Smtp; using MailKit.Net.Smtp;
using MimeKit; using MimeKit;
namespace InnovEnergy.Lib.Mailer; namespace InnovEnergy.Lib.Mailer;
public class Mailer public static class Mailer
{ {
private static MimeMessage Email = new(); [UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "<Pending>")]
public static async Task Send(String recipientName, String recipientEmailAddress, String subject, String body)
public MimeMessage To(String name, String emailAddress)
{ {
Email.To.Add(new MailboxAddress(name, emailAddress)); var config = await ReadMailerConfig();
return Email;
} var from = new MailboxAddress(config!.SenderName, config.SenderAddress);
var to = new MailboxAddress(recipientName, recipientEmailAddress);
public MimeMessage From(String name, String emailAddress)
{ var msg = new MimeMessage
Email.From.Add(new MailboxAddress(name, emailAddress));
return Email;
}
public MimeMessage Subject(String subjectText)
{
Email.Subject = subjectText;
return Email;
}
public MimeMessage Body(String bodyText)
{
Email.Body = new TextPart(MimeKit.Text.TextFormat.Plain)
{ {
Text = bodyText From = { from },
To = { to },
Subject = subject,
Body = new TextPart { Text = body }
}; };
return Email;
using var smtp = new SmtpClient();
await smtp.ConnectAsync(config.SmtpServerUrl, config.SmtpPort, false);
await smtp.AuthenticateAsync(config.SmtpUsername, config.SmtpPassword);
await smtp.SendAsync(msg);
await smtp.DisconnectAsync(true);
} }
public Boolean SendEmailUsingSmtpConfig(SmtpConfig config) [RequiresUnreferencedCode("Calls System.Text.Json.JsonSerializer.DeserializeAsync<TValue>(Stream, JsonSerializerOptions, CancellationToken)")]
private static async Task<MailerConfig?> ReadMailerConfig()
{ {
try{ await using var fileStream = File.OpenRead(MailerConfig.DefaultFile);
using var smtp = new SmtpClient(); return await JsonSerializer.DeserializeAsync<MailerConfig>(fileStream);
smtp.Connect(config.Url, config.Port, false);
smtp.Authenticate(config.Username, config.Password);
smtp.Send(Email);
smtp.Disconnect(true);
}
catch (Exception)
{
return false;
}
return true;
} }
} }

View File

@ -7,4 +7,16 @@
<ItemGroup> <ItemGroup>
<PackageReference Include="MailKit" Version="4.2.0" /> <PackageReference Include="MailKit" Version="4.2.0" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<Content Update="MailerConfig.json">
<ExcludeFromSingleFile>true</ExcludeFromSingleFile>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
</Content>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="../Utils/Utils.csproj" />
</ItemGroup>
</Project> </Project>

View File

@ -0,0 +1,17 @@
using System.Diagnostics.CodeAnalysis;
namespace InnovEnergy.Lib.Mailer;
[SuppressMessage("ReSharper", "ClassNeverInstantiated.Global")]
public class MailerConfig
{
public required String SmtpServerUrl { get; init; }
public required String SmtpUsername { get; init; }
public required String SmtpPassword { get; init; }
public UInt16 SmtpPort { get; init; } = 587;
public required String SenderName { get; init; }
public required String SenderAddress { get; init; }
public const String DefaultFile = $"{nameof(MailerConfig)}.json";
}

View File

@ -0,0 +1,9 @@
{
"SmtpServerUrl" : "mail.agenturserver.de",
"SmtpUsername" : "p518526p69",
"SmtpPassword" : "i;b*xqm4iB5uhl",
"SmtpPort" : 587,
"SenderName" : "InnovEnergy",
"SenderAddress" : "noreply@innov.energy"
}

View File

@ -1,12 +0,0 @@
using System.Diagnostics.CodeAnalysis;
namespace InnovEnergy.Lib.Mailer;
[SuppressMessage("ReSharper", "ClassNeverInstantiated.Global")]
public class SmtpConfig
{
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

@ -42,11 +42,11 @@ public static class S3
.Select(o => new S3Url(o.Key, bucket)); .Select(o => new S3Url(o.Key, bucket));
} }
public static async Task<ListBucketsResponse> ListAllBuckets(this S3Region region) public static Task<ListBucketsResponse> ListAllBuckets(this S3Region region)
{ {
return await region return region
.GetS3Client() .GetS3Client()
.ListBucketsAsync(); .ListBucketsAsync();
} }
public static Task<Boolean> PutObject(this S3Url path, String data, Encoding encoding) => path.PutObject(encoding.GetBytes(data)); public static Task<Boolean> PutObject(this S3Url path, String data, Encoding encoding) => path.PutObject(encoding.GetBytes(data));