improve backend
This commit is contained in:
parent
c4eb538132
commit
4c37c92f73
|
@ -1,438 +1,283 @@
|
||||||
using System.Net;
|
|
||||||
using System.Text;
|
|
||||||
using System.Web.Http;
|
|
||||||
using InnovEnergy.App.Backend.Database;
|
using InnovEnergy.App.Backend.Database;
|
||||||
using InnovEnergy.App.Backend.Model;
|
using InnovEnergy.App.Backend.DataTypes;
|
||||||
using InnovEnergy.App.Backend.Model.Relations;
|
using InnovEnergy.App.Backend.DataTypes.Methods;
|
||||||
using InnovEnergy.App.Backend.Utils;
|
using InnovEnergy.App.Backend.Relations;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using HttpContextAccessor = Microsoft.AspNetCore.Http.HttpContextAccessor;
|
using static System.Net.HttpStatusCode;
|
||||||
|
using static System.String;
|
||||||
|
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;
|
||||||
|
|
||||||
[ApiController]
|
[ApiController]
|
||||||
[Microsoft.AspNetCore.Mvc.Route("api/")]
|
[Route("api/")]
|
||||||
public class Controller
|
public class Controller
|
||||||
{
|
{
|
||||||
|
private static readonly HttpResponseMessage _Unauthorized = new HttpResponseMessage(Unauthorized);
|
||||||
|
private static readonly HttpResponseMessage _Ok = new HttpResponseMessage(OK);
|
||||||
|
private static readonly HttpResponseMessage _BadRequest = new HttpResponseMessage(BadRequest);
|
||||||
|
|
||||||
[Returns<String>]
|
[Returns<String>]
|
||||||
[Returns(HttpStatusCode.Unauthorized)]
|
[Returns(Unauthorized)]
|
||||||
[Returns(HttpStatusCode.BadRequest)]
|
[Returns(BadRequest)]
|
||||||
[Microsoft.AspNetCore.Mvc.HttpPost($"{nameof(Login)}")]
|
[HttpPost($"{nameof(Login)}")]
|
||||||
public Object Login(Credentials credentials)
|
public Object Login(Credentials credentials)
|
||||||
{
|
{
|
||||||
if (String.IsNullOrWhiteSpace(credentials.Username) ||
|
var session = credentials.Login();
|
||||||
String.IsNullOrWhiteSpace(credentials.Password))
|
|
||||||
return new HttpResponseException(HttpStatusCode.BadRequest);
|
|
||||||
|
|
||||||
using var db = Db.Connect();
|
return session is null
|
||||||
var user = db.GetUserByEmail(credentials.Username);
|
? _Unauthorized
|
||||||
|
: session;
|
||||||
|
|
||||||
if (user is null)
|
|
||||||
return new HttpResponseException(HttpStatusCode.BadRequest);
|
|
||||||
|
|
||||||
if (!VerifyPassword(credentials.Password, user))
|
|
||||||
return new HttpResponseException(HttpStatusCode.Unauthorized);
|
|
||||||
|
|
||||||
var ses = new Session(user);
|
|
||||||
db.NewSession(ses);
|
|
||||||
return new {ses.Token, user.Language};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
[Returns(HttpStatusCode.OK)]
|
[Returns(OK)]
|
||||||
[Returns(HttpStatusCode.Unauthorized)]
|
[Returns(Unauthorized)]
|
||||||
[Microsoft.AspNetCore.Mvc.HttpPost($"{nameof(Logout)}")]
|
[HttpPost($"{nameof(Logout)}")]
|
||||||
public Object Logout()
|
public Object Logout()
|
||||||
{
|
{
|
||||||
var caller = GetCaller();
|
var session = GetSession();
|
||||||
|
|
||||||
if (caller is null)
|
return session.Logout()
|
||||||
return new HttpResponseMessage(HttpStatusCode.Unauthorized);
|
? _Ok
|
||||||
|
: _Unauthorized;
|
||||||
using var db = Db.Connect();
|
|
||||||
return db.DeleteSession(caller.Id);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
[Returns(HttpStatusCode.OK)]
|
|
||||||
[Returns(HttpStatusCode.Unauthorized)]
|
|
||||||
[Microsoft.AspNetCore.Mvc.HttpGet($"{nameof(GetInstallationS3Key)}")]
|
|
||||||
public Object GetInstallationS3Key(Int64 installationId)
|
|
||||||
{
|
|
||||||
var caller = GetCaller();
|
|
||||||
if (caller is null)
|
|
||||||
return new HttpResponseMessage(HttpStatusCode.Unauthorized);
|
|
||||||
|
|
||||||
using var db = Db.Connect();
|
|
||||||
|
|
||||||
var installation = db
|
|
||||||
.GetAllAccessibleInstallations(caller)
|
|
||||||
.FirstOrDefault(i => i.Id == installationId);
|
|
||||||
|
|
||||||
if(installation == null)
|
|
||||||
{
|
|
||||||
return new HttpResponseMessage(HttpStatusCode.Unauthorized);
|
|
||||||
}
|
|
||||||
|
|
||||||
var key = db.GetInstallationS3Key(installationId);
|
|
||||||
return key ?? db.CreateAndSaveInstallationS3ApiKey(installation);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
[Returns<User>]
|
// [Returns<User>]
|
||||||
[Returns(HttpStatusCode.Unauthorized)]
|
// [Returns(HttpStatusCode.Unauthorized)]
|
||||||
[Microsoft.AspNetCore.Mvc.HttpGet($"{nameof(GetUserById)}")]
|
// [HttpGet($"{nameof(GetUserById)}")]
|
||||||
public Object GetUserById(Int64 id)
|
// public Object GetUserById(Int64 id)
|
||||||
{
|
// {
|
||||||
var caller = GetCaller();
|
// var caller = GetCaller();
|
||||||
if (caller is null)
|
// if (caller is null)
|
||||||
return new HttpResponseMessage(HttpStatusCode.Unauthorized);
|
// return new HttpResponseMessage(HttpStatusCode.Unauthorized);
|
||||||
|
//
|
||||||
using var db = Db.Connect();
|
// var user = Db.GetUserById(id);
|
||||||
|
//
|
||||||
|
// if (user is null || !caller.HasAccessTo(user))
|
||||||
|
// return new HttpResponseMessage(HttpStatusCode.Unauthorized);
|
||||||
|
//
|
||||||
|
// return user;
|
||||||
|
// }
|
||||||
|
|
||||||
var user = db
|
//
|
||||||
.GetDescendantUsers(caller)
|
// [Returns<Installation>]
|
||||||
.FirstOrDefault(u => u.Id == id);
|
// [Returns(HttpStatusCode.Unauthorized)]
|
||||||
|
// [HttpGet($"{nameof(GetInstallationById)}")]
|
||||||
return user as Object ?? new HttpResponseMessage(HttpStatusCode.Unauthorized);
|
// public Object GetInstallationById(Int64 id)
|
||||||
}
|
// {
|
||||||
|
// var caller = GetCaller();
|
||||||
|
// if (caller == null)
|
||||||
|
// return new HttpResponseMessage(HttpStatusCode.Unauthorized);
|
||||||
|
//
|
||||||
|
// var installation = Db.GetInstallationById(id);
|
||||||
|
//
|
||||||
|
// if (installation is null || !caller.HasAccessTo(installation))
|
||||||
|
// return new HttpResponseMessage(HttpStatusCode.Unauthorized);
|
||||||
|
//
|
||||||
|
// return installation;
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
[Returns<Installation>]
|
// [Returns<Folder>]
|
||||||
[Returns(HttpStatusCode.Unauthorized)]
|
// [Returns(HttpStatusCode.Unauthorized)]
|
||||||
[Microsoft.AspNetCore.Mvc.HttpGet($"{nameof(GetInstallationById)}")]
|
// [HttpGet($"{nameof(GetFolderById)}")]
|
||||||
public Object GetInstallationById(Int64 id)
|
// public Object GetFolderById(Int64 id)
|
||||||
{
|
// {
|
||||||
var caller = GetCaller();
|
// var caller = GetCaller();
|
||||||
if (caller == null)
|
// if (caller == null)
|
||||||
return new HttpResponseMessage(HttpStatusCode.Unauthorized);
|
// return new HttpResponseMessage(HttpStatusCode.Unauthorized);
|
||||||
|
//
|
||||||
using var db = Db.Connect();
|
// var folder = Db.GetFolderById(id);
|
||||||
|
//
|
||||||
var installation = db
|
// if (folder is null || !caller.HasAccessTo(folder))
|
||||||
.GetAllAccessibleInstallations(caller)
|
// return new HttpResponseMessage(HttpStatusCode.Unauthorized);
|
||||||
.FirstOrDefault(i => i.Id == id);
|
//
|
||||||
|
// return folder;
|
||||||
return installation as Object ?? new HttpResponseMessage(HttpStatusCode.NotFound);
|
// }
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
[Returns<Folder>]
|
|
||||||
[Returns(HttpStatusCode.Unauthorized)]
|
|
||||||
[Microsoft.AspNetCore.Mvc.HttpGet($"{nameof(GetFolderById)}")]
|
|
||||||
public Object GetFolderById(Int64 id)
|
|
||||||
{
|
|
||||||
var caller = GetCaller();
|
|
||||||
if (caller == null)
|
|
||||||
return new HttpResponseMessage(HttpStatusCode.Unauthorized);
|
|
||||||
|
|
||||||
using var db = Db.Connect();
|
|
||||||
|
|
||||||
var folder = db
|
|
||||||
.GetAllAccessibleFolders(caller)
|
|
||||||
.FirstOrDefault(f => f.Id == id);
|
|
||||||
|
|
||||||
return folder as Object ?? new HttpResponseMessage(HttpStatusCode.NotFound);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
[Returns<Installation[]>] // assuming swagger knows about arrays but not lists (JSON)
|
[Returns<Installation[]>] // assuming swagger knows about arrays but not lists (JSON)
|
||||||
[Returns(HttpStatusCode.Unauthorized)]
|
[Returns(Unauthorized)]
|
||||||
[Microsoft.AspNetCore.Mvc.HttpGet($"{nameof(GetAllInstallations)}/")]
|
[HttpGet($"{nameof(GetAllInstallations)}/")]
|
||||||
public Object GetAllInstallations()
|
public Object GetAllInstallations()
|
||||||
{
|
{
|
||||||
var caller = GetCaller();
|
var user = GetSession()?.User;
|
||||||
if (caller == null)
|
|
||||||
return new HttpResponseMessage(HttpStatusCode.Unauthorized);
|
|
||||||
|
|
||||||
using var db = Db.Connect();
|
|
||||||
|
|
||||||
return db
|
return user is null
|
||||||
.GetAllAccessibleInstallations(caller)
|
? _Unauthorized
|
||||||
.ToList(); // important!
|
: user.AccessibleInstallations();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
[Returns<Folder[]>] // assuming swagger knows about arrays but not lists (JSON)
|
[Returns<Folder[]>] // assuming swagger knows about arrays but not lists (JSON)
|
||||||
[Returns(HttpStatusCode.Unauthorized)]
|
[Returns(Unauthorized)]
|
||||||
[Microsoft.AspNetCore.Mvc.HttpGet($"{nameof(GetAllFolders)}/")]
|
[HttpGet($"{nameof(GetAllFolders)}/")]
|
||||||
public Object GetAllFolders()
|
public Object GetAllFolders()
|
||||||
{
|
{
|
||||||
var caller = GetCaller();
|
var user = GetSession()?.User;
|
||||||
if (caller == null)
|
|
||||||
return new HttpResponseMessage(HttpStatusCode.Unauthorized);
|
return user is null
|
||||||
|
? _Unauthorized
|
||||||
using var db = Db.Connect();
|
: user.AccessibleFolders();
|
||||||
return db
|
|
||||||
.GetAllAccessibleFolders(caller)
|
|
||||||
.ToList(); // important!
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Returns<TreeNode[]>] // assuming swagger knows about arrays but not lists (JSON)
|
// [Returns<Folder[]>] // assuming swagger knows about arrays but not lists (JSON)
|
||||||
[Returns(HttpStatusCode.Unauthorized)]
|
// [Returns(Unauthorized)]
|
||||||
[Microsoft.AspNetCore.Mvc.HttpGet($"{nameof(GetTree)}/")]
|
// [HttpGet($"{nameof(GetUsersOfFolder)}/")]
|
||||||
public Object GetTree()
|
// public Object GetUsersOfFolder(Int64 folderId)
|
||||||
{
|
// {
|
||||||
var caller = GetCaller();
|
// var caller = GetCaller();
|
||||||
if (caller == null)
|
// if (caller == null)
|
||||||
return new HttpResponseMessage(HttpStatusCode.Unauthorized);
|
// return new HttpResponseMessage(Unauthorized);
|
||||||
|
//
|
||||||
using var db = Db.Connect();
|
// var folder = Db.GetFolderById(folderId);
|
||||||
|
//
|
||||||
var folders = db
|
// if (folder is null || !caller.HasAccessTo(folder))
|
||||||
.GetDirectlyAccessibleFolders(caller) // ReSharper disable once AccessToDisposedClosure
|
// return new HttpResponseMessage(Unauthorized);
|
||||||
.Select(f => PopulateChildren(db, f));
|
//
|
||||||
|
// return descendantUsers;
|
||||||
var installations = db.GetDirectlyAccessibleInstallations(caller);
|
// }
|
||||||
|
|
||||||
return folders
|
|
||||||
.Concat<TreeNode>(installations)
|
|
||||||
.ToList(); // important!
|
|
||||||
}
|
|
||||||
|
|
||||||
[Returns<TreeNode[]>] // assuming swagger knows about arrays but not lists (JSON)
|
[Returns<TreeNode[]>] // assuming swagger knows about arrays but not lists (JSON)
|
||||||
[Returns(HttpStatusCode.Unauthorized)]
|
[Returns(Unauthorized)]
|
||||||
[Microsoft.AspNetCore.Mvc.HttpGet($"{nameof(GetAllFoldersAndInstallations)}/")]
|
[HttpGet($"{nameof(GetAllFoldersAndInstallations)}/")]
|
||||||
public Object GetAllFoldersAndInstallations()
|
public Object GetAllFoldersAndInstallations()
|
||||||
{
|
{
|
||||||
var caller = GetCaller();
|
var user = GetSession()?.User;
|
||||||
if (caller == null)
|
|
||||||
return new HttpResponseMessage(HttpStatusCode.Unauthorized);
|
|
||||||
|
|
||||||
using var db = Db.Connect();
|
|
||||||
|
|
||||||
var folders = db.GetAllAccessibleFolders(caller) as IEnumerable<TreeNode>;
|
|
||||||
var installations = db.GetAllAccessibleInstallations(caller);
|
|
||||||
|
|
||||||
return folders
|
return user is null
|
||||||
.Concat(installations)
|
? _Unauthorized
|
||||||
.ToList(); // important!
|
: user.AccessibleFoldersAndInstallations();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Folder PopulateChildren(Db db, Folder folder, HashSet<Int64>? hs = null)
|
|
||||||
{
|
|
||||||
// TODO: remove cycle detector
|
|
||||||
hs ??= new HashSet<Int64>();
|
|
||||||
if (!hs.Add(folder.Id))
|
|
||||||
throw new Exception("Cycle detected: folder " + folder.Id);
|
|
||||||
|
|
||||||
var installations = db.GetChildInstallations(folder);
|
|
||||||
var folders = db
|
|
||||||
.GetChildFolders(folder)
|
|
||||||
.Select(c => PopulateChildren(db, c, hs));
|
|
||||||
|
|
||||||
folder.Children = folders.Concat<TreeNode>(installations).ToList();
|
|
||||||
|
|
||||||
return folder;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Returns(HttpStatusCode.OK)]
|
|
||||||
[Returns(HttpStatusCode.Unauthorized)]
|
[Returns(OK)]
|
||||||
[Microsoft.AspNetCore.Mvc.HttpPost($"{nameof(CreateUser)}/")]
|
[Returns(Unauthorized)]
|
||||||
|
[HttpPost($"{nameof(CreateUser)}/")]
|
||||||
public Object CreateUser(User newUser)
|
public Object CreateUser(User newUser)
|
||||||
{
|
{
|
||||||
var caller = GetCaller();
|
var session = GetSession();
|
||||||
using var db = Db.Connect();
|
|
||||||
if (caller == null || !caller.HasWriteAccess)
|
|
||||||
return new HttpResponseMessage(HttpStatusCode.Unauthorized);
|
|
||||||
|
|
||||||
newUser.ParentId = caller.Id;
|
return session.Create(newUser)
|
||||||
|
? newUser
|
||||||
return db.CreateUser(newUser);
|
: _Unauthorized ;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Returns(HttpStatusCode.OK)]
|
[Returns(OK)]
|
||||||
[Returns(HttpStatusCode.Unauthorized)]
|
[Returns(Unauthorized)]
|
||||||
[Microsoft.AspNetCore.Mvc.HttpPost($"{nameof(CreateInstallation)}/")]
|
[HttpPost($"{nameof(CreateInstallation)}/")]
|
||||||
public Object CreateInstallation(Installation installation)
|
public Object CreateInstallation(Installation installation)
|
||||||
{
|
{
|
||||||
var caller = GetCaller();
|
var session = GetSession();
|
||||||
using var db = Db.Connect();
|
|
||||||
if (caller == null || !caller.HasWriteAccess)
|
|
||||||
return new HttpResponseMessage(HttpStatusCode.Unauthorized);
|
|
||||||
|
|
||||||
var id = db.CreateInstallation(installation);
|
|
||||||
|
|
||||||
return db.AddToAccessibleInstallations(caller.Id, id);
|
|
||||||
|
|
||||||
|
return session.Create(installation)
|
||||||
|
? installation
|
||||||
|
: _Unauthorized;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Returns(HttpStatusCode.OK)]
|
[Returns(OK)]
|
||||||
[Returns(HttpStatusCode.Unauthorized)]
|
[Returns(Unauthorized)]
|
||||||
[Microsoft.AspNetCore.Mvc.HttpPost($"{nameof(CreateFolder)}/")]
|
[Returns(InternalServerError)]
|
||||||
|
[HttpPost($"{nameof(CreateFolder)}/")]
|
||||||
public Object CreateFolder(Folder folder)
|
public Object CreateFolder(Folder folder)
|
||||||
{
|
{
|
||||||
var caller = GetCaller();
|
var session = GetSession();
|
||||||
using var db = Db.Connect();
|
|
||||||
if (caller == null || !caller.HasWriteAccess)
|
|
||||||
return new HttpResponseMessage(HttpStatusCode.Unauthorized);
|
|
||||||
|
|
||||||
var id = db.CreateFolder(folder);
|
|
||||||
return db.AddToAccessibleFolders(caller.Id, id);
|
|
||||||
|
|
||||||
|
return session.Create(folder)
|
||||||
|
? folder
|
||||||
|
: _Unauthorized;
|
||||||
}
|
}
|
||||||
|
|
||||||
[Returns(HttpStatusCode.OK)]
|
[Returns(OK)]
|
||||||
[Returns(HttpStatusCode.Unauthorized)]
|
[Returns(Unauthorized)]
|
||||||
[Microsoft.AspNetCore.Mvc.HttpPut($"{nameof(UpdateUser)}/")]
|
[HttpPut($"{nameof(UpdateUser)}/")]
|
||||||
public Object UpdateUser(User updatedUser)
|
public Object UpdateUser(User updatedUser)
|
||||||
{
|
{
|
||||||
var caller = GetCaller();
|
var session = GetSession();
|
||||||
using var db = Db.Connect();
|
|
||||||
if (caller == null || !db.IsParentOfChild(caller.Id, updatedUser) || !caller.HasWriteAccess)
|
return session.Update(updatedUser)
|
||||||
return new HttpResponseMessage(HttpStatusCode.Unauthorized);
|
? updatedUser
|
||||||
|
: _Unauthorized;
|
||||||
return db.UpdateUser(updatedUser);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
[Returns(HttpStatusCode.OK)]
|
[Returns(OK)]
|
||||||
[Returns(HttpStatusCode.Unauthorized)]
|
[Returns(Unauthorized)]
|
||||||
[Microsoft.AspNetCore.Mvc.HttpPut($"{nameof(UpdateInstallation)}/")]
|
[HttpPut($"{nameof(UpdateInstallation)}/")]
|
||||||
public Object UpdateInstallation(Installation installation)
|
public Object UpdateInstallation(Installation installation)
|
||||||
{
|
{
|
||||||
var caller = GetCaller();
|
var session = GetSession();
|
||||||
|
|
||||||
if (caller is null || !caller.HasWriteAccess)
|
return session.Update(installation)
|
||||||
return new HttpResponseMessage(HttpStatusCode.Unauthorized);
|
? installation
|
||||||
|
: _Unauthorized;
|
||||||
using var db = Db.Connect();
|
|
||||||
|
|
||||||
var installationFromAccessibleInstallations = db
|
|
||||||
.GetAllAccessibleInstallations(caller)
|
|
||||||
.FirstOrDefault(i => i.Id == installation.Id);
|
|
||||||
|
|
||||||
if (installationFromAccessibleInstallations == null)
|
|
||||||
return new HttpResponseMessage(HttpStatusCode.Unauthorized);
|
|
||||||
|
|
||||||
// TODO: accessibility by other users etc
|
|
||||||
// TODO: sanity check changes
|
|
||||||
// foreach(var property in installationFromAccessibleInstallations.GetType().GetProperties()){
|
|
||||||
// if(installation.GetType().GetProperties().Contains(property))
|
|
||||||
// {
|
|
||||||
// property.SetValue(installationFromAccessibleInstallations, property.GetValue(installation));
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
return db.UpdateInstallation(installation);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
[Returns(HttpStatusCode.OK)]
|
[Returns(OK)]
|
||||||
[Returns(HttpStatusCode.Unauthorized)]
|
[Returns(Unauthorized)]
|
||||||
[Microsoft.AspNetCore.Mvc.HttpPut($"{nameof(UpdateFolder)}/")]
|
[HttpPut($"{nameof(UpdateFolder)}/")]
|
||||||
public Object UpdateFolder(Folder folder)
|
public Object UpdateFolder(Folder folder)
|
||||||
{
|
{
|
||||||
var caller = GetCaller();
|
var session = GetSession();
|
||||||
|
|
||||||
if (caller is null || !caller.HasWriteAccess)
|
return session.Update(folder)
|
||||||
return new HttpResponseMessage(HttpStatusCode.Unauthorized);
|
? folder
|
||||||
|
: _Unauthorized;
|
||||||
using var db = Db.Connect();
|
|
||||||
|
|
||||||
var installationFromAccessibleFolders = db
|
|
||||||
.GetAllAccessibleFolders(caller)
|
|
||||||
.FirstOrDefault(f => f.Id == folder.Id);
|
|
||||||
|
|
||||||
if (installationFromAccessibleFolders == null)
|
|
||||||
return new HttpResponseMessage(HttpStatusCode.Unauthorized);
|
|
||||||
|
|
||||||
// TODO: accessibility by other users etc
|
|
||||||
// TODO: sanity check changes
|
|
||||||
|
|
||||||
// foreach(var property in installationFromAccessibleFolders.GetType().GetProperties()){
|
|
||||||
// if(folder.GetType().GetProperties().Contains(property))
|
|
||||||
// {
|
|
||||||
// property.SetValue(installationFromAccessibleFolders, property.GetValue(folder));
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
return db.UpdateFolder(installationFromAccessibleFolders);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Returns(HttpStatusCode.OK)]
|
[Returns(OK)]
|
||||||
[Returns(HttpStatusCode.Unauthorized)]
|
[Returns(Unauthorized)]
|
||||||
[Microsoft.AspNetCore.Mvc.HttpDelete($"{nameof(DeleteUser)}/")]
|
[HttpDelete($"{nameof(DeleteUser)}/")]
|
||||||
public Object DeleteUser(Int64 userId)
|
public Object DeleteUser(Int64 userId)
|
||||||
{
|
{
|
||||||
var caller = GetCaller();
|
var session = GetSession();
|
||||||
|
var user = Db.GetUserById(userId);
|
||||||
if (caller is null || !caller.HasWriteAccess)
|
|
||||||
return new HttpResponseMessage(HttpStatusCode.Unauthorized);
|
|
||||||
|
|
||||||
using var db = Db.Connect();
|
|
||||||
|
|
||||||
var userToBeDeleted = db
|
|
||||||
.GetDescendantUsers(caller)
|
|
||||||
.FirstOrDefault(u => u.Id == userId);
|
|
||||||
|
|
||||||
if (userToBeDeleted is null)
|
return session.Delete(user)
|
||||||
return new HttpResponseMessage(HttpStatusCode.Unauthorized);
|
? _Ok
|
||||||
|
: _Unauthorized;
|
||||||
return db.DeleteUser(userToBeDeleted);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Returns(HttpStatusCode.OK)]
|
[Returns(OK)]
|
||||||
[Returns(HttpStatusCode.Unauthorized)]
|
[Returns(Unauthorized)]
|
||||||
[Microsoft.AspNetCore.Mvc.HttpDelete($"{nameof(DeleteInstallation)}/")]
|
[HttpDelete($"{nameof(DeleteInstallation)}/")]
|
||||||
public Object DeleteInstallation(Int64 installationId)
|
public Object DeleteInstallation(Int64 installationId)
|
||||||
{
|
{
|
||||||
var caller = GetCaller();
|
var session = GetSession();
|
||||||
|
var installation = Db.GetInstallationById(installationId);
|
||||||
if (caller is null || !caller.HasWriteAccess)
|
|
||||||
return new HttpResponseMessage(HttpStatusCode.Unauthorized);
|
|
||||||
|
|
||||||
using var db = Db.Connect();
|
|
||||||
|
|
||||||
var installationToBeDeleted = db
|
return session.Delete(installation)
|
||||||
.GetAllAccessibleInstallations(caller)
|
? _Ok
|
||||||
.FirstOrDefault(i => i.Id == installationId);
|
: _Unauthorized;
|
||||||
|
|
||||||
if (installationToBeDeleted is null)
|
|
||||||
return new HttpResponseMessage(HttpStatusCode.Unauthorized);
|
|
||||||
|
|
||||||
return db.DeleteInstallation(installationToBeDeleted);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
[ProducesResponseType(200)]
|
[ProducesResponseType(200)]
|
||||||
[ProducesResponseType(401)]
|
[ProducesResponseType(401)]
|
||||||
[Microsoft.AspNetCore.Mvc.HttpDelete($"{nameof(DeleteFolder)}/")]
|
[HttpDelete($"{nameof(DeleteFolder)}/")]
|
||||||
public Object DeleteFolder(Int64 folderId)
|
public Object DeleteFolder(Int64 folderId)
|
||||||
{
|
{
|
||||||
var caller = GetCaller();
|
var session = GetSession();
|
||||||
if (caller == null)
|
|
||||||
return new HttpResponseMessage(HttpStatusCode.Unauthorized);
|
|
||||||
|
|
||||||
using var db = Db.Connect();
|
|
||||||
|
|
||||||
var folderToDelete = db
|
var folder = Db.GetFolderById(folderId);
|
||||||
.GetAllAccessibleFolders(caller)
|
|
||||||
.FirstOrDefault(f => f.Id == folderId);
|
return session.Delete(folder)
|
||||||
|
? _Ok
|
||||||
|
: _Unauthorized;
|
||||||
|
|
||||||
if (folderToDelete is null)
|
|
||||||
return new HttpResponseMessage(HttpStatusCode.Unauthorized);
|
|
||||||
|
|
||||||
return db.DeleteFolder(folderToDelete);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static User? GetCaller()
|
private static Session? GetSession()
|
||||||
{
|
{
|
||||||
var ctxAccessor = new HttpContextAccessor();
|
var ctxAccessor = new HttpContextAccessor();
|
||||||
return ctxAccessor.HttpContext?.Items["User"] as User;
|
return ctxAccessor.HttpContext?.Items["Session"] as Session;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Boolean VerifyPassword(String password, User user)
|
|
||||||
{
|
|
||||||
var pwdBytes = Encoding.UTF8.GetBytes(password);
|
|
||||||
var saltBytes = Encoding.UTF8.GetBytes(user.Salt + "innovEnergy");
|
|
||||||
var pwdHash = Crypto.ComputeHash(pwdBytes, saltBytes);
|
|
||||||
|
|
||||||
return user.Password == pwdHash;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1,6 +0,0 @@
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
|
|
||||||
namespace InnovEnergy.App.Backend.Controllers;
|
|
||||||
|
|
||||||
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]
|
|
||||||
public record Credentials(String Username, String Password);
|
|
|
@ -0,0 +1,6 @@
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
|
||||||
|
namespace InnovEnergy.App.Backend.DataTypes;
|
||||||
|
|
||||||
|
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]
|
||||||
|
public record Credentials(String Username, String Password);
|
|
@ -0,0 +1,3 @@
|
||||||
|
namespace InnovEnergy.App.Backend.DataTypes;
|
||||||
|
|
||||||
|
public class Folder : TreeNode {}
|
|
@ -1,4 +1,4 @@
|
||||||
namespace InnovEnergy.App.Backend.Model;
|
namespace InnovEnergy.App.Backend.DataTypes;
|
||||||
|
|
||||||
|
|
||||||
public class Installation : TreeNode
|
public class Installation : TreeNode
|
||||||
|
@ -14,7 +14,6 @@ public class Installation : TreeNode
|
||||||
public Double Long { get; set; }
|
public Double Long { get; set; }
|
||||||
|
|
||||||
public String S3Bucket { get; set; } = "";
|
public String S3Bucket { get; set; } = "";
|
||||||
public String? S3Key { get; set; }
|
public String S3Key { get; set; } = "";
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
if (credentials.Username.IsNull() || credentials.Password.IsNull())
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var user = Db.GetUserByEmail(credentials.Username);
|
||||||
|
|
||||||
|
if (user is null || !user.VerifyPassword(credentials.Password))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var session = new Session(user);
|
||||||
|
|
||||||
|
return Db.Create(session)
|
||||||
|
? session
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,73 @@
|
||||||
|
using InnovEnergy.App.Backend.Database;
|
||||||
|
using InnovEnergy.Lib.Utils;
|
||||||
|
|
||||||
|
namespace InnovEnergy.App.Backend.DataTypes.Methods;
|
||||||
|
|
||||||
|
public static class FolderMethods
|
||||||
|
{
|
||||||
|
public static IEnumerable<Folder> ChildFolders(this Folder parent)
|
||||||
|
{
|
||||||
|
return Db
|
||||||
|
.Folders
|
||||||
|
.Where(f => f.ParentId == parent.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<Installation> ChildInstallations(this Folder parent)
|
||||||
|
{
|
||||||
|
return Db
|
||||||
|
.Installations
|
||||||
|
.Where(f => f.ParentId == parent.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<Folder> DescendantFolders(this Folder parent)
|
||||||
|
{
|
||||||
|
return parent.Traverse(ChildFolders);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean IsDescendantOf(this Folder folder, Folder ancestor)
|
||||||
|
{
|
||||||
|
return folder
|
||||||
|
.Ancestors()
|
||||||
|
.Any(u => u.Id == ancestor.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<Folder> Ancestors(this Folder folder)
|
||||||
|
{
|
||||||
|
return folder.Unfold(Parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Folder? Parent(this Folder folder)
|
||||||
|
{
|
||||||
|
return IsAbsoluteRoot(folder)
|
||||||
|
? null
|
||||||
|
: Db.GetFolderById(folder.ParentId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean IsAbsoluteRoot(this Folder folder)
|
||||||
|
{
|
||||||
|
return folder.ParentId == 0; // root has ParentId 0 by definition
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean IsRelativeRoot(this Folder folder)
|
||||||
|
{
|
||||||
|
return folder.ParentId < 0; // root has ParentId 0 by definition
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean WasMoved(this Folder folder)
|
||||||
|
{
|
||||||
|
if (folder.IsRelativeRoot())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var existingFolder = Db.GetFolderById(folder.Id);
|
||||||
|
|
||||||
|
return existingFolder is not null
|
||||||
|
&& existingFolder.ParentId != folder.ParentId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean Exists(this Folder folder)
|
||||||
|
{
|
||||||
|
return Db.Folders.Any(f => f.Id == folder.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,47 @@
|
||||||
|
using InnovEnergy.App.Backend.Database;
|
||||||
|
|
||||||
|
namespace InnovEnergy.App.Backend.DataTypes.Methods;
|
||||||
|
|
||||||
|
|
||||||
|
public static class InstallationMethods
|
||||||
|
{
|
||||||
|
public static IEnumerable<Folder> Ancestors(this Installation installation)
|
||||||
|
{
|
||||||
|
var parentFolder = Parent(installation);
|
||||||
|
|
||||||
|
return parentFolder is null
|
||||||
|
? Enumerable.Empty<Folder>()
|
||||||
|
: parentFolder.Ancestors();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Folder? Parent(this Installation installation)
|
||||||
|
{
|
||||||
|
return installation.IsRelativeRoot()
|
||||||
|
? null
|
||||||
|
: Db.GetFolderById(installation.ParentId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean IsRelativeRoot(this Installation i)
|
||||||
|
{
|
||||||
|
return i.ParentId < 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean WasMoved(this Installation installation)
|
||||||
|
{
|
||||||
|
if (installation.IsRelativeRoot())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
var existingInstallation = Db.GetInstallationById(installation.Id);
|
||||||
|
|
||||||
|
return existingInstallation is not null
|
||||||
|
&& existingInstallation.ParentId != installation.ParentId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean Exists(this Installation installation)
|
||||||
|
{
|
||||||
|
return Db.Installations.Any(i => i.Id == installation.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,119 @@
|
||||||
|
using InnovEnergy.App.Backend.Database;
|
||||||
|
using InnovEnergy.App.Backend.Relations;
|
||||||
|
|
||||||
|
namespace InnovEnergy.App.Backend.DataTypes.Methods;
|
||||||
|
|
||||||
|
public static class SessionMethods
|
||||||
|
{
|
||||||
|
public static Boolean Create(this Session? session, Folder? folder)
|
||||||
|
{
|
||||||
|
var user = session?.User;
|
||||||
|
|
||||||
|
return user is not null
|
||||||
|
&& folder is not null
|
||||||
|
&& user.HasWriteAccess
|
||||||
|
&& user.HasAccessTo(folder.Parent())
|
||||||
|
&& Db.Create(folder);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean Update(this Session? session, Folder? folder)
|
||||||
|
{
|
||||||
|
var user = session?.User;
|
||||||
|
|
||||||
|
return user is not null
|
||||||
|
&& folder is not null
|
||||||
|
&& user.HasWriteAccess
|
||||||
|
&& user.HasAccessTo(folder)
|
||||||
|
&& (folder.IsRelativeRoot() || user.HasAccessTo(folder.Parent()))
|
||||||
|
&& Db.Update(folder);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean Delete(this Session? session, Folder? folder)
|
||||||
|
{
|
||||||
|
var user = session?.User;
|
||||||
|
|
||||||
|
return user is not null
|
||||||
|
&& folder is not null
|
||||||
|
&& user.HasWriteAccess
|
||||||
|
&& user.HasAccessTo(folder) // TODO: && user.HasAccessTo(folder.Parent()) ???
|
||||||
|
&& Db.Delete(folder);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static Boolean Create(this Session? session, Installation? installation)
|
||||||
|
{
|
||||||
|
var user = session?.User;
|
||||||
|
|
||||||
|
return user is not null
|
||||||
|
&& installation is not null
|
||||||
|
&& user.HasWriteAccess
|
||||||
|
&& user.HasAccessTo(installation.Parent())
|
||||||
|
&& Db.Create(installation);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean Update(this Session? session, Installation? installation)
|
||||||
|
{
|
||||||
|
var user = session?.User;
|
||||||
|
|
||||||
|
return user is not null
|
||||||
|
&& installation is not null
|
||||||
|
&& user.HasWriteAccess
|
||||||
|
&& installation.Exists()
|
||||||
|
&& user.HasAccessTo(installation)
|
||||||
|
&& (installation.IsRelativeRoot() || user.HasAccessTo(installation.Parent())) // TODO: triple check this
|
||||||
|
&& Db.Update(installation);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean Delete(this Session? session, Installation? installation)
|
||||||
|
{
|
||||||
|
var user = session?.User;
|
||||||
|
|
||||||
|
return user is not null
|
||||||
|
&& installation is not null
|
||||||
|
&& user.HasWriteAccess
|
||||||
|
&& user.HasAccessTo(installation) // TODO: && user.HasAccessTo(installation.Parent()) ???
|
||||||
|
&& Db.Delete(installation);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean Create(this Session? session, User? newUser)
|
||||||
|
{
|
||||||
|
var sessionUser = session?.User;
|
||||||
|
|
||||||
|
if (sessionUser is null || newUser is null || !sessionUser.HasWriteAccess)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
newUser.ParentId = sessionUser.Id; // Important!
|
||||||
|
|
||||||
|
return Db.Create(newUser);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean Update(this Session? session, User? editedUser)
|
||||||
|
{
|
||||||
|
var sessionUser = session?.User;
|
||||||
|
|
||||||
|
return sessionUser is not null
|
||||||
|
&& editedUser is not null
|
||||||
|
&& sessionUser.HasWriteAccess
|
||||||
|
&& sessionUser.HasAccessTo(editedUser)
|
||||||
|
&& (editedUser.IsRelativeRoot() || sessionUser.HasAccessTo(editedUser.Parent())) // TODO: triple check this
|
||||||
|
&& Db.Update(editedUser);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean Delete(this Session? session, User? userToDelete)
|
||||||
|
{
|
||||||
|
var sessionUser = session?.User;
|
||||||
|
|
||||||
|
return sessionUser is not null
|
||||||
|
&& userToDelete is not null
|
||||||
|
&& sessionUser.HasWriteAccess
|
||||||
|
&& sessionUser.HasAccessTo(userToDelete) // TODO: && user.HasAccessTo(installation.Parent()) ???
|
||||||
|
&& Db.Delete(userToDelete);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean Logout(this Session? session)
|
||||||
|
{
|
||||||
|
return session is not null
|
||||||
|
&& Db.Sessions.Delete(s => s.Token == session.Token) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,342 @@
|
||||||
|
using System.Net.Http.Headers;
|
||||||
|
using System.Net.Mail;
|
||||||
|
using System.Security.Cryptography;
|
||||||
|
using System.Text.Json.Nodes;
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
using InnovEnergy.App.Backend.Database;
|
||||||
|
using InnovEnergy.Lib.Utils;
|
||||||
|
using Convert = System.Convert;
|
||||||
|
using static System.Text.Encoding;
|
||||||
|
|
||||||
|
|
||||||
|
namespace InnovEnergy.App.Backend.DataTypes.Methods;
|
||||||
|
|
||||||
|
|
||||||
|
public static class UserMethods
|
||||||
|
{
|
||||||
|
public static IEnumerable<Installation> AccessibleInstallations(this User user)
|
||||||
|
{
|
||||||
|
var direct = user.DirectlyAccessibleInstallations();
|
||||||
|
var fromFolders = user
|
||||||
|
.AccessibleFolders()
|
||||||
|
.SelectMany(u => u.ChildInstallations());
|
||||||
|
|
||||||
|
return direct
|
||||||
|
.Concat(fromFolders)
|
||||||
|
.Distinct();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<Folder> AccessibleFolders(this User user)
|
||||||
|
{
|
||||||
|
return user
|
||||||
|
.DirectlyAccessibleFolders()
|
||||||
|
.SelectMany(f => f.DescendantFolders())
|
||||||
|
.Distinct();
|
||||||
|
|
||||||
|
// Distinct because the user might have direct access
|
||||||
|
// to a child folder of a folder he has already access to
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<TreeNode> AccessibleFoldersAndInstallations(this User user)
|
||||||
|
{
|
||||||
|
var folders = user.AccessibleFolders() as IEnumerable<TreeNode>;
|
||||||
|
var installations = user.AccessibleInstallations();
|
||||||
|
|
||||||
|
return folders.Concat(installations);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<Installation> DirectlyAccessibleInstallations(this User user)
|
||||||
|
{
|
||||||
|
return Db
|
||||||
|
.User2Installation
|
||||||
|
.Where(r => r.UserId == user.Id)
|
||||||
|
.Select(r => r.InstallationId)
|
||||||
|
.Select(Db.GetInstallationById)
|
||||||
|
.NotNull()
|
||||||
|
.Do(i => i.ParentId = -1); // hide inaccessible parents from calling user
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<Folder> DirectlyAccessibleFolders(this User user)
|
||||||
|
{
|
||||||
|
return Db
|
||||||
|
.User2Folder
|
||||||
|
.Where(r => r.UserId == user.Id)
|
||||||
|
.Select(r => r.FolderId)
|
||||||
|
.Select(Db.GetFolderById)
|
||||||
|
.NotNull()
|
||||||
|
.Do(i => i.ParentId = -1); // hide inaccessible parents from calling user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<User> ChildUsers(this User parent)
|
||||||
|
{
|
||||||
|
return Db
|
||||||
|
.Users
|
||||||
|
.Where(f => f.ParentId == parent.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<User> DescendantUsers(this User parent)
|
||||||
|
{
|
||||||
|
return parent.Traverse(ChildUsers);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean IsDescendantOf(this User user, User ancestor)
|
||||||
|
{
|
||||||
|
return user
|
||||||
|
.Ancestors()
|
||||||
|
.Any(u => u.Id == ancestor.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IEnumerable<User> Ancestors(this User user)
|
||||||
|
{
|
||||||
|
return user.Unfold(Parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean VerifyPassword(this User user, String password)
|
||||||
|
{
|
||||||
|
return user.Password == user.SaltAndHashPassword(password);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String SaltAndHashPassword(this User user, String password)
|
||||||
|
{
|
||||||
|
var dataToHash = $"{password}{user.Salt()}";
|
||||||
|
|
||||||
|
return dataToHash
|
||||||
|
.Apply(UTF8.GetBytes)
|
||||||
|
.Apply(SHA256.HashData)
|
||||||
|
.Apply(Convert.ToBase64String);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static User? Parent(this User u)
|
||||||
|
{
|
||||||
|
return u.IsAbsoluteRoot()
|
||||||
|
? null
|
||||||
|
: Db.GetUserById(u.ParentId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean IsAbsoluteRoot(this User u)
|
||||||
|
{
|
||||||
|
return u.ParentId == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean IsRelativeRoot(this User u)
|
||||||
|
{
|
||||||
|
return u.ParentId < 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean HasDirectAccessTo(this User user, Folder folder)
|
||||||
|
{
|
||||||
|
return Db
|
||||||
|
.User2Folder
|
||||||
|
.Any(r => r.FolderId == folder.Id && r.UserId == user.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean HasAccessTo(this User user, Folder? folder)
|
||||||
|
{
|
||||||
|
if (folder is null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return folder
|
||||||
|
.Ancestors()
|
||||||
|
.Any(user.HasDirectAccessTo);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean HasDirectAccessTo(this User user, Installation installation)
|
||||||
|
{
|
||||||
|
return Db
|
||||||
|
.User2Installation
|
||||||
|
.Any(r => r.InstallationId == installation.Id && r.UserId == user.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean HasAccessTo(this User user, Installation? installation)
|
||||||
|
{
|
||||||
|
if (installation is null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return user.HasDirectAccessTo(installation) ||
|
||||||
|
installation.Ancestors().Any(user.HasDirectAccessTo);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean HasAccessTo(this User user, User? other)
|
||||||
|
{
|
||||||
|
if (other is null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return other
|
||||||
|
.Ancestors()
|
||||||
|
.Skip(1) // Important! skip self, user cannot delete or edit himself
|
||||||
|
.Contains(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String Salt(this User user)
|
||||||
|
{
|
||||||
|
// + id => salt unique per user
|
||||||
|
// + InnovEnergy => globally unique
|
||||||
|
|
||||||
|
return $"{user.Id}InnovEnergy";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static Byte[] HmacSha256Digest(String message, String secret)
|
||||||
|
{
|
||||||
|
// var encoding = new UTF8Encoding();
|
||||||
|
// var keyBytes = encoding.GetBytes(secret);
|
||||||
|
// var messageBytes = encoding.GetBytes(message);
|
||||||
|
// var cryptographer = new HMACSHA256(keyBytes);
|
||||||
|
// return cryptographer.ComputeHash(messageBytes);
|
||||||
|
|
||||||
|
var keyBytes = UTF8.GetBytes(secret);
|
||||||
|
var messageBytes = UTF8.GetBytes(message);
|
||||||
|
|
||||||
|
return HMACSHA256.HashData(keyBytes, messageBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String BuildSignature(String method, String path, String data, Int64 time, String secret)
|
||||||
|
{
|
||||||
|
var messageToSign = "";
|
||||||
|
messageToSign += method + " /v2/" + path + "\n";
|
||||||
|
messageToSign += data + "\n";
|
||||||
|
|
||||||
|
// query strings
|
||||||
|
messageToSign += "\n";
|
||||||
|
// headers
|
||||||
|
messageToSign += "\n";
|
||||||
|
|
||||||
|
messageToSign += time;
|
||||||
|
|
||||||
|
Console.WriteLine("Message to sign:\n" + messageToSign);
|
||||||
|
|
||||||
|
var hmac = HmacSha256Digest(messageToSign, secret);
|
||||||
|
return Convert.ToBase64String(hmac);
|
||||||
|
}
|
||||||
|
|
||||||
|
// public Object CreateAndSaveUserS3ApiKey(User user)
|
||||||
|
// {
|
||||||
|
// //EXOSCALE API URL
|
||||||
|
// const String url = "https://api-ch-dk-2.exoscale.com/v2/";
|
||||||
|
// const String path = "access-key";
|
||||||
|
//
|
||||||
|
// //TODO HIDE ME
|
||||||
|
// const String secret = "S2K1okphiCSNK4mzqr4swguFzngWAMb1OoSlZsJa9F0";
|
||||||
|
// const String apiKey = "EXOb98ec9008e3ec16e19d7b593";
|
||||||
|
//
|
||||||
|
// var installationList = User2Installation
|
||||||
|
// .Where(i => i.UserId == user.Id)
|
||||||
|
// .SelectMany(i => Installations.Where(f => i.InstallationId == f.Id))
|
||||||
|
// .ToList();
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// var instList = new JsonArray();
|
||||||
|
//
|
||||||
|
// foreach (var installation in installationList)
|
||||||
|
// {
|
||||||
|
// instList.Add(new JsonObject {["domain"] = "sos",["resource-name"] = installation.Name,["resource-type"] = "bucket"});
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// var jsonPayload = new JsonObject { ["name"] = user.Email, ["operations"] = new JsonArray{ "list-sos-bucket", "get-sos-object" }, ["content"] = instList};
|
||||||
|
// var stringPayload = jsonPayload.ToJsonString();
|
||||||
|
//
|
||||||
|
// var unixExpiration = DateTimeOffset.UtcNow.ToUnixTimeSeconds()+60;
|
||||||
|
// var signature = BuildSignature("POST", path, stringPayload, unixExpiration , secret);
|
||||||
|
//
|
||||||
|
// var authHeader = "credential="+apiKey+",expires="+unixExpiration+",signature="+signature;
|
||||||
|
//
|
||||||
|
// var client = new HttpClient();
|
||||||
|
// client.DefaultRequestHeaders.Authorization =
|
||||||
|
// new AuthenticationHeaderValue("EXO2-HMAC-SHA256", authHeader);
|
||||||
|
//
|
||||||
|
// var content = new StringContent(stringPayload, Encoding.UTF8, "application/json");
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// var response = client.PostAsync(url+path, content).Result;
|
||||||
|
//
|
||||||
|
// if (response.StatusCode.ToString() != "OK")
|
||||||
|
// {
|
||||||
|
// return response;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// var responseString = response.Content.ReadAsStringAsync().Result;
|
||||||
|
// return Enumerable.Last(Regex.Match(responseString, "key\\\":\\\"([A-Z])\\w+").ToString().Split('"'));
|
||||||
|
// // return SetUserS3ApiKey(user, newKey);
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
|
||||||
|
public static Object CreateAndSaveInstallationS3ApiKey(Installation installation)
|
||||||
|
{
|
||||||
|
//EXOSCALE API URL
|
||||||
|
const String url = "https://api-ch-dk-2.exoscale.com/v2/";
|
||||||
|
const String path = "access-key";
|
||||||
|
|
||||||
|
//TODO HIDE ME
|
||||||
|
const String secret = "S2K1okphiCSNK4mzqr4swguFzngWAMb1OoSlZsJa9F0";
|
||||||
|
const String apiKey = "EXOb98ec9008e3ec16e19d7b593";
|
||||||
|
|
||||||
|
|
||||||
|
var jsonPayload = new JsonObject
|
||||||
|
{
|
||||||
|
["name"] = installation.Id,
|
||||||
|
["operations"] = new JsonArray
|
||||||
|
{
|
||||||
|
"list-sos-bucket",
|
||||||
|
"get-sos-object"
|
||||||
|
},
|
||||||
|
["content"] = new JsonArray
|
||||||
|
{
|
||||||
|
new JsonObject
|
||||||
|
{
|
||||||
|
["domain"] = "sos",
|
||||||
|
["resource-name"] = installation.Name,
|
||||||
|
["resource-type"] = "bucket"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var stringPayload = jsonPayload.ToJsonString();
|
||||||
|
|
||||||
|
var unixExpiration = DateTimeOffset.UtcNow.ToUnixTimeSeconds() + 60;
|
||||||
|
|
||||||
|
var signature = BuildSignature("POST", path, stringPayload, unixExpiration, secret);
|
||||||
|
|
||||||
|
var authHeader = "credential=" + apiKey + ",expires=" + unixExpiration + ",signature=" + signature;
|
||||||
|
|
||||||
|
var client = new HttpClient();
|
||||||
|
|
||||||
|
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("EXO2-HMAC-SHA256", authHeader);
|
||||||
|
|
||||||
|
var content = new StringContent(stringPayload, UTF8, "application/json");
|
||||||
|
|
||||||
|
var response = client.PostAsync(url + path, content).Result;
|
||||||
|
|
||||||
|
if (response.StatusCode.ToString() != "OK")
|
||||||
|
{
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
var responseString = response.Content.ReadAsStringAsync().Result;
|
||||||
|
var newKey = Regex
|
||||||
|
.Match(responseString, "key\\\":\\\"([A-Z])\\w+")
|
||||||
|
.ToString()
|
||||||
|
.Split('"')
|
||||||
|
.Last();
|
||||||
|
|
||||||
|
installation.S3Key = newKey;
|
||||||
|
Db.Update(installation);
|
||||||
|
return newKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
private static Boolean IsValidEmail(String email)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var emailAddress = new MailAddress(email);
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,23 @@
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
|
||||||
|
namespace InnovEnergy.App.Backend.DataTypes;
|
||||||
|
|
||||||
|
public abstract partial class TreeNode
|
||||||
|
{
|
||||||
|
public override Boolean Equals(Object? obj)
|
||||||
|
{
|
||||||
|
if (ReferenceEquals(null, obj)) return false;
|
||||||
|
if (ReferenceEquals(this, obj)) return true;
|
||||||
|
if (obj.GetType() != GetType()) return false;
|
||||||
|
|
||||||
|
return Equals((TreeNode)obj);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Boolean Equals(TreeNode other) => Id == other.Id;
|
||||||
|
|
||||||
|
[SuppressMessage("ReSharper", "NonReadonlyMemberInGetHashCode")]
|
||||||
|
public override Int32 GetHashCode() => Id.GetHashCode();
|
||||||
|
|
||||||
|
public static Boolean operator ==(TreeNode? left, TreeNode? right) => Equals(left, right);
|
||||||
|
public static Boolean operator !=(TreeNode? left, TreeNode? right) => !Equals(left, right);
|
||||||
|
}
|
|
@ -1,6 +1,6 @@
|
||||||
using SQLite;
|
using SQLite;
|
||||||
|
|
||||||
namespace InnovEnergy.App.Backend.Model;
|
namespace InnovEnergy.App.Backend.DataTypes;
|
||||||
|
|
||||||
public abstract partial class TreeNode
|
public abstract partial class TreeNode
|
||||||
{
|
{
|
||||||
|
@ -12,10 +12,7 @@ public abstract partial class TreeNode
|
||||||
[Indexed] // parent/child relation
|
[Indexed] // parent/child relation
|
||||||
public Int64 ParentId { get; set; }
|
public Int64 ParentId { get; set; }
|
||||||
|
|
||||||
[Ignore] // not in DB, can be used in typescript as type discriminator
|
[Ignore]
|
||||||
public String Type => GetType().Name;
|
public String Type => GetType().Name;
|
||||||
|
|
||||||
[Ignore]
|
|
||||||
public IReadOnlyList<TreeNode>? Children { get; set; }
|
|
||||||
|
|
||||||
}
|
}
|
|
@ -1,17 +1,14 @@
|
||||||
using SQLite;
|
using SQLite;
|
||||||
|
|
||||||
namespace InnovEnergy.App.Backend.Model;
|
namespace InnovEnergy.App.Backend.DataTypes;
|
||||||
|
|
||||||
public class User : TreeNode
|
public class User : TreeNode
|
||||||
{
|
{
|
||||||
[Indexed]
|
[Indexed]
|
||||||
public String Email { get; set; } = null!;
|
public String Email { get; set; } = null!;
|
||||||
public Boolean HasWriteAccess { get; set; } = false;
|
public Boolean HasWriteAccess { get; set; } = false;
|
||||||
public String Salt { get; set; } = null!;
|
|
||||||
public String Language { get; set; } = null!;
|
public String Language { get; set; } = null!;
|
||||||
public String Password { get; set; } = null!;
|
public String Password { get; set; } = null!;
|
||||||
|
|
||||||
// TODO: must reset pwd
|
// TODO: must reset pwd
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
using InnovEnergy.App.Backend.DataTypes;
|
||||||
|
using InnovEnergy.App.Backend.DataTypes.Methods;
|
||||||
|
using InnovEnergy.App.Backend.Relations;
|
||||||
|
|
||||||
|
|
||||||
|
namespace InnovEnergy.App.Backend.Database;
|
||||||
|
|
||||||
|
|
||||||
|
public static partial class Db
|
||||||
|
{
|
||||||
|
public static Boolean Create(Installation installation)
|
||||||
|
{
|
||||||
|
// SQLite wrapper is smart and *modifies* t's Id to the one generated (autoincrement) by the insertion
|
||||||
|
return Connection.Insert(installation) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean Create(Folder folder)
|
||||||
|
{
|
||||||
|
return Connection.Insert(folder) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean Create(User user)
|
||||||
|
{
|
||||||
|
if (GetUserByEmail(user.Email) is not null) // TODO: User unique by username instead of email?
|
||||||
|
return false;
|
||||||
|
|
||||||
|
user.Password = user.SaltAndHashPassword(user.Password);
|
||||||
|
|
||||||
|
return Connection.Insert(user) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static Boolean Create(Session session)
|
||||||
|
{
|
||||||
|
return Connection.Insert(session) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,16 +1,20 @@
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Reactive.Linq;
|
||||||
using InnovEnergy.App.Backend.Model;
|
using InnovEnergy.App.Backend.DataTypes;
|
||||||
using InnovEnergy.App.Backend.Model.Relations;
|
using InnovEnergy.App.Backend.DataTypes.Methods;
|
||||||
|
using InnovEnergy.App.Backend.Relations;
|
||||||
using InnovEnergy.Lib.Utils;
|
using InnovEnergy.Lib.Utils;
|
||||||
using SQLite;
|
using SQLite;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
namespace InnovEnergy.App.Backend.Database;
|
namespace InnovEnergy.App.Backend.Database;
|
||||||
|
|
||||||
|
|
||||||
public static partial class Db
|
public static partial class Db
|
||||||
{
|
{
|
||||||
internal const String DbPath = "./db.sqlite";
|
internal const String DbPath = "./db.sqlite";
|
||||||
|
|
||||||
public static SQLiteConnection Connection { get; } = new SQLiteConnection(DbPath);
|
private static SQLiteConnection Connection { get; } = new SQLiteConnection(DbPath);
|
||||||
|
|
||||||
public static TableQuery<Session> Sessions => Connection.Table<Session>();
|
public static TableQuery<Session> Sessions => Connection.Table<Session>();
|
||||||
public static TableQuery<Folder> Folders => Connection.Table<Folder>();
|
public static TableQuery<Folder> Folders => Connection.Table<Folder>();
|
||||||
|
@ -18,149 +22,55 @@ public static partial class Db
|
||||||
public static TableQuery<User> Users => Connection.Table<User>();
|
public static TableQuery<User> Users => Connection.Table<User>();
|
||||||
public static TableQuery<User2Folder> User2Folder => Connection.Table<User2Folder>();
|
public static TableQuery<User2Folder> User2Folder => Connection.Table<User2Folder>();
|
||||||
public static TableQuery<User2Installation> User2Installation => Connection.Table<User2Installation>();
|
public static TableQuery<User2Installation> User2Installation => Connection.Table<User2Installation>();
|
||||||
|
|
||||||
public static Int32 NbUser2Installation => User2Installation.Count();
|
|
||||||
public static Int32 NbUser2Folder => User2Folder.Count();
|
|
||||||
public static Int32 NbFolders => Folders.Count();
|
|
||||||
public static Int32 NbInstallations => Installations.Count();
|
|
||||||
public static Int32 NbUsers => Users.Count();
|
|
||||||
|
|
||||||
|
|
||||||
public static Folder? GetFolderById(Int64 id)
|
|
||||||
{
|
|
||||||
return Folders
|
|
||||||
.FirstOrDefault(f => f.Id == id);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Installation? GetInstallationById(Int64 id)
|
|
||||||
{
|
|
||||||
return Installations
|
|
||||||
.FirstOrDefault(i => i.Id == id);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static User? GetUserById(Int64 id)
|
|
||||||
{
|
|
||||||
return Users
|
|
||||||
.FirstOrDefault(u => u.Id == id);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
[SuppressMessage("ReSharper", "AccessToDisposedClosure")]
|
|
||||||
static Db()
|
static Db()
|
||||||
{
|
{
|
||||||
// on startup create/migrate tables
|
// on startup create/migrate tables
|
||||||
|
|
||||||
using var db = new SQLiteConnection(DbPath);
|
Connection.RunInTransaction(() =>
|
||||||
|
|
||||||
db.RunInTransaction(() =>
|
|
||||||
{
|
{
|
||||||
db.CreateTable<User>();
|
Connection.CreateTable<User>();
|
||||||
db.CreateTable<Installation>();
|
Connection.CreateTable<Installation>();
|
||||||
db.CreateTable<Folder>();
|
Connection.CreateTable<Folder>();
|
||||||
db.CreateTable<User2Folder>();
|
Connection.CreateTable<User2Folder>();
|
||||||
db.CreateTable<User2Installation>();
|
Connection.CreateTable<User2Installation>();
|
||||||
db.CreateTable<Session>();
|
Connection.CreateTable<Session>();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
var installation = Installations.First();
|
||||||
|
UserMethods.CreateAndSaveInstallationS3ApiKey(installation);
|
||||||
|
|
||||||
|
|
||||||
|
Observable.Interval(TimeSpan.FromDays(1))
|
||||||
|
.StartWith(0) // Do it right away (on startup)
|
||||||
|
.Subscribe(Cleanup); // and then daily
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// the C in CRUD
|
|
||||||
private static Int64 Create(TreeNode treeNode)
|
private static Boolean RunTransaction(Func<Boolean> func)
|
||||||
{
|
{
|
||||||
|
var savepoint = Connection.SaveTransactionPoint();
|
||||||
|
var success = false;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Connection.Insert(treeNode);
|
success = func();
|
||||||
return SQLite3.LastInsertRowid(Connection.Handle);
|
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
finally
|
||||||
{
|
{
|
||||||
return -1;
|
if (success)
|
||||||
|
Connection.Release(savepoint);
|
||||||
|
else
|
||||||
|
Connection.RollbackTo(savepoint);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private static Boolean Create(Session session)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Connection.Insert(session);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// the U in CRUD
|
|
||||||
private static Boolean Update(TreeNode treeNode)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Connection.InsertOrReplace(treeNode);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// the D in CRUD
|
|
||||||
private static Boolean Delete(TreeNode treeNode)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
Connection.Delete(treeNode);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IEnumerable<Installation> GetAllAccessibleInstallations(User user)
|
|
||||||
{
|
|
||||||
var direct = GetDirectlyAccessibleInstallations(user);
|
|
||||||
var fromFolders = GetAllAccessibleFolders(user)
|
|
||||||
.SelectMany(GetChildInstallations);
|
|
||||||
|
|
||||||
return direct
|
|
||||||
.Concat(fromFolders)
|
|
||||||
.Distinct();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IEnumerable<Folder> GetAllAccessibleFolders(User user)
|
|
||||||
{
|
|
||||||
return GetDirectlyAccessibleFolders(user)
|
|
||||||
.SelectMany(GetDescendantFolders)
|
|
||||||
.Distinct();
|
|
||||||
|
|
||||||
// Distinct because the user might have direct access
|
|
||||||
// to a child folder of a folder he has already access to
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public static IEnumerable<Installation> GetDirectlyAccessibleInstallations(User user)
|
|
||||||
{
|
|
||||||
return User2Installation
|
|
||||||
.Where(r => r.UserId == user.Id)
|
|
||||||
.Select(r => r.InstallationId)
|
|
||||||
.Select(GetInstallationById)
|
|
||||||
.NotNull()
|
|
||||||
.Do(i => i.ParentId = 0); // hide inaccessible parents from calling user
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IEnumerable<Folder> GetDirectlyAccessibleFolders(User user)
|
|
||||||
{
|
|
||||||
return User2Folder
|
|
||||||
.Where(r => r.UserId == user.Id)
|
|
||||||
.Select(r => r.FolderId)
|
|
||||||
.Select(GetFolderById)
|
|
||||||
.NotNull()
|
|
||||||
.Do(i => i.ParentId = 0); // hide inaccessible parents from calling user;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Boolean AddToAccessibleInstallations(Int64 userId, Int64 updatedInstallationId)
|
public static Boolean AddToAccessibleInstallations(Int64 userId, Int64 updatedInstallationId)
|
||||||
{
|
{
|
||||||
|
@ -201,45 +111,22 @@ public static partial class Db
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static User? GetUserByToken(String token)
|
private static void Cleanup(Int64 _)
|
||||||
{
|
{
|
||||||
return Sessions
|
DeleteS3Keys();
|
||||||
.Where(s => s.Token == token).ToList()
|
DeleteStaleSessions();
|
||||||
.Where(s => s.Valid)
|
|
||||||
.Select(s => s.UserId)
|
|
||||||
.Select(GetUserById)
|
|
||||||
.FirstOrDefault();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void DeleteStaleSessions()
|
||||||
public static Boolean NewSession(Session ses) => Create(ses);
|
|
||||||
|
|
||||||
public static Boolean DeleteSession(Int64 id)
|
|
||||||
{
|
{
|
||||||
try
|
var deadline = DateTime.Now - Session.MaxAge;
|
||||||
{
|
Sessions.Delete(s => s.LastSeen < deadline);
|
||||||
Sessions.Delete(u => u.UserId == id);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String? GetInstallationS3Key(Int64 installationId)
|
private static void DeleteS3Keys()
|
||||||
{
|
{
|
||||||
return Installations
|
void DeleteKeys() => Installations.Do(i => i.S3Key = "").ForEach(Update); // TODO
|
||||||
.FirstOrDefault(i => i.Id == installationId)?
|
|
||||||
.S3Key;
|
Connection.RunInTransaction(DeleteKeys);
|
||||||
}
|
|
||||||
|
|
||||||
public static void DeleteAllS3Keys()
|
|
||||||
{
|
|
||||||
foreach (var installation in Installations.ToList())
|
|
||||||
{
|
|
||||||
installation.S3Key = null;
|
|
||||||
Update(installation);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
using InnovEnergy.App.Backend.DataTypes;
|
||||||
|
using InnovEnergy.App.Backend.DataTypes.Methods;
|
||||||
|
using InnovEnergy.App.Backend.Relations;
|
||||||
|
|
||||||
|
|
||||||
|
namespace InnovEnergy.App.Backend.Database;
|
||||||
|
|
||||||
|
|
||||||
|
public static partial class Db
|
||||||
|
{
|
||||||
|
public static Boolean Delete(Folder folder)
|
||||||
|
{
|
||||||
|
return RunTransaction(DeleteFolderAndAllItsDependencies);
|
||||||
|
|
||||||
|
Boolean DeleteFolderAndAllItsDependencies()
|
||||||
|
{
|
||||||
|
return folder
|
||||||
|
.DescendantFolders()
|
||||||
|
.All(DeleteDescendantFolderAndItsDependencies);
|
||||||
|
}
|
||||||
|
|
||||||
|
Boolean DeleteDescendantFolderAndItsDependencies(Folder f)
|
||||||
|
{
|
||||||
|
User2Folder .Delete(r => r.FolderId == f.Id);
|
||||||
|
Installations.Delete(r => r.ParentId == f.Id);
|
||||||
|
|
||||||
|
return Folders.Delete(r => r.Id == f.Id) > 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean Delete(Installation installation)
|
||||||
|
{
|
||||||
|
return RunTransaction(DeleteInstallationAndItsDependencies);
|
||||||
|
|
||||||
|
Boolean DeleteInstallationAndItsDependencies()
|
||||||
|
{
|
||||||
|
User2Installation.Delete(i => i.InstallationId == installation.Id);
|
||||||
|
return Installations.Delete(i => i.Id == installation.Id) > 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean Delete(User user)
|
||||||
|
{
|
||||||
|
return RunTransaction(DeleteUserAndHisDependencies);
|
||||||
|
|
||||||
|
Boolean DeleteUserAndHisDependencies()
|
||||||
|
{
|
||||||
|
User2Folder .Delete(u => u.UserId == user.Id);
|
||||||
|
User2Installation.Delete(u => u.UserId == user.Id);
|
||||||
|
|
||||||
|
return Users.Delete(u => u.Id == user.Id) > 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#pragma warning disable CS0618
|
||||||
|
|
||||||
|
// private!!
|
||||||
|
private static Boolean Delete(Session session)
|
||||||
|
{
|
||||||
|
return Sessions.Delete(s => s.Id == session.Id) > 0;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
using InnovEnergy.App.Backend.Model.Relations;
|
using InnovEnergy.App.Backend.Relations;
|
||||||
|
|
||||||
namespace InnovEnergy.App.Backend.Database;
|
namespace InnovEnergy.App.Backend.Database;
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@ public static partial class Db
|
||||||
|
|
||||||
private static void CreateFakeUserTree()
|
private static void CreateFakeUserTree()
|
||||||
{
|
{
|
||||||
foreach (var userId in Enumerable.Range(1, NbUsers))
|
foreach (var userId in Enumerable.Range(1, Users.Count()))
|
||||||
{
|
{
|
||||||
var user = GetUserById(userId);
|
var user = GetUserById(userId);
|
||||||
if (user is null)
|
if (user is null)
|
||||||
|
@ -34,7 +34,7 @@ public static partial class Db
|
||||||
|
|
||||||
private static void CreateFakeFolderTree()
|
private static void CreateFakeFolderTree()
|
||||||
{
|
{
|
||||||
foreach (var folderId in Enumerable.Range(1, NbFolders))
|
foreach (var folderId in Enumerable.Range(1, Folders.Count()))
|
||||||
{
|
{
|
||||||
var folder = GetFolderById(folderId);
|
var folder = GetFolderById(folderId);
|
||||||
if (folder is null)
|
if (folder is null)
|
||||||
|
@ -50,7 +50,7 @@ public static partial class Db
|
||||||
|
|
||||||
private static void LinkFakeInstallationsToFolders()
|
private static void LinkFakeInstallationsToFolders()
|
||||||
{
|
{
|
||||||
var nFolders = NbFolders;
|
var nFolders = Folders.Count();
|
||||||
|
|
||||||
foreach (var installation in Installations)
|
foreach (var installation in Installations)
|
||||||
{
|
{
|
||||||
|
@ -64,8 +64,8 @@ public static partial class Db
|
||||||
foreach (var uf in User2Folder) // remove existing relations
|
foreach (var uf in User2Folder) // remove existing relations
|
||||||
Connection.Delete(uf);
|
Connection.Delete(uf);
|
||||||
|
|
||||||
var nFolders = NbFolders;
|
var nFolders = Folders.Count();
|
||||||
var nUsers = NbUsers;
|
var nUsers = Users.Count();
|
||||||
|
|
||||||
foreach (var user in Users)
|
foreach (var user in Users)
|
||||||
while (Random.Shared.Next((Int32)(nUsers - user.Id + 1)) != 0)
|
while (Random.Shared.Next((Int32)(nUsers - user.Id + 1)) != 0)
|
||||||
|
@ -84,7 +84,7 @@ public static partial class Db
|
||||||
foreach (var ui in User2Installation) // remove existing relations
|
foreach (var ui in User2Installation) // remove existing relations
|
||||||
Connection.Delete(ui);
|
Connection.Delete(ui);
|
||||||
|
|
||||||
var nbInstallations = NbInstallations;
|
var nbInstallations = Installations.Count();
|
||||||
|
|
||||||
foreach (var user in Users)
|
foreach (var user in Users)
|
||||||
while (Random.Shared.Next(5) != 0)
|
while (Random.Shared.Next(5) != 0)
|
||||||
|
|
|
@ -1,91 +0,0 @@
|
||||||
using InnovEnergy.App.Backend.Model;
|
|
||||||
using InnovEnergy.Lib.Utils;
|
|
||||||
|
|
||||||
namespace InnovEnergy.App.Backend.Database;
|
|
||||||
|
|
||||||
public static partial class Db
|
|
||||||
{
|
|
||||||
public static IEnumerable<Folder> GetChildFolders(this Folder parent)
|
|
||||||
{
|
|
||||||
return Folders.Where(f => f.ParentId == parent.Id);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IEnumerable<Installation> GetChildInstallations(this Folder parent)
|
|
||||||
{
|
|
||||||
return Installations.Where(f => f.ParentId == parent.Id);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IEnumerable<Folder> GetDescendantFolders(this Folder parent)
|
|
||||||
{
|
|
||||||
return parent.Traverse(GetChildFolders);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Boolean IsDescendantOf(this Folder folder, Int64 ancestorFolderId)
|
|
||||||
{
|
|
||||||
return Ancestors(folder)
|
|
||||||
.Any(u => u.Id == ancestorFolderId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Boolean IsDescendantOf(this Folder folder, Folder ancestor)
|
|
||||||
{
|
|
||||||
return IsDescendantOf(folder, ancestor.Id);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static IEnumerable<Folder> Ancestors(this Folder child)
|
|
||||||
{
|
|
||||||
return child.Unfold(GetParent);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Folder? GetParent(this Folder f)
|
|
||||||
{
|
|
||||||
return IsRoot(f)
|
|
||||||
? null
|
|
||||||
: GetFolderById(f.ParentId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Boolean IsRoot(this Folder f)
|
|
||||||
{
|
|
||||||
return f.ParentId == 0; // root has ParentId 0 by definition
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Int64 CreateFolder(Folder folder)
|
|
||||||
{
|
|
||||||
return Create(folder);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Boolean UpdateFolder(Folder folder)
|
|
||||||
{
|
|
||||||
// TODO: no circles in path
|
|
||||||
|
|
||||||
return Update(folder);
|
|
||||||
}
|
|
||||||
|
|
||||||
// These should not be necessary, just Update folder/installation with new parentId
|
|
||||||
|
|
||||||
// public Boolean ChangeParent(Installation child, Int64 parentId)
|
|
||||||
// {
|
|
||||||
// child.ParentId = parentId;
|
|
||||||
// return UpdateInstallation(child);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// public Boolean ChangeParent(Folder child, Int64 parentId)
|
|
||||||
// {
|
|
||||||
// child.ParentId = parentId;
|
|
||||||
// return UpdateFolder(child);
|
|
||||||
// }
|
|
||||||
|
|
||||||
public static Boolean DeleteFolder(Folder folder)
|
|
||||||
{
|
|
||||||
// Delete direct children
|
|
||||||
User2Folder .Delete(f => f.FolderId == folder.Id);
|
|
||||||
Installations.Delete(i => i.ParentId == folder.Id);
|
|
||||||
|
|
||||||
// recursion
|
|
||||||
Folders.Where(f => f.ParentId == folder.Id)
|
|
||||||
.ForEach(DeleteFolder);
|
|
||||||
|
|
||||||
return Delete(folder);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
|
@ -1,47 +0,0 @@
|
||||||
using InnovEnergy.App.Backend.Model;
|
|
||||||
using SQLite;
|
|
||||||
|
|
||||||
namespace InnovEnergy.App.Backend.Database;
|
|
||||||
|
|
||||||
public static partial class Db
|
|
||||||
{
|
|
||||||
public static IEnumerable<Folder> Ancestors(this Installation installation)
|
|
||||||
{
|
|
||||||
var parentFolder = GetParent(installation);
|
|
||||||
|
|
||||||
return parentFolder is null
|
|
||||||
? Enumerable.Empty<Folder>()
|
|
||||||
: Ancestors(parentFolder);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Folder? GetParent(this Installation installation)
|
|
||||||
{
|
|
||||||
return IsRoot(installation)
|
|
||||||
? null
|
|
||||||
: GetFolderById(installation.ParentId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Boolean IsRoot(this Installation i)
|
|
||||||
{
|
|
||||||
return i.ParentId == 0; // root has ParentId 0 by definition
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Int64 CreateInstallation(this Installation installation)
|
|
||||||
{
|
|
||||||
return Create(installation);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Boolean UpdateInstallation(this Installation installation)
|
|
||||||
{
|
|
||||||
return Update(installation);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Boolean DeleteInstallation(this Installation installation)
|
|
||||||
{
|
|
||||||
User2Installation.Delete(i => i.InstallationId == installation.Id);
|
|
||||||
|
|
||||||
return Delete(installation);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
|
@ -0,0 +1,86 @@
|
||||||
|
using InnovEnergy.App.Backend.DataTypes;
|
||||||
|
using InnovEnergy.App.Backend.Relations;
|
||||||
|
|
||||||
|
|
||||||
|
namespace InnovEnergy.App.Backend.Database;
|
||||||
|
|
||||||
|
|
||||||
|
public static partial class Db
|
||||||
|
{
|
||||||
|
public static Folder? GetFolderById(Int64 id)
|
||||||
|
{
|
||||||
|
return Folders
|
||||||
|
.FirstOrDefault(f => f.Id == id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Installation? GetInstallationById(Int64 id)
|
||||||
|
{
|
||||||
|
return Installations
|
||||||
|
.FirstOrDefault(i => i.Id == id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static User? GetUserById(Int64 id)
|
||||||
|
{
|
||||||
|
return Users
|
||||||
|
.FirstOrDefault(u => u.Id == id);
|
||||||
|
}
|
||||||
|
|
||||||
|
// private!!
|
||||||
|
private static Session? GetSessionById(Int64 id)
|
||||||
|
{
|
||||||
|
#pragma warning disable CS0618
|
||||||
|
|
||||||
|
return Sessions
|
||||||
|
.FirstOrDefault(u => u.Id == id);
|
||||||
|
|
||||||
|
#pragma warning restore CS0618
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static User? GetUserByEmail(String email)
|
||||||
|
{
|
||||||
|
return Users
|
||||||
|
.FirstOrDefault(u => u.Email == email);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Session? GetSession(String token)
|
||||||
|
{
|
||||||
|
var session = Sessions
|
||||||
|
.FirstOrDefault(s => s.Token == token);
|
||||||
|
|
||||||
|
// cannot use session.Valid in the DB query above.
|
||||||
|
// It does not exist in the db (IgnoreAttribute)
|
||||||
|
|
||||||
|
if (session is null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
if (!session.Valid)
|
||||||
|
{
|
||||||
|
Delete(session);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return session;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static User? GetUserBySessionToken(String token)
|
||||||
|
{
|
||||||
|
var session = Sessions
|
||||||
|
.FirstOrDefault(s => s.Token == token);
|
||||||
|
|
||||||
|
// cannot user session.Expired in the DB query above.
|
||||||
|
// It does not exist in the db (IgnoreAttribute)
|
||||||
|
|
||||||
|
if (session is null)
|
||||||
|
return null;
|
||||||
|
|
||||||
|
if (!session.Valid)
|
||||||
|
{
|
||||||
|
Delete(session);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return GetUserById(session.UserId);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,64 @@
|
||||||
|
using InnovEnergy.App.Backend.DataTypes;
|
||||||
|
using InnovEnergy.App.Backend.DataTypes.Methods;
|
||||||
|
using InnovEnergy.App.Backend.Relations;
|
||||||
|
|
||||||
|
|
||||||
|
namespace InnovEnergy.App.Backend.Database;
|
||||||
|
|
||||||
|
|
||||||
|
public static partial class Db
|
||||||
|
{
|
||||||
|
public static Boolean Update(Folder folder)
|
||||||
|
{
|
||||||
|
if (folder.IsRelativeRoot()) // TODO: triple check
|
||||||
|
{
|
||||||
|
var original = GetFolderById(folder.Id);
|
||||||
|
if (original is null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
folder.ParentId = original.ParentId;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Connection.InsertOrReplace(folder) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean Update(Installation installation)
|
||||||
|
{
|
||||||
|
if (installation.IsRelativeRoot()) // TODO: triple check
|
||||||
|
{
|
||||||
|
var original = GetInstallationById(installation.Id);
|
||||||
|
if (original is null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
installation.ParentId = original.ParentId;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Connection.InsertOrReplace(installation) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static Boolean Update(User user)
|
||||||
|
{
|
||||||
|
var originalUser = GetUserById(user.Id);
|
||||||
|
|
||||||
|
return originalUser is not null
|
||||||
|
&& user.Id == originalUser.Id // these columns must not be modified!
|
||||||
|
&& user.ParentId == originalUser.ParentId
|
||||||
|
&& user.Email == originalUser.Email
|
||||||
|
&& user.Password == originalUser.Password
|
||||||
|
&& Connection.InsertOrReplace(user) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Boolean Update(this Session session)
|
||||||
|
{
|
||||||
|
#pragma warning disable CS0618
|
||||||
|
var originalSession = GetSessionById(session.Id);
|
||||||
|
#pragma warning restore CS0618
|
||||||
|
|
||||||
|
return originalSession is not null
|
||||||
|
&& session.Token == originalSession.Token // these columns must not be modified!
|
||||||
|
&& session.UserId == originalSession.UserId
|
||||||
|
&& Connection.InsertOrReplace(session) > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -1,254 +0,0 @@
|
||||||
using System.Net.Http.Headers;
|
|
||||||
using System.Net.Mail;
|
|
||||||
using System.Security.Cryptography;
|
|
||||||
using System.Text;
|
|
||||||
using System.Text.Json.Nodes;
|
|
||||||
using System.Text.RegularExpressions;
|
|
||||||
using InnovEnergy.App.Backend.Model;
|
|
||||||
using InnovEnergy.App.Backend.Utils;
|
|
||||||
using InnovEnergy.Lib.Utils;
|
|
||||||
|
|
||||||
|
|
||||||
namespace InnovEnergy.App.Backend.Database;
|
|
||||||
|
|
||||||
public static partial class Db
|
|
||||||
{
|
|
||||||
public static IEnumerable<User> GetChildUsers(this User parent)
|
|
||||||
{
|
|
||||||
return Users
|
|
||||||
.Where(f => f.ParentId == parent.Id);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IEnumerable<User> GetDescendantUsers(this User parent)
|
|
||||||
{
|
|
||||||
return parent.Traverse(GetChildUsers);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Boolean IsDescendantOf(this User user, User ancestor)
|
|
||||||
{
|
|
||||||
return Ancestors(user)
|
|
||||||
.Any(u => u.Id == ancestor.Id);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static IEnumerable<User> Ancestors(this User child)
|
|
||||||
{
|
|
||||||
return child.Unfold(GetParent);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static User? GetParent(this User u)
|
|
||||||
{
|
|
||||||
return IsRoot(u)
|
|
||||||
? null
|
|
||||||
: GetUserById(u.ParentId);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Boolean IsRoot(this User u)
|
|
||||||
{
|
|
||||||
return u.ParentId == 0; // root has ParentId 0 by definition
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Boolean HasDirectAccessToFolder(this User user, Folder folder)
|
|
||||||
{
|
|
||||||
return HasDirectAccessToFolder(user, folder.Id);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Boolean HasDirectAccessToFolder(this User user, Int64 folderId)
|
|
||||||
{
|
|
||||||
return User2Folder.Any(r => r.FolderId == folderId && r.UserId == user.Id);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Boolean HasAccessToFolder(this User user, Int64 folderId)
|
|
||||||
{
|
|
||||||
var folder = GetFolderById(folderId);
|
|
||||||
if (folder is null)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
return Ancestors(folder).Any(f => HasDirectAccessToFolder(user, f));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static User? GetUserByEmail(String email) => Users.FirstOrDefault(u => u.Email == email);
|
|
||||||
|
|
||||||
public static Int64 CreateUser(User user)
|
|
||||||
{
|
|
||||||
if (GetUserByEmail(user.Email) is not null)
|
|
||||||
return -1; // TODO: User with that email already exists
|
|
||||||
|
|
||||||
//Salting and Hashing password
|
|
||||||
var salt = Crypto.GenerateSalt();
|
|
||||||
var hashedPassword = Crypto.ComputeHash(Encoding.UTF8.GetBytes(user.Password),
|
|
||||||
Encoding.UTF8.GetBytes(salt + "innovEnergy"));
|
|
||||||
|
|
||||||
user.Salt = salt;
|
|
||||||
user.Password = hashedPassword;
|
|
||||||
|
|
||||||
return Create(user);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private static Byte[] HmacSha256Digest(String message, String secret)
|
|
||||||
{
|
|
||||||
var encoding = new UTF8Encoding();
|
|
||||||
var keyBytes = encoding.GetBytes(secret);
|
|
||||||
var messageBytes = encoding.GetBytes(message);
|
|
||||||
var cryptographer = new HMACSHA256(keyBytes);
|
|
||||||
|
|
||||||
return cryptographer.ComputeHash(messageBytes);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String BuildSignature(String method, String path, String data, Int64 time, String secret)
|
|
||||||
{
|
|
||||||
var messageToSign = "";
|
|
||||||
messageToSign += method + " /v2/" + path + "\n";
|
|
||||||
messageToSign += data + "\n";
|
|
||||||
|
|
||||||
// query strings
|
|
||||||
messageToSign += "\n";
|
|
||||||
// headers
|
|
||||||
messageToSign += "\n";
|
|
||||||
|
|
||||||
messageToSign += time;
|
|
||||||
|
|
||||||
Console.WriteLine("Message to sign:\n" + messageToSign);
|
|
||||||
|
|
||||||
var hmac = HmacSha256Digest(messageToSign, secret);
|
|
||||||
return Convert.ToBase64String(hmac);
|
|
||||||
}
|
|
||||||
|
|
||||||
// public Object CreateAndSaveUserS3ApiKey(User user)
|
|
||||||
// {
|
|
||||||
// //EXOSCALE API URL
|
|
||||||
// const String url = "https://api-ch-dk-2.exoscale.com/v2/";
|
|
||||||
// const String path = "access-key";
|
|
||||||
//
|
|
||||||
// //TODO HIDE ME
|
|
||||||
// const String secret = "S2K1okphiCSNK4mzqr4swguFzngWAMb1OoSlZsJa9F0";
|
|
||||||
// const String apiKey = "EXOb98ec9008e3ec16e19d7b593";
|
|
||||||
//
|
|
||||||
// var installationList = User2Installation
|
|
||||||
// .Where(i => i.UserId == user.Id)
|
|
||||||
// .SelectMany(i => Installations.Where(f => i.InstallationId == f.Id))
|
|
||||||
// .ToList();
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// var instList = new JsonArray();
|
|
||||||
//
|
|
||||||
// foreach (var installation in installationList)
|
|
||||||
// {
|
|
||||||
// instList.Add(new JsonObject {["domain"] = "sos",["resource-name"] = installation.Name,["resource-type"] = "bucket"});
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// var jsonPayload = new JsonObject { ["name"] = user.Email, ["operations"] = new JsonArray{ "list-sos-bucket", "get-sos-object" }, ["content"] = instList};
|
|
||||||
// var stringPayload = jsonPayload.ToJsonString();
|
|
||||||
//
|
|
||||||
// var unixExpiration = DateTimeOffset.UtcNow.ToUnixTimeSeconds()+60;
|
|
||||||
// var signature = BuildSignature("POST", path, stringPayload, unixExpiration , secret);
|
|
||||||
//
|
|
||||||
// var authHeader = "credential="+apiKey+",expires="+unixExpiration+",signature="+signature;
|
|
||||||
//
|
|
||||||
// var client = new HttpClient();
|
|
||||||
// client.DefaultRequestHeaders.Authorization =
|
|
||||||
// new AuthenticationHeaderValue("EXO2-HMAC-SHA256", authHeader);
|
|
||||||
//
|
|
||||||
// var content = new StringContent(stringPayload, Encoding.UTF8, "application/json");
|
|
||||||
//
|
|
||||||
//
|
|
||||||
// var response = client.PostAsync(url+path, content).Result;
|
|
||||||
//
|
|
||||||
// if (response.StatusCode.ToString() != "OK")
|
|
||||||
// {
|
|
||||||
// return response;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// var responseString = response.Content.ReadAsStringAsync().Result;
|
|
||||||
// return Enumerable.Last(Regex.Match(responseString, "key\\\":\\\"([A-Z])\\w+").ToString().Split('"'));
|
|
||||||
// // return SetUserS3ApiKey(user, newKey);
|
|
||||||
//
|
|
||||||
// }
|
|
||||||
|
|
||||||
public static Object CreateAndSaveInstallationS3ApiKey(Installation installation)
|
|
||||||
{
|
|
||||||
//EXOSCALE API URL
|
|
||||||
const String url = "https://api-ch-dk-2.exoscale.com/v2/";
|
|
||||||
const String path = "access-key";
|
|
||||||
|
|
||||||
//TODO HIDE ME
|
|
||||||
const String secret = "S2K1okphiCSNK4mzqr4swguFzngWAMb1OoSlZsJa9F0";
|
|
||||||
const String apiKey = "EXOb98ec9008e3ec16e19d7b593";
|
|
||||||
|
|
||||||
var instList = new JsonArray();
|
|
||||||
instList.Add(new JsonObject {["domain"] = "sos",["resource-name"] = installation.Name,["resource-type"] = "bucket"});
|
|
||||||
|
|
||||||
var jsonPayload = new JsonObject { ["name"] = installation.Id, ["operations"] = new JsonArray{ "list-sos-bucket", "get-sos-object" }, ["content"] = instList};
|
|
||||||
var stringPayload = jsonPayload.ToJsonString();
|
|
||||||
|
|
||||||
var unixExpiration = DateTimeOffset.UtcNow.ToUnixTimeSeconds()+60;
|
|
||||||
var signature = BuildSignature("POST", path, stringPayload, unixExpiration , secret);
|
|
||||||
|
|
||||||
var authHeader = "credential="+apiKey+",expires="+unixExpiration+",signature="+signature;
|
|
||||||
|
|
||||||
var client = new HttpClient();
|
|
||||||
client.DefaultRequestHeaders.Authorization =
|
|
||||||
new AuthenticationHeaderValue("EXO2-HMAC-SHA256", authHeader);
|
|
||||||
|
|
||||||
var content = new StringContent(stringPayload, Encoding.UTF8, "application/json");
|
|
||||||
|
|
||||||
|
|
||||||
var response = client.PostAsync(url+path, content).Result;
|
|
||||||
|
|
||||||
if (response.StatusCode.ToString() != "OK")
|
|
||||||
{
|
|
||||||
return response;
|
|
||||||
}
|
|
||||||
|
|
||||||
var responseString = response.Content.ReadAsStringAsync().Result;
|
|
||||||
var newKey = Enumerable.Last(Regex.Match(responseString, "key\\\":\\\"([A-Z])\\w+").ToString().Split('"'));
|
|
||||||
|
|
||||||
installation.S3Key = newKey;
|
|
||||||
UpdateInstallation(installation);
|
|
||||||
return newKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Boolean UpdateUser(User user)
|
|
||||||
{
|
|
||||||
var oldUser = GetUserById(user.Id);
|
|
||||||
if (oldUser == null)
|
|
||||||
return false; // TODO: "User doesn't exist"
|
|
||||||
|
|
||||||
//Checking for unchangeable things
|
|
||||||
// TODO: depends on privileges of caller
|
|
||||||
|
|
||||||
user.Id = oldUser.Id;
|
|
||||||
user.ParentId = oldUser.ParentId;
|
|
||||||
user.Email = oldUser.Email;
|
|
||||||
|
|
||||||
return Update(user);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Boolean DeleteUser(User user)
|
|
||||||
{
|
|
||||||
User2Folder.Delete(u => u.UserId == user.Id);
|
|
||||||
User2Installation.Delete(u => u.UserId == user.Id);
|
|
||||||
|
|
||||||
//Todo check for orphaned Installations/Folders
|
|
||||||
|
|
||||||
// GetChildUsers()
|
|
||||||
|
|
||||||
return Delete(user);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// TODO
|
|
||||||
private static Boolean IsValidEmail(String email)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
var emailAddress = new MailAddress(email);
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,6 +0,0 @@
|
||||||
namespace InnovEnergy.App.Backend.Model;
|
|
||||||
|
|
||||||
public class Folder : TreeNode
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
|
@ -1,35 +0,0 @@
|
||||||
using SQLite;
|
|
||||||
|
|
||||||
namespace InnovEnergy.App.Backend.Model.Relations;
|
|
||||||
|
|
||||||
public class Session : Relation<String, Int64>
|
|
||||||
{
|
|
||||||
[Indexed] public String Token { get => Left ; set => Left = value;}
|
|
||||||
[Indexed] public Int64 UserId { get => Right; set => Right = value;}
|
|
||||||
[Indexed] public DateTime ExpiresAt { get; set; }
|
|
||||||
|
|
||||||
[Ignore] public Boolean Valid => ExpiresAt > DateTime.Now;
|
|
||||||
[Ignore] public Boolean Expired => !Valid;
|
|
||||||
|
|
||||||
[Obsolete("To be used only by serializer")]
|
|
||||||
public Session()
|
|
||||||
{}
|
|
||||||
|
|
||||||
public Session(User user) : this(user, TimeSpan.FromDays(7))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public Session(User user, TimeSpan validFor)
|
|
||||||
{
|
|
||||||
Token = CreateToken();
|
|
||||||
UserId = user.Id;
|
|
||||||
ExpiresAt = DateTime.Now + validFor;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static String CreateToken()
|
|
||||||
{
|
|
||||||
var token = new Byte[16]; // 128 bit
|
|
||||||
Random.Shared.NextBytes(token);
|
|
||||||
return Convert.ToBase64String(token);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
using SQLite;
|
|
||||||
|
|
||||||
namespace InnovEnergy.App.Backend.Model.Relations;
|
|
||||||
|
|
||||||
internal class User2Folder : Relation<Int64, Int64>
|
|
||||||
{
|
|
||||||
[Indexed] public Int64 UserId { get => Left ; set => Left = value;}
|
|
||||||
[Indexed] public Int64 FolderId { get => Right; set => Right = value;}
|
|
||||||
}
|
|
|
@ -1,9 +0,0 @@
|
||||||
using SQLite;
|
|
||||||
|
|
||||||
namespace InnovEnergy.App.Backend.Model.Relations;
|
|
||||||
|
|
||||||
internal class User2Installation : Relation<Int64, Int64>
|
|
||||||
{
|
|
||||||
[Indexed] public Int64 UserId { get => Left ; set => Left = value;}
|
|
||||||
[Indexed] public Int64 InstallationId { get => Right; set => Right = value;}
|
|
||||||
}
|
|
|
@ -1,22 +0,0 @@
|
||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
|
|
||||||
namespace InnovEnergy.App.Backend.Model;
|
|
||||||
|
|
||||||
public abstract partial class TreeNode
|
|
||||||
{
|
|
||||||
// Note: Only consider Id, but not ParentId for TreeNode equality checks
|
|
||||||
protected Boolean Equals(TreeNode other)
|
|
||||||
{
|
|
||||||
return Id == other.Id;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override Boolean Equals(Object? obj)
|
|
||||||
{
|
|
||||||
if (ReferenceEquals(null, obj)) return false;
|
|
||||||
if (ReferenceEquals(this, obj)) return true;
|
|
||||||
return obj.GetType() == GetType() && Equals((TreeNode)obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
[SuppressMessage("ReSharper", "NonReadonlyMemberInGetHashCode")]
|
|
||||||
public override Int32 GetHashCode() => Id.GetHashCode();
|
|
||||||
}
|
|
|
@ -8,11 +8,9 @@ public static class Program
|
||||||
{
|
{
|
||||||
public static void Main(String[] args)
|
public static void Main(String[] args)
|
||||||
{
|
{
|
||||||
using (var db = Db.Connect())
|
|
||||||
db.CreateFakeRelations();
|
|
||||||
|
|
||||||
Observable.Interval(TimeSpan.FromDays(1)).Subscribe((_) => deleteInstallationS3KeysDaily());
|
|
||||||
|
|
||||||
|
Db.CreateFakeRelations();
|
||||||
|
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
builder.Services.AddControllers(); // TODO: remove magic, specify controllers explicitly
|
builder.Services.AddControllers(); // TODO: remove magic, specify controllers explicitly
|
||||||
|
@ -47,22 +45,19 @@ public static class Program
|
||||||
app.Run();
|
app.Run();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static void deleteInstallationS3KeysDaily()
|
|
||||||
{
|
|
||||||
using var db = Db.Connect();
|
|
||||||
db.DeleteS3KeysDaily();
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private static async Task SetSessionUser(HttpContext ctx, RequestDelegate next)
|
private static async Task SetSessionUser(HttpContext ctx, RequestDelegate next)
|
||||||
{
|
{
|
||||||
var headers = ctx.Request.Headers;
|
var headers = ctx.Request.Headers;
|
||||||
var hasToken = headers.TryGetValue("auth", out var token);
|
var hasToken = headers.TryGetValue("auth", out var token) ;
|
||||||
|
|
||||||
if (hasToken)
|
if (hasToken)
|
||||||
{
|
{
|
||||||
using var db = Db.Connect();
|
var session = Db.GetSession(token);
|
||||||
ctx.Items["User"] = db.GetUserByToken(token.ToString());
|
|
||||||
|
if (session is not null)
|
||||||
|
ctx.Items["User"] = session;
|
||||||
}
|
}
|
||||||
|
|
||||||
await next(ctx);
|
await next(ctx);
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
using System.Diagnostics.CodeAnalysis;
|
using System.Diagnostics.CodeAnalysis;
|
||||||
using SQLite;
|
using SQLite;
|
||||||
|
|
||||||
namespace InnovEnergy.App.Backend.Model.Relations;
|
namespace InnovEnergy.App.Backend.Relations;
|
||||||
|
|
||||||
public abstract class Relation<L,R>
|
public abstract class Relation<L,R>
|
||||||
{
|
{
|
|
@ -0,0 +1,44 @@
|
||||||
|
using InnovEnergy.App.Backend.Database;
|
||||||
|
using InnovEnergy.App.Backend.DataTypes;
|
||||||
|
using InnovEnergy.Lib.Utils;
|
||||||
|
using SQLite;
|
||||||
|
|
||||||
|
namespace InnovEnergy.App.Backend.Relations;
|
||||||
|
|
||||||
|
public class Session : Relation<String, Int64>
|
||||||
|
{
|
||||||
|
public static TimeSpan MaxAge { get; } = TimeSpan.FromDays(7);
|
||||||
|
|
||||||
|
[Unique ] public String Token { get => Left ; init => Left = value;}
|
||||||
|
[Indexed] public Int64 UserId { get => Right; init => Right = value;}
|
||||||
|
[Indexed] public DateTime LastSeen { get; set; }
|
||||||
|
|
||||||
|
[Ignore] public Boolean Valid => DateTime.Now - LastSeen < MaxAge
|
||||||
|
&& !User.IsNull();
|
||||||
|
|
||||||
|
[Ignore] public User User => _User ??= Db.GetUserById(UserId)!;
|
||||||
|
|
||||||
|
|
||||||
|
private User? _User;
|
||||||
|
|
||||||
|
[Obsolete("To be used only by deserializer")]
|
||||||
|
public Session()
|
||||||
|
{}
|
||||||
|
|
||||||
|
public Session(User user)
|
||||||
|
{
|
||||||
|
_User = user;
|
||||||
|
Token = CreateToken();
|
||||||
|
UserId = user.Id;
|
||||||
|
LastSeen = DateTime.Now;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String CreateToken()
|
||||||
|
{
|
||||||
|
var token = new Byte[24];
|
||||||
|
Random.Shared.NextBytes(token);
|
||||||
|
return Convert.ToBase64String(token);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
using SQLite;
|
||||||
|
|
||||||
|
namespace InnovEnergy.App.Backend.Relations;
|
||||||
|
|
||||||
|
public class User2Folder : Relation<Int64, Int64>
|
||||||
|
{
|
||||||
|
[Indexed] public Int64 UserId { get => Left ; init => Left = value;}
|
||||||
|
[Indexed] public Int64 FolderId { get => Right; init => Right = value;}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
using SQLite;
|
||||||
|
|
||||||
|
namespace InnovEnergy.App.Backend.Relations;
|
||||||
|
|
||||||
|
public class User2Installation : Relation<Int64, Int64>
|
||||||
|
{
|
||||||
|
[Indexed] public Int64 UserId { get => Left ; init => Left = value;}
|
||||||
|
[Indexed] public Int64 InstallationId { get => Right; init => Right = value;}
|
||||||
|
}
|
|
@ -1,22 +0,0 @@
|
||||||
using System.Security.Cryptography;
|
|
||||||
|
|
||||||
namespace InnovEnergy.App.Backend.Utils;
|
|
||||||
|
|
||||||
public static class Crypto
|
|
||||||
{
|
|
||||||
public static String ComputeHash(Byte[] bytesToHash, Byte[] salt)
|
|
||||||
{
|
|
||||||
using var mySHA256 = SHA256.Create();
|
|
||||||
var hashValue = mySHA256.ComputeHash(bytesToHash);
|
|
||||||
// var hashValue = new Rfc2898DeriveBytes(hashValue, salt, 10000);
|
|
||||||
return Convert.ToBase64String(hashValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String GenerateSalt()
|
|
||||||
{
|
|
||||||
var bytes = new Byte[128 / 8];
|
|
||||||
var rng = RandomNumberGenerator.Create();
|
|
||||||
rng.GetBytes(bytes);
|
|
||||||
return Convert.ToBase64String(bytes);
|
|
||||||
}
|
|
||||||
}
|
|
Binary file not shown.
Loading…
Reference in New Issue