This commit is contained in:
Sina Blattmann 2023-03-20 11:16:37 +01:00
commit de96ce6f16
22 changed files with 387 additions and 454 deletions

View File

@ -2,388 +2,337 @@ 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;
using static System.Net.HttpStatusCode;
using Folder = InnovEnergy.App.Backend.DataTypes.Folder;
using Installation = InnovEnergy.App.Backend.DataTypes.Installation;
using Object = System.Object;
using User = InnovEnergy.App.Backend.DataTypes.User;
namespace InnovEnergy.App.Backend.Controllers; namespace InnovEnergy.App.Backend.Controllers;
using Token = String;
[ApiController] [ApiController]
[Route("api/")] [Route("api/")]
public class Controller public class Controller : ControllerBase
{ {
private static readonly HttpResponseMessage _Unauthorized = new HttpResponseMessage(Unauthorized); [HttpPost(nameof(Login))]
private static readonly HttpResponseMessage _Ok = new HttpResponseMessage(OK); public ActionResult<Session> Login(String username, String password)
private static readonly HttpResponseMessage _BadRequest = new HttpResponseMessage(BadRequest);
[Returns<String>]
[Returns(Unauthorized)]
[Returns(BadRequest)]
[HttpPost($"{nameof(Login)}")]
public Object Login(Credentials credentials)
{ {
var session = credentials.Login(); var user = Db.GetUserByEmail(username);
return session is null if (user is null || !user.VerifyPassword(password))
? _Unauthorized return Unauthorized();
: session;
var session = new Session(user);
return Db.Create(session)
? session
: Unauthorized();
} }
[Returns(OK)] [HttpPost(nameof(Logout))]
[Returns(Unauthorized)] public ActionResult Logout(Token authToken)
[HttpPost($"{nameof(Logout)}")]
public Object Logout()
{ {
var session = GetSession(); var session = Db.GetSession(authToken);
return session.Logout() return session.Logout()
? _Ok ? Ok()
: _Unauthorized; : Unauthorized();
} }
[Returns<User>] [HttpGet(nameof(GetUserById))]
[Returns(Unauthorized)] public ActionResult<User> GetUserById(Int64 id, Token authToken)
[HttpGet($"{nameof(GetUserById)}")]
public Object GetUserById(Int64 id)
{ {
var caller = GetSession()?.User; var session = Db.GetSession(authToken)?.User;
if (caller == null) if (session == null)
return _Unauthorized; return Unauthorized();
var user = Db.GetUserById(id); var user = Db.GetUserById(id);
if (user is null || !caller.HasAccessTo(user)) if (user is null || !session.HasAccessTo(user))
return _Unauthorized; return Unauthorized();
user.Password = ""; user.Password = "";
return user; return user;
} }
[Returns<Installation>] [HttpGet(nameof(GetInstallationById))]
[Returns(Unauthorized)] public ActionResult<Installation> GetInstallationById(Int64 id, Token authToken)
[HttpGet($"{nameof(GetInstallationById)}")]
public Object GetInstallationById(Int64 id)
{ {
var user = GetSession()?.User; var user = Db.GetSession(authToken)?.User;
if (user == null) if (user == null)
return _Unauthorized; return Unauthorized();
var installation = Db.GetInstallationById(id); var installation = Db.GetInstallationById(id);
if (installation is null || !user.HasAccessTo(installation)) if (installation is null || !user.HasAccessTo(installation))
return _Unauthorized; return Unauthorized();
return installation; return installation;
} }
[Returns<Installation>] [HttpGet(nameof(GetUsersWithAccessToInstallation))]
[Returns(Unauthorized)] public ActionResult<IEnumerable<Object>> GetUsersWithAccessToInstallation(Int64 id, Token authToken)
[HttpGet($"{nameof(GetUsersWithAccessToInstallation)}")]
public Object GetUsersWithAccessToInstallation(Int64 id)
{ {
var user = GetSession()?.User; var user = Db.GetSession(authToken)?.User;
if (user == null) if (user == null)
return _Unauthorized; return Unauthorized();
var installation = Db.GetInstallationById(id); var installation = Db.GetInstallationById(id);
if (installation is null || !user.HasAccessTo(installation)) if (installation is null || !user.HasAccessTo(installation))
return _Unauthorized; return Unauthorized();
var usersWithInheritedAccess = installation var directAccess = installation
.UsersWithDirectAccess()
.Where(u => u.IsDescendantOf(user));
var inheritedAccess = installation
.Ancestors() .Ancestors()
.SelectMany(f => f.UsersWithDirectAccess()
.Where(u => u.IsDescendantOf(user))
.Select(u => new { folderId = f.Id, user = u }))
.OfType<Object>();
var usersWithDirectAccess = installation.UsersWithDirectAccess()
.Where(u => u.IsDescendantOf(user))
.Select(u => new { installationId = installation.Id, user = u })
.OfType<Object>();
return usersWithInheritedAccess.Concat(usersWithDirectAccess);
}
[Returns<Installation>]
[Returns(Unauthorized)]
[HttpGet($"{nameof(GetUsersWithAccessToFolder)}")]
public Object GetUsersWithAccessToFolder(Int64 id)
{
var user = GetSession()?.User;
if (user == null)
return _Unauthorized;
var folder = Db.GetFolderById(id);
if (folder is null || !user.HasAccessTo(folder))
return _Unauthorized;
return folder
.Ancestors()
.Append(folder)
.SelectMany(f => f.UsersWithDirectAccess() .SelectMany(f => f.UsersWithDirectAccess()
.Where(u => u.IsDescendantOf(user)) .Where(u => u.IsDescendantOf(user))
.Select(u => new { folderId = f.Id, user = u })); .Select(u => new { folderId = f.Id, user = u }));
return directAccess
.Concat<Object>(inheritedAccess)
.Apply(Ok); // TODO: typing
} }
[Returns<Folder>] [HttpGet(nameof(GetUsersWithAccessToFolder))]
[Returns(Unauthorized)] public ActionResult<IEnumerable<Object>> GetUsersWithAccessToFolder(Int64 id, Token authToken)
[HttpGet($"{nameof(GetFolderById)}")]
public Object GetFolderById(Int64 id)
{ {
var user = GetSession()?.User; var user = Db.GetSession(authToken)?.User;
if (user == null) if (user == null)
return _Unauthorized; return Unauthorized();
var folder = Db.GetFolderById(id); var folder = Db.GetFolderById(id);
if (folder is null || !user.HasAccessTo(folder)) if (folder is null || !user.HasAccessTo(folder))
return _Unauthorized; return Unauthorized();
return folder
.Ancestors()
.Prepend(folder)
.SelectMany(f => f.UsersWithDirectAccess()
.Where(u => u.IsDescendantOf(user))
.Select(u => new { folderId = f.Id, user = u }))
.ToList();
}
[HttpGet(nameof(GetFolderById))]
public ActionResult<Folder> GetFolderById(Int64 id, Token authToken)
{
var user = Db.GetSession(authToken)?.User;
if (user == null)
return Unauthorized();
var folder = Db.GetFolderById(id);
if (folder is null || !user.HasAccessTo(folder))
return Unauthorized();
return folder; return folder;
} }
[Returns<Installation[]>] // assuming swagger knows about arrays but not lists (JSON) [HttpGet(nameof(GetAllInstallations))]
[Returns(Unauthorized)] public ActionResult<IEnumerable<Installation>> GetAllInstallations(Token authToken)
[HttpGet($"{nameof(GetAllInstallations)}/")]
public Object GetAllInstallations()
{ {
var user = GetSession()?.User; var user = Db.GetSession(authToken)?.User;
return user is null if (user is null)
? _Unauthorized return Unauthorized();
: user.AccessibleInstallations();
}
return user.AccessibleInstallations().ToList();
[Returns<Folder[]>] // assuming swagger knows about arrays but not lists (JSON)
[Returns(Unauthorized)]
[HttpGet($"{nameof(GetAllFolders)}/")]
public Object GetAllFolders()
{
var user = GetSession()?.User;
return user is null
? _Unauthorized
: user.AccessibleFolders();
}
// [Returns<Folder[]>] // assuming swagger knows about arrays but not lists (JSON)
// [Returns(Unauthorized)]
// [HttpGet($"{nameof(GetUsersOfFolder)}/")]
// public Object GetUsersOfFolder(Int64 folderId)
// {
// var caller = GetCaller();
// if (caller == null)
// return new HttpResponseMessage(Unauthorized);
//
// var folder = Db.GetFolderById(folderId);
//
// if (folder is null || !caller.HasAccessTo(folder))
// return new HttpResponseMessage(Unauthorized);
//
// return descendantUsers;
// }
[Returns<TreeNode[]>] // assuming swagger knows about arrays but not lists (JSON)
[Returns(Unauthorized)]
[HttpGet($"{nameof(GetAllFoldersAndInstallations)}/")]
public Object GetAllFoldersAndInstallations()
{
var user = GetSession()?.User;
return user is null
? _Unauthorized
: user.AccessibleFoldersAndInstallations();
} }
[Returns(OK)] [HttpGet(nameof(GetAllFolders))]
[Returns(Unauthorized)] public ActionResult<IEnumerable<Folder>> GetAllFolders(Token authToken)
[HttpPost($"{nameof(CreateUser)}/")]
public Object CreateUser(User newUser)
{ {
var session = GetSession(); var user = Db.GetSession(authToken)?.User;
return session.Create(newUser) if (user is null)
return Unauthorized();
return new(user.AccessibleFolders());
}
[HttpGet(nameof(GetAllFoldersAndInstallations))]
public ActionResult<IEnumerable<TreeNode>> GetAllFoldersAndInstallations(Token authToken)
{
var user = Db.GetSession(authToken)?.User;
if (user is null)
return Unauthorized();
return new (user.AccessibleFoldersAndInstallations());
}
[HttpPost(nameof(CreateUser))]
public ActionResult<User> CreateUser(User newUser, Token authToken)
{
return Db.GetSession(authToken).Create(newUser)
? newUser ? newUser
: _Unauthorized ; : Unauthorized() ;
} }
[Returns(OK)] [HttpPost(nameof(CreateInstallation))]
[Returns(Unauthorized)] public async Task<ActionResult<Installation>> CreateInstallation(Installation installation, Token authToken)
[HttpPost($"{nameof(CreateInstallation)}/")]
public Object CreateInstallation(Installation installation)
{ {
var session = GetSession(); if (!await Db.GetSession(authToken).Create(installation))
return Unauthorized();
return session.Create(installation) return installation;
? installation
: _Unauthorized;
} }
[Returns(OK)] [HttpPost(nameof(CreateFolder))]
[Returns(Unauthorized)] public ActionResult<Folder> CreateFolder(Folder folder, Token authToken)
[Returns(InternalServerError)]
[HttpPost($"{nameof(CreateFolder)}/")]
public Object CreateFolder(Folder folder)
{ {
var session = GetSession(); var session = Db.GetSession(authToken);
return session.Create(folder) if (!session.Create(folder))
? folder return Unauthorized();
: _Unauthorized;
return folder;
} }
[Returns(OK)] [HttpPost(nameof(GrantUserAccessToFolder))]
[Returns(Unauthorized)] public ActionResult GrantUserAccessToFolder(FolderAccess folderAccess, Token authToken)
[HttpPost($"{nameof(GrantUserAccessToFolder)}/")]
public Object GrantUserAccessToFolder([FromQuery] Int64 folderId, [FromQuery] Int64? id)
{ {
var session = GetSession(); var session = Db.GetSession(authToken);
var user = id is not null ? Db.GetUserById(id) : session?.User;
return session.GrantUserAccessTo(user, Db.GetFolderById(folderId)) // TODO: automatic BadRequest when properties are null during deserialization
? _Ok var folder = Db.GetFolderById(folderAccess.FolderId);
: _Unauthorized; var user = Db.GetUserById(folderAccess.UserId);
return session.GrantUserAccessTo(user, folder)
? Ok()
: Unauthorized();
} }
[Returns(OK)] [HttpPost(nameof(RevokeUserAccessToFolder))]
[Returns(Unauthorized)] public ActionResult RevokeUserAccessToFolder(FolderAccess folderAccess, Token authToken)
[HttpPost($"{nameof(GrantUserAccessToInstallation)}/")]
public Object GrantUserAccessToInstallation([FromQuery] Int64 installationId, [FromQuery] Int64? id)
{ {
var session = GetSession(); var session = Db.GetSession(authToken);
var user = id is not null ? Db.GetUserById(id) : session?.User; // TODO: automatic BadRequest when properties are null during deserialization
var folder = Db.GetFolderById(folderAccess.FolderId);
var user = Db.GetUserById(folderAccess.UserId);
return session.GrantUserAccessTo(user, Db.GetInstallationById(installationId)) return session.RevokeUserAccessTo(user, folder)
? _Ok ? Ok()
: _Unauthorized; : Unauthorized();
} }
[Returns(OK)]
[Returns(Unauthorized)] [HttpPost(nameof(GrantUserAccessToInstallation))]
[HttpPost($"{nameof(RevokeUserAccessToInstallation)}/")] public ActionResult GrantUserAccessToInstallation(InstallationAccess installationAccess, Token authToken)
public Object RevokeUserAccessToInstallation([FromQuery] Int64 installationId, [FromQuery] Int64? id)
{ {
var session = GetSession(); var session = Db.GetSession(authToken);
var user = id is not null ? Db.GetUserById(id) : session?.User;
// TODO: automatic BadRequest when properties are null during deserialization
var installation = Db.GetFolderById(installationAccess.InstallationId);
var user = Db.GetUserById(installationAccess.UserId);
return session.RevokeAccessTo(user, Db.GetInstallationById(installationId)) return session.GrantUserAccessTo(user, installation)
? _Ok ? Ok()
: _Unauthorized; : Unauthorized();
} }
[Returns(OK)] [HttpPost(nameof(RevokeUserAccessToInstallation))]
[Returns(Unauthorized)] public ActionResult RevokeUserAccessToInstallation(InstallationAccess installationAccess, Token authToken)
[HttpPost($"{nameof(RevokeUserAccessToFolder)}/")]
public Object RevokeUserAccessToFolder([FromQuery] Int64 folderId, [FromQuery] Int64? id)
{ {
var session = GetSession(); var session = Db.GetSession(authToken);
var user = id is not null ? Db.GetUserById(id) : session?.User;
// TODO: automatic BadRequest when properties are null during deserialization
var installation = Db.GetFolderById(installationAccess.InstallationId);
var user = Db.GetUserById(installationAccess.UserId);
return session.RevokeAccessTo(user, Db.GetFolderById(folderId)) return session.RevokeUserAccessTo(user, installation)
? _Ok ? Ok()
: _Unauthorized; : Unauthorized();
} }
[Returns(OK)]
[Returns(Unauthorized)]
[HttpPut($"{nameof(UpdateUser)}/")]
public Object UpdateUser(User updatedUser)
{
var session = GetSession();
if (!session.Update(updatedUser)) return _Unauthorized;
updatedUser.Password = ""; [HttpPut(nameof(UpdateUser))]
public ActionResult<User> UpdateUser(User updatedUser, Token authToken)
{
var session = Db.GetSession(authToken);
if (!session.Update(updatedUser))
return Unauthorized();
updatedUser.Password = ""; // TODO: generic sanitize return values
return updatedUser; return updatedUser;
} }
[Returns(OK)] [HttpPut(nameof(UpdateInstallation))]
[Returns(Unauthorized)] public ActionResult<Installation> UpdateInstallation(Installation installation, Token authToken)
[HttpPut($"{nameof(UpdateInstallation)}/")]
public Object UpdateInstallation(Installation installation)
{ {
var session = GetSession(); var session = Db.GetSession(authToken);
return session.Update(installation) if (!session.Update(installation))
? installation return Unauthorized();
: _Unauthorized;
return installation;
} }
[Returns(OK)] [HttpPut(nameof(UpdateFolder))]
[Returns(Unauthorized)] public ActionResult<Folder> UpdateFolder(Folder folder, Token authToken)
[HttpPut($"{nameof(UpdateFolder)}/")]
public Object UpdateFolder(Folder folder)
{ {
var session = GetSession(); var session = Db.GetSession(authToken);
return session.Update(folder) if (!session.Update(folder))
? folder return Unauthorized();
: _Unauthorized;
return folder;
} }
[Returns(OK)] [HttpDelete(nameof(DeleteUser))]
[Returns(Unauthorized)] public ActionResult DeleteUser(Int64 userId, Token authToken)
[HttpDelete($"{nameof(DeleteUser)}/")]
public Object DeleteUser(Int64 userId)
{ {
var session = GetSession(); var session = Db.GetSession(authToken);
var user = Db.GetUserById(userId); var user = Db.GetUserById(userId);
return session.Delete(user) return session.Delete(user)
? _Ok ? Ok()
: _Unauthorized; : Unauthorized();
} }
[Returns(OK)] [HttpDelete(nameof(DeleteInstallation))]
[Returns(Unauthorized)] public ActionResult DeleteInstallation(Int64 installationId, Token authToken)
[HttpDelete($"{nameof(DeleteInstallation)}/")]
public Object DeleteInstallation(Int64 installationId)
{ {
var session = GetSession(); var session = Db.GetSession(authToken);
var installation = Db.GetInstallationById(installationId); var installation = Db.GetInstallationById(installationId);
return session.Delete(installation) return session.Delete(installation)
? _Ok ? Ok()
: _Unauthorized; : Unauthorized();
} }
[ProducesResponseType(200)] [HttpDelete(nameof(DeleteFolder))]
[ProducesResponseType(401)] public ActionResult DeleteFolder(Int64 folderId, Token authToken)
[HttpDelete($"{nameof(DeleteFolder)}/")]
public Object DeleteFolder(Int64 folderId)
{ {
var session = GetSession(); var session = Db.GetSession(authToken);
var folder = Db.GetFolderById(folderId); var folder = Db.GetFolderById(folderId);
return session.Delete(folder) return session.Delete(folder)
? _Ok ? Ok()
: _Unauthorized; : Unauthorized();
} }
private static Session? GetSession()
{
var ctxAccessor = new HttpContextAccessor();
return ctxAccessor.HttpContext?.Items["Session"] as Session;
}
} }

View File

@ -1,22 +0,0 @@
using System.Net;
using Microsoft.AspNetCore.Mvc;
namespace InnovEnergy.App.Backend.Controllers;
public class ReturnsAttribute : ProducesResponseTypeAttribute
{
public ReturnsAttribute(HttpStatusCode statusCode) : base((Int32)statusCode)
{
}
}
public class ReturnsAttribute<T> : ProducesResponseTypeAttribute
{
public ReturnsAttribute(HttpStatusCode statusCode) : base(typeof(T), (Int32)statusCode)
{
}
public ReturnsAttribute() : base(typeof(T), (Int32)HttpStatusCode.OK)
{
}
}

View File

@ -1,6 +0,0 @@
using System.Diagnostics.CodeAnalysis;
namespace InnovEnergy.App.Backend.DataTypes;
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]
public record Credentials(String Username, String Password);

View File

@ -1,27 +0,0 @@
using InnovEnergy.App.Backend.Database;
using InnovEnergy.App.Backend.Relations;
using InnovEnergy.Lib.Utils;
namespace InnovEnergy.App.Backend.DataTypes.Methods;
public static class CredentialsMethods
{
public static Session? Login(this Credentials credentials)
{
var (username, password) = credentials;
if (username.IsNullOrEmpty() || password.IsNullOrEmpty())
return null;
var user = Db.GetUserByEmail(username);
if (user is null || !user.VerifyPassword(password))
return null;
var session = new Session(user);
return Db.Create(session)
? session
: null;
}
}

View File

@ -1,4 +1,3 @@
using System.Collections;
using InnovEnergy.App.Backend.Database; using InnovEnergy.App.Backend.Database;
using InnovEnergy.Lib.Utils; using InnovEnergy.Lib.Utils;
@ -9,20 +8,27 @@ public static class FolderMethods
public static IEnumerable<User> UsersWithAccess(this Folder folder) public static IEnumerable<User> UsersWithAccess(this Folder folder)
{ {
return UsersWithDirectAccess(folder).Concat(UsersWithInheritedAccess(folder)); var direct = folder.UsersWithDirectAccess();
var inherited = folder.UsersWithInheritedAccess();
return direct.Concat(inherited);
} }
public static IEnumerable<User> UsersWithDirectAccess(this Folder folder) public static IEnumerable<User> UsersWithDirectAccess(this Folder folder)
{ {
return Db.FolderAccess return Db
.Where(access => access.FolderId == folder.Id) .FolderAccess
.Select(access => Db.GetUserById(access.UserId)) .Where(a => a.FolderId == folder.Id)
.Select(a => Db.GetUserById(a.UserId))
.NotNull(); .NotNull();
} }
public static IEnumerable<User> UsersWithInheritedAccess(this Folder folder) public static IEnumerable<User> UsersWithInheritedAccess(this Folder folder)
{ {
return folder.Ancestors().SelectMany(f => f.UsersWithDirectAccess()).NotNull(); return folder
.Ancestors()
.SelectMany(f => f.UsersWithDirectAccess())
.NotNull();
} }
public static IEnumerable<Folder> ChildFolders(this Folder parent) public static IEnumerable<Folder> ChildFolders(this Folder parent)
@ -74,7 +80,7 @@ public static class FolderMethods
public static Boolean IsRelativeRoot(this Folder folder) public static Boolean IsRelativeRoot(this Folder folder)
{ {
return folder.ParentId < 0; return folder.ParentId < 0; // TODO
} }
public static Boolean WasMoved(this Folder folder) public static Boolean WasMoved(this Folder folder)

View File

@ -1,6 +1,5 @@
using CliWrap;
using CliWrap.Buffered;
using InnovEnergy.App.Backend.Database; using InnovEnergy.App.Backend.Database;
using InnovEnergy.App.Backend.S3;
using InnovEnergy.Lib.Utils; using InnovEnergy.Lib.Utils;
namespace InnovEnergy.App.Backend.DataTypes.Methods; namespace InnovEnergy.App.Backend.DataTypes.Methods;
@ -8,96 +7,59 @@ namespace InnovEnergy.App.Backend.DataTypes.Methods;
public static class InstallationMethods public static class InstallationMethods
{ {
public static async Task RenewS3BucketUrl(this Installation installation) private const String BucketNameSalt = "3e5b3069-214a-43ee-8d85-57d72000c19d";
public static String BucketName(this Installation installation)
{ {
await RenewS3BucketUrl(installation, TimeSpan.FromDays(1)); return $"s3://{installation.Id}-{BucketNameSalt}";
} }
public static async Task RenewS3BucketUrl(this Installation installation, TimeSpan validity) public static async Task<Boolean> RenewS3BucketUrl(this Installation installation)
{ {
const String secret = "55MAqyO_FqUmh7O64VIO0egq50ERn_WIAWuc2QC44QU"; return await RenewS3BucketUrl(installation, TimeSpan.FromDays(1));
const String apiKey = "EXO44d2979c8e570eae81ead564";
const String salt = "3e5b3069-214a-43ee-8d85-57d72000c19d";
var cmd = Cli
.Wrap("python3")
.WithArguments(new[]
{
"Resources/s3cmd.py", "signurl", $"s3://{installation.Id}-{salt}", validity.TotalSeconds.ToString(), "--access_key",
apiKey, "--secret_key", secret
});
var x = await cmd.ExecuteBufferedAsync();
installation.S3Url = x.StandardOutput.Replace("\n", "").Replace(" ", "");
Db.Update(installation);
} }
public static async Task<Boolean> RenewS3BucketUrl(this Installation installation, TimeSpan validity)
public static async Task<Boolean> CreateBucket(this Installation installation)
{ {
const String secret = "-T9TAqy9a3-0-xj7HKsFFJOCcxfRpcnL6OW5oOrOcWU"; installation.S3Url = await S3Access.ReadOnly.SignUrl(installation.BucketName(), validity);
return Db.Update(installation);
const String apiKey = "EXO87ca85e29dd412f1238f1cf0";
const String salt = "3e5b3069-214a-43ee-8d85-57d72000c19d";
var cmd = Cli
.Wrap("python3")
.WithArguments(new[]
{
"Resources/s3cmd.py", "mb", $"s3://{installation.Id}-{salt}", "--access_key",
apiKey, "--secret_key", secret
});
var x = await cmd.ExecuteBufferedAsync();
//Updating the url in the db as not wait until the next bi-daily update
var cmd2 = Cli
.Wrap("python3")
.WithArguments(new[]
{
"Resources/s3cmd.py", "signurl", $"s3://{installation.Id}-{salt}",
TimeSpan.FromDays(1).TotalSeconds.ToString(), "--access_key",
apiKey, "--secret_key", secret
});
var y = await cmd2.ExecuteBufferedAsync();
installation.S3Url = y.StandardOutput.Replace("\n", "").Replace(" ", "");
Db.Update(installation);
return x.ExitCode == 0;
} }
public static async Task<Boolean> DeleteBucket(this Installation installation) public static Task<Boolean> CreateBucket(this Installation installation)
{ {
const String secret = "-T9TAqy9a3-0-xj7HKsFFJOCcxfRpcnL6OW5oOrOcWU"; return S3Access
const String apiKey = "EXO87ca85e29dd412f1238f1cf0"; .ReadWrite
const String salt = "3e5b3069-214a-43ee-8d85-57d72000c19d"; .CreateBucket(installation.BucketName());
var cmd = Cli }
.Wrap("python3")
.WithArguments(new[] public static Task<Boolean> DeleteBucket(this Installation installation)
{ {
"Resources/s3cmd.py", "rb", $"s3://{installation.Id}-{salt}", "--access_key", return S3Access
apiKey, "--secret_key", secret .ReadWrite
}); .DeleteBucket(installation.BucketName());
var x = await cmd.ExecuteBufferedAsync();
return x.ExitCode == 0;
} }
public static IEnumerable<User> UsersWithAccess(this Installation installation) public static IEnumerable<User> UsersWithAccess(this Installation installation)
{ {
return UsersWithDirectAccess(installation).Concat(UsersWithInheritedAccess(installation)); return installation.UsersWithDirectAccess()
.Concat(installation.UsersWithInheritedAccess());
} }
public static IEnumerable<User> UsersWithDirectAccess(this Installation installation) public static IEnumerable<User> UsersWithDirectAccess(this Installation installation)
{ {
return Db.InstallationAccess return Db
.Where(access => access.InstallationId == installation.Id) .InstallationAccess
.Select(access => Db.GetUserById(access.UserId)) .Where(a => a.InstallationId == installation.Id)
.Select(a => Db.GetUserById(a.UserId))
.NotNull(); .NotNull();
} }
public static IEnumerable<User> UsersWithInheritedAccess(this Installation installation) public static IEnumerable<User> UsersWithInheritedAccess(this Installation installation)
{ {
return installation.Ancestors().SelectMany(f => f.UsersWithDirectAccess()).NotNull(); return installation
.Ancestors()
.SelectMany(f => f.UsersWithDirectAccess())
.NotNull();
} }
public static IEnumerable<Folder> Ancestors(this Installation installation) public static IEnumerable<Folder> Ancestors(this Installation installation)

View File

@ -14,7 +14,7 @@ public static class SessionMethods
&& user.HasWriteAccess && user.HasWriteAccess
&& user.HasAccessTo(folder.Parent()) && user.HasAccessTo(folder.Parent())
&& Db.Create(folder) && Db.Create(folder)
&& Db.Create(new FolderAccess() { UserId = user.Id, FolderId = folder.Id }); && Db.Create(new FolderAccess { UserId = user.Id, FolderId = folder.Id });
} }
public static Boolean Update(this Session? session, Folder? folder) public static Boolean Update(this Session? session, Folder? folder)
@ -41,19 +41,19 @@ public static class SessionMethods
} }
public static Boolean Create(this Session? session, Installation? installation) public static async Task<Boolean> Create(this Session? session, Installation? installation)
{ {
var user = session?.User; var user = session?.User;
//Note: keep generation of access _after_ generation of object to prevent "zombie" access-rights.
return user is not null return user is not null
&& installation is not null && installation is not null
&& user.HasWriteAccess && user.HasWriteAccess
&& user.HasAccessTo(installation.Parent()) && user.HasAccessTo(installation.Parent())
&& Db.Create(installation) && Db.Create(installation)
&& installation.CreateBucket().Result && Db.Create(new InstallationAccess { UserId = user.Id, InstallationId = installation.Id })
&& Db.Create(new InstallationAccess { UserId = user.Id, InstallationId = installation.Id }); && await installation.CreateBucket()
&& await installation.RenewS3BucketUrl(); // generation of access _after_ generation of
// bucket to prevent "zombie" access-rights.
} }
public static Boolean Update(this Session? session, Installation? installation) public static Boolean Update(this Session? session, Installation? installation)
@ -77,6 +77,7 @@ public static class SessionMethods
&& installation is not null && installation is not null
&& user.HasWriteAccess && user.HasWriteAccess
&& user.HasAccessTo(installation) && user.HasAccessTo(installation)
// && installation.DeleteBucket().Result // TODO: await?
&& Db.Delete(installation); && Db.Delete(installation);
} }
@ -97,13 +98,11 @@ public static class SessionMethods
var sessionUser = session?.User; var sessionUser = session?.User;
if (editedUser == null || sessionUser == null) return false; if (editedUser == null || sessionUser == null) return false;
// TODO: make specific method for changing user account settings like pwd
// Password change is only allowed for oneself // Password change is only allowed for oneself
if ( editedUser.Id != sessionUser.Id) editedUser.Password = sessionUser.Password; editedUser.Password = editedUser.Id != sessionUser.Id
else ? sessionUser.Password
{ : sessionUser.SaltAndHashPassword(editedUser.Password);
editedUser.Password = sessionUser.SaltAndHashPassword(editedUser.Password);
}
return sessionUser.HasWriteAccess return sessionUser.HasWriteAccess
&& sessionUser.HasAccessTo(editedUser) && sessionUser.HasAccessTo(editedUser)
@ -149,7 +148,7 @@ public static class SessionMethods
&& Db.Create(new FolderAccess { UserId = user.Id, FolderId = folder.Id }); && Db.Create(new FolderAccess { UserId = user.Id, FolderId = folder.Id });
} }
public static Boolean RevokeAccessTo(this Session? session, User? user, Installation? installation) public static Boolean RevokeUserAccessTo(this Session? session, User? user, Installation? installation)
{ {
var sessionUser = session?.User; var sessionUser = session?.User;
@ -159,11 +158,10 @@ public static class SessionMethods
&& user.IsDescendantOf(sessionUser) && user.IsDescendantOf(sessionUser)
&& sessionUser.HasAccessTo(installation) && sessionUser.HasAccessTo(installation)
&& user.HasAccessTo(installation) && user.HasAccessTo(installation)
&& Db.InstallationAccess.Delete(access => && Db.InstallationAccess.Delete(a => a.UserId == user.Id && a.InstallationId == installation.Id) > 0;
access.UserId == user.Id && access.InstallationId == installation.Id) > 0;
} }
public static Boolean RevokeAccessTo(this Session? session, User? user, Folder? folder) public static Boolean RevokeUserAccessTo(this Session? session, User? user, Folder? folder)
{ {
var sessionUser = session?.User; var sessionUser = session?.User;
@ -173,8 +171,7 @@ public static class SessionMethods
&& user.IsDescendantOf(sessionUser) && user.IsDescendantOf(sessionUser)
&& sessionUser.HasAccessTo(folder) && sessionUser.HasAccessTo(folder)
&& user.HasAccessTo(folder) && user.HasAccessTo(folder)
&& Db.FolderAccess.Delete(access => && Db.FolderAccess.Delete(a => a.UserId == user.Id && a.FolderId == folder.Id) > 0;
access.UserId == user.Id && access.FolderId == folder.Id) > 0;
} }
public static Boolean Logout(this Session? session) public static Boolean Logout(this Session? session)

View File

@ -32,7 +32,9 @@ public static class UserMethods
// Distinct because the user might have direct access // Distinct because the user might have direct access
// to a child folder of a folder he has already access to // to a child folder of a folder he has already access to
// ---TODO shouldn't we prevent doubling permissions? -K" // TODO shouldn't we prevent doubling permissions? -K"
// TODO yes we should -ig (still TODO)
// however we should leave the distinct, defensive programming...
} }
public static IEnumerable<TreeNode> AccessibleFoldersAndInstallations(this User user) public static IEnumerable<TreeNode> AccessibleFoldersAndInstallations(this User user)
@ -81,7 +83,6 @@ public static class UserMethods
public static Boolean IsDescendantOf(this User user, User ancestor) public static Boolean IsDescendantOf(this User user, User ancestor)
{ {
// if (user.Id == ancestor.Id) return true;
return user return user
.Ancestors() .Ancestors()
.Any(u => u.Id == ancestor.Id); .Any(u => u.Id == ancestor.Id);

View File

@ -6,7 +6,6 @@ using InnovEnergy.Lib.Utils;
using SQLite; using SQLite;
namespace InnovEnergy.App.Backend.Database; namespace InnovEnergy.App.Backend.Database;
@ -38,9 +37,6 @@ public static partial class Db
Connection.CreateTable<Session>(); Connection.CreateTable<Session>();
}); });
Observable.Interval(TimeSpan.FromDays(0.5)) Observable.Interval(TimeSpan.FromDays(0.5))
.StartWith(0) // Do it right away (on startup) .StartWith(0) // Do it right away (on startup)
.SelectMany(Cleanup) .SelectMany(Cleanup)
@ -85,7 +81,8 @@ public static partial class Db
private static Task UpdateS3Urls() private static Task UpdateS3Urls()
{ {
var renewTasks = Installations.Select(i => i.RenewS3BucketUrl()).ToArray(); return Installations
return Task.WhenAll(renewTasks); .Select(i => i.RenewS3BucketUrl())
.WhenAll();
} }
} }

View File

@ -1,4 +1,3 @@
using InnovEnergy.App.Backend.Database;
using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Models;
namespace InnovEnergy.App.Backend; namespace InnovEnergy.App.Backend;
@ -7,57 +6,50 @@ public static class Program
{ {
public static void Main(String[] args) public static void Main(String[] args)
{ {
Db.CreateFakeRelations(); //Db.CreateFakeRelations();
var builder = WebApplication.CreateBuilder(args); var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers(); // TODO: remove magic, specify controllers explicitly //builder.Services.AddHttpContextAccessor();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle //builder.Services.AddEndpointsApiExplorer();
//builder.Services.AddCors(o => o.AddDefaultPolicy(p => p.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod()));
builder.Services.AddHttpContextAccessor(); builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddCors(o => o.AddDefaultPolicy(p => p.WithOrigins("*").AllowAnyHeader().AllowAnyMethod()));
builder.Services.AddSwaggerGen(c => builder.Services.AddSwaggerGen(c =>
{ {
c.SwaggerDoc("v1", new OpenApiInfo { Title = "InnovEnergy Backend API", Version = "v1" }); c.SwaggerDoc("v1", OpenApiInfo);
c.UseAllOfToExtendReferenceSchemas(); c.UseAllOfToExtendReferenceSchemas();
c.OperationFilter<HeaderFilter>(); //Todo testing throw me out c.SupportNonNullableReferenceTypes();
}); });
var app = builder.Build(); var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment()) if (app.Environment.IsDevelopment())
{ {
app.UseSwagger(); app.UseSwagger();
app.UseSwaggerUI(cfg => cfg.EnableFilter()); app.UseSwaggerUI();
} }
app.UseCors(); //app.UseCors(p => p.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod()) ;
app.UseHttpsRedirection(); app.UseHttpsRedirection();
app.UseAuthorization(); //app.UseAuthorization();
app.Use(SetSessionUser);
app.MapControllers(); app.MapControllers();
app.Run(); app.Run();
} }
private static OpenApiInfo OpenApiInfo { get; } = new OpenApiInfo
private static async Task SetSessionUser(HttpContext ctx, RequestDelegate next)
{ {
var headers = ctx.Request.Headers; Title = "InnovEnergy Backend API",
var hasToken = headers.TryGetValue("auth", out var token) ; Version = "v1"
};
if (hasToken)
{
var session = Db.GetSession(token);
if (session is not null)
ctx.Items["Session"] = session;
} }
await next(ctx);
} // var x = new CorsPolicy
} // {
// Headers = { "*" },
// Origins = { "*" },
// Methods = { "*" }
// };

View File

@ -0,0 +1,21 @@
namespace InnovEnergy.App.Backend.S3;
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 { get; } = new S3Cmd
(
key : "EXO44d2979c8e570eae81ead564",
secret: "55MAqyO_FqUmh7O64VIO0egq50ERn_WIAWuc2QC44QU"
);
public static S3Cmd ReadWrite { get; } = new S3Cmd
(
key : "EXO87ca85e29dd412f1238f1cf0",
secret: "-T9TAqy9a3-0-xj7HKsFFJOCcxfRpcnL6OW5oOrOcWU"
);
}

View File

@ -0,0 +1,62 @@
using CliWrap;
using CliWrap.Buffered;
using InnovEnergy.Lib.Utils;
namespace InnovEnergy.App.Backend.S3;
public class S3Cmd
{
private static readonly Command Python = Cli.Wrap("python3");
private const String S3CmdPath = "Resources/s3cmd.py";
private const String S3Prefix = "s3://";
private String[] DefaultArgs { get; }
// ReSharper disable StringLiteralTypo
// ReSharper enable StringLiteralTypo
public S3Cmd(String key, String secret)
{
DefaultArgs = new[]
{
S3CmdPath,
"--access_key", key,
"--secret_key", secret,
};
}
public async Task<String> SignUrl(String bucketName, TimeSpan validity)
{
var result = await Run(bucketName, "signurl", $"+{validity.TotalSeconds}");
return result
.StandardOutput
.Replace("\n", "")
.Replace(" ", "");
}
public async Task<Boolean> CreateBucket(String bucketName)
{
var result = await Run(bucketName, "mb");
return result.ExitCode == 0;
}
public async Task<Boolean> DeleteBucket(String bucketName)
{
var result = await Run(bucketName, "rb");
return result.ExitCode == 0;
}
private Task<BufferedCommandResult> Run(String bucketName, String operation, params String[] optionalArgs)
{
var args = DefaultArgs
.Append(operation)
.Append(bucketName.EnsureStartsWith(S3Prefix))
.Concat(optionalArgs);
return Python
.WithArguments(args)
.ExecuteBufferedAsync();
}
}

Binary file not shown.

View File

@ -33,6 +33,7 @@
<s:Boolean x:Key="/Default/UserDictionary/Words/=proxyport/@EntryIndexedValue">True</s:Boolean> <s:Boolean x:Key="/Default/UserDictionary/Words/=proxyport/@EntryIndexedValue">True</s:Boolean>
<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/=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>