cleanup Rest API

This commit is contained in:
ig 2023-02-24 12:58:47 +01:00
parent d2006b52e1
commit c7649dced2
23 changed files with 319 additions and 315 deletions

View File

@ -4,6 +4,8 @@
<TargetFramework>net6.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable> <Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings> <ImplicitUsings>enable</ImplicitUsings>
<LangVersion>preview</LangVersion>
<RootNamespace>Innovenergy.Backend</RootNamespace>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View File

@ -1,21 +1,21 @@
using System.Net; using System.Net;
using System.Text; using System.Text;
using System.Text.Json; using Innovenergy.Backend.Database;
using Backend.Database; using Innovenergy.Backend.Model;
using Backend.Model; using Innovenergy.Backend.Model.Relations;
using Backend.Model.Relations; using Innovenergy.Backend.Utils;
using Backend.Utils;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using HttpContextAccessor = Microsoft.AspNetCore.Http.HttpContextAccessor; using HttpContextAccessor = Microsoft.AspNetCore.Http.HttpContextAccessor;
namespace Backend.Controllers; namespace Innovenergy.Backend.Controllers;
[ApiController] [ApiController]
[Route("api/")] [Route("api/")]
public class Controller public class Controller
{ {
[ProducesResponseType(200)] [Returns<String>]
[ProducesResponseType(401)] [Returns(HttpStatusCode.Unauthorized)]
[Returns(HttpStatusCode.BadRequest)]
[HttpPost($"{nameof(Login)}")] [HttpPost($"{nameof(Login)}")]
public Object Login(Credentials credentials) public Object Login(Credentials credentials)
{ {
@ -29,181 +29,175 @@ public class Controller
if (user is null) if (user is null)
return new HttpResponseMessage(HttpStatusCode.Unauthorized); return new HttpResponseMessage(HttpStatusCode.Unauthorized);
// if (!VerifyPassword(password, user)) #if !DEBUG
// return new HttpResponseMessage(HttpStatusCode.Unauthorized); if (!VerifyPassword(credentials.Password, user))
return new HttpResponseMessage(HttpStatusCode.Unauthorized);
#endif
var ses = new Session(user); var ses = new Session(user);
db.NewSession(ses); db.NewSession(ses);
return ses.Token; return ses.Token;
} }
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; [Returns(HttpStatusCode.OK)]
} [Returns(HttpStatusCode.Unauthorized)]
[ProducesResponseType(200)]
[ProducesResponseType(401)]
[HttpPost($"{nameof(Logout)}")] [HttpPost($"{nameof(Logout)}")]
public Object Logout() public Object Logout()
{ {
var ctxAccessor = new HttpContextAccessor(); var caller = GetCaller();
var ctx = ctxAccessor.HttpContext;
using var db = Db.Connect();
var currentUser = (User)ctx.Items["User"];
if (currentUser is null) if (caller is null)
return new HttpResponseMessage(HttpStatusCode.Conflict); return new HttpResponseMessage(HttpStatusCode.Unauthorized);
return db.DeleteSession(currentUser.Id); using var db = Db.Connect();
return db.DeleteSession(caller.Id);
} }
[ProducesResponseType(200)]
[ProducesResponseType(401)] [Returns(HttpStatusCode.OK)]
[HttpPost($"{nameof(UpdateS3Creds)}")] [Returns(HttpStatusCode.Unauthorized)]
public Object UpdateS3Creds() [HttpPost($"{nameof(UpdateS3Credentials)}")]
public Object UpdateS3Credentials()
{ {
var ctxAccessor = new HttpContextAccessor(); // TODO: S3Credentials should be per session, not per user
var ctx = ctxAccessor.HttpContext;
using var db = Db.Connect();
var currentUser = (User)ctx!.Items["User"]!;
return db.CreateAndSaveUserS3ApiKey(currentUser); var caller = GetCaller();
if (caller is null)
return new HttpResponseMessage(HttpStatusCode.Unauthorized);
using var db = Db.Connect();
return db.CreateAndSaveUserS3ApiKey(caller);
} }
[ProducesResponseType(typeof(User), 200)] [Returns<User>]
[ProducesResponseType(401)] [Returns(HttpStatusCode.Unauthorized)]
[HttpGet($"{nameof(GetUserById)}")] [HttpGet($"{nameof(GetUserById)}")]
public Object GetUserById(Int64 id) public Object GetUserById(Int64 id)
{ {
var ctxAccessor = new HttpContextAccessor(); var caller = GetCaller();
var ctx = ctxAccessor.HttpContext; if (caller is null)
using var db = Db.Connect();
var currentUser = (User)ctx.Items["User"];
var viewedUser = db.GetUserById(id);
//using the same error to prevent fishing for ids
if (currentUser == null || viewedUser == null || !db.IsParentOfChild(currentUser, viewedUser))
return new HttpResponseMessage(HttpStatusCode.Unauthorized); return new HttpResponseMessage(HttpStatusCode.Unauthorized);
using var db = Db.Connect();
return viewedUser; var user = db
.GetDescendantUsers(caller)
.FirstOrDefault(u => u.Id == id);
return user as Object ?? new HttpResponseMessage(HttpStatusCode.Unauthorized);
} }
[ProducesResponseType(typeof(Installation), 200)]
[ProducesResponseType(401)] [Returns<Installation>]
[Returns(HttpStatusCode.Unauthorized)]
[HttpGet($"{nameof(GetInstallationById)}")] [HttpGet($"{nameof(GetInstallationById)}")]
public Object GetInstallationById(Int64 id) public Object GetInstallationById(Int64 id)
{ {
var ctxAccessor = new HttpContextAccessor(); var caller = GetCaller();
var ctx = ctxAccessor.HttpContext; if (caller == null)
using var db = Db.Connect();
var currentUser = (User)ctx.Items["User"];
if (currentUser == null)
return new HttpResponseMessage(HttpStatusCode.Unauthorized); return new HttpResponseMessage(HttpStatusCode.Unauthorized);
using var db = Db.Connect();
var installation = db var installation = db
.GetAllAccessibleInstallations(currentUser) .GetAllAccessibleInstallations(caller)
.FirstOrDefault(i => i.Id == id); .FirstOrDefault(i => i.Id == id);
if (installation is null) return installation as Object ?? new HttpResponseMessage(HttpStatusCode.NotFound);
return new HttpResponseMessage(HttpStatusCode.NotFound);
return installation;
} }
[ProducesResponseType(typeof(Folder), 200)]
[ProducesResponseType(401)] [Returns<Folder>]
[Returns(HttpStatusCode.Unauthorized)]
[HttpGet($"{nameof(GetFolderById)}")] [HttpGet($"{nameof(GetFolderById)}")]
public Object GetFolderById(Int64 id) public Object GetFolderById(Int64 id)
{ {
var ctxAccessor = new HttpContextAccessor(); var caller = GetCaller();
var ctx = ctxAccessor.HttpContext; if (caller == null)
return new HttpResponseMessage(HttpStatusCode.Unauthorized);
using var db = Db.Connect(); using var db = Db.Connect();
var currentUser = (User)ctx.Items["User"];
var folder = db var folder = db
.GetAllAccessibleFolders(currentUser!) .GetAllAccessibleFolders(caller)
.FirstOrDefault(f => f.Id == id); .FirstOrDefault(f => f.Id == id);
if(folder is null) return folder as Object ?? new HttpResponseMessage(HttpStatusCode.NotFound);
return new HttpResponseMessage(HttpStatusCode.NotFound);
return folder;
} }
[ProducesResponseType(200)]
[ProducesResponseType(401)] [Returns<Installation[]>] // assuming swagger knows about arrays but not lists (JSON)
[Returns(HttpStatusCode.Unauthorized)]
[HttpGet($"{nameof(GetAllInstallations)}/")] [HttpGet($"{nameof(GetAllInstallations)}/")]
public Object GetAllInstallations() public Object GetAllInstallations()
{ {
var ctxAccessor = new HttpContextAccessor(); var caller = GetCaller();
var ctx = ctxAccessor.HttpContext; if (caller == null)
using var db = Db.Connect();
var user = (User)ctx.Items["User"];
if (user == null)
return new HttpResponseMessage(HttpStatusCode.Unauthorized); return new HttpResponseMessage(HttpStatusCode.Unauthorized);
using var db = Db.Connect();
return db.GetAllAccessibleInstallations(user).ToList(); return db
.GetAllAccessibleInstallations(caller)
.ToList(); // important!
} }
[ProducesResponseType(200)]
[ProducesResponseType(401)] [Returns<Folder[]>] // assuming swagger knows about arrays but not lists (JSON)
[Returns(HttpStatusCode.Unauthorized)]
[HttpGet($"{nameof(GetAllFolders)}/")] [HttpGet($"{nameof(GetAllFolders)}/")]
public Object GetAllFolders() public Object GetAllFolders()
{ {
var ctxAccessor = new HttpContextAccessor(); var caller = GetCaller();
var ctx = ctxAccessor.HttpContext; if (caller == null)
var user = (User)ctx.Items["User"];
using var db = Db.Connect();
if (user == null)
return new HttpResponseMessage(HttpStatusCode.Unauthorized); return new HttpResponseMessage(HttpStatusCode.Unauthorized);
return db.GetAllAccessibleFolders(user).ToList(); using var db = Db.Connect();
return db
.GetAllAccessibleFolders(caller)
.ToList(); // important!
} }
[ProducesResponseType(200)] [Returns(HttpStatusCode.OK)]
[ProducesResponseType(401)] [Returns(HttpStatusCode.Unauthorized)]
[HttpPut($"{nameof(UpdateUser)}/")] [HttpPut($"{nameof(UpdateUser)}/")]
public Object UpdateUser(User updatedUser) public Object UpdateUser(User updatedUser)
{ {
var ctxAccessor = new HttpContextAccessor(); // TODO: distinguish between create and update
var ctx = ctxAccessor.HttpContext;
using var db = Db.Connect(); var caller = GetCaller();
var currentUser = (User)ctx.Items["User"]; if (caller == null)
if (currentUser == null || !currentUser.HasWriteAccess || !db.IsParentOfChild(currentUser, updatedUser))
return new HttpResponseMessage(HttpStatusCode.Unauthorized); return new HttpResponseMessage(HttpStatusCode.Unauthorized);
return db.GetUserById(updatedUser.Id) != null ? db.UpdateUser(updatedUser) : db.CreateUser(updatedUser); using var db = Db.Connect();
return db.GetUserById(updatedUser.Id) != null
? db.UpdateUser(updatedUser)
: db.CreateUser(updatedUser);
} }
[ProducesResponseType(200)]
[ProducesResponseType(401)] [Returns(HttpStatusCode.OK)]
[Returns(HttpStatusCode.Unauthorized)]
[HttpPut($"{nameof(UpdateInstallation)}/")] [HttpPut($"{nameof(UpdateInstallation)}/")]
public Object UpdateInstallation(Installation updatedInstallation) public Object UpdateInstallation(Installation updatedInstallation)
{ {
var ctxAccessor = new HttpContextAccessor(); var caller = GetCaller();
var ctx = ctxAccessor.HttpContext;
var currentUser = (User)ctx.Items["User"]; if (caller is null || !caller.HasWriteAccess)
if (currentUser == null || !currentUser.HasWriteAccess)
return new HttpResponseMessage(HttpStatusCode.Unauthorized); return new HttpResponseMessage(HttpStatusCode.Unauthorized);
using var db = Db.Connect(); using var db = Db.Connect();
var hasAccess = db.GetAllAccessibleInstallations(currentUser) var hasAccessToInstallation = db
.Any(i => i.Id == updatedInstallation.Id); .GetAllAccessibleInstallations(caller)
if (!hasAccess) .Any(i => i.Id == updatedInstallation.Id);
if (!hasAccessToInstallation)
return new HttpResponseMessage(HttpStatusCode.Unauthorized); return new HttpResponseMessage(HttpStatusCode.Unauthorized);
// TODO: accessibility by other users etc // TODO: accessibility by other users etc
@ -213,64 +207,68 @@ public class Controller
} }
[ProducesResponseType(200)] [Returns(HttpStatusCode.OK)]
[ProducesResponseType(401)] [Returns(HttpStatusCode.Unauthorized)]
[HttpPut($"{nameof(UpdateFolder)}/")] [HttpPut($"{nameof(UpdateFolder)}/")]
public Object UpdateFolder(Folder updatedFolder) public Object UpdateFolder(Folder folder)
{ {
var ctxAccessor = new HttpContextAccessor(); var caller = GetCaller();
var ctx = ctxAccessor.HttpContext;
using var db = Db.Connect(); if (caller is null || !caller.HasWriteAccess)
var currentUser = (User)ctx.Items["User"];
if (currentUser == null || !currentUser.HasWriteAccess)
return new HttpResponseMessage(HttpStatusCode.Unauthorized); return new HttpResponseMessage(HttpStatusCode.Unauthorized);
var hasAccess = db.GetAllAccessibleFolders(currentUser) using var db = Db.Connect();
.Any(f => f.Id == updatedFolder.Id);
var hasAccessToFolder = db
.GetAllAccessibleFolders(caller)
.Any(f => f.Id == folder.Id);
if (!hasAccess) if (!hasAccessToFolder)
return new HttpResponseMessage(HttpStatusCode.Unauthorized); return new HttpResponseMessage(HttpStatusCode.Unauthorized);
// TODO: accessibility by other users etc // TODO: accessibility by other users etc
// TODO: sanity check changes // TODO: sanity check changes
return db.UpdateFolder(updatedFolder); return db.UpdateFolder(folder);
} }
[ProducesResponseType(200)] [Returns(HttpStatusCode.OK)]
[ProducesResponseType(401)] [Returns(HttpStatusCode.Unauthorized)]
[HttpDelete($"{nameof(DeleteUser)}/")] [HttpDelete($"{nameof(DeleteUser)}/")]
public Object DeleteUser(Int64 userId) public Object DeleteUser(Int64 userId)
{ {
var ctxAccessor = new HttpContextAccessor(); var caller = GetCaller();
var ctx = ctxAccessor.HttpContext;
using var db = Db.Connect();
var currentUser = (User)ctx.Items["User"];
var userToBeDeleted = db.GetUserById(userId);
if (currentUser == null if (caller is null || !caller.HasWriteAccess)
|| userToBeDeleted == null return new HttpResponseMessage(HttpStatusCode.Unauthorized);
|| !currentUser.HasWriteAccess
|| !db.IsParentOfChild(currentUser,userToBeDeleted)) using var db = Db.Connect();
var userToBeDeleted = db
.GetDescendantUsers(caller)
.FirstOrDefault(u => u.Id == userId);
if (userToBeDeleted is null)
return new HttpResponseMessage(HttpStatusCode.Unauthorized); return new HttpResponseMessage(HttpStatusCode.Unauthorized);
return db.DeleteUser(userToBeDeleted); return db.DeleteUser(userToBeDeleted);
} }
[ProducesResponseType(200)] [Returns(HttpStatusCode.OK)]
[ProducesResponseType(401)] [Returns(HttpStatusCode.Unauthorized)]
[HttpDelete($"{nameof(DeleteInstallation)}/")] [HttpDelete($"{nameof(DeleteInstallation)}/")]
public Object DeleteInstallation(Int64 idOfInstallationToBeDeleted) public Object DeleteInstallation(Int64 installationId)
{ {
var ctxAccessor = new HttpContextAccessor(); var caller = GetCaller();
var ctx = ctxAccessor.HttpContext;
if (caller is null || !caller.HasWriteAccess)
return new HttpResponseMessage(HttpStatusCode.Unauthorized);
using var db = Db.Connect(); using var db = Db.Connect();
var currentUser = (User)ctx.Items["User"];
var installationToBeDeleted = db var installationToBeDeleted = db
.GetAllAccessibleInstallations(currentUser!) .GetAllAccessibleInstallations(caller)
.FirstOrDefault(i => i.Id == idOfInstallationToBeDeleted); .FirstOrDefault(i => i.Id == installationId);
if (installationToBeDeleted is null) if (installationToBeDeleted is null)
return new HttpResponseMessage(HttpStatusCode.Unauthorized); return new HttpResponseMessage(HttpStatusCode.Unauthorized);
@ -278,18 +276,20 @@ public class Controller
return db.DeleteInstallation(installationToBeDeleted); return db.DeleteInstallation(installationToBeDeleted);
} }
[ProducesResponseType(200)] [ProducesResponseType(200)]
[ProducesResponseType(401)] [ProducesResponseType(401)]
[HttpDelete($"{nameof(DeleteFolder)}/")] [HttpDelete($"{nameof(DeleteFolder)}/")]
public Object DeleteFolder(Int64 folderId) public Object DeleteFolder(Int64 folderId)
{ {
var ctxAccessor = new HttpContextAccessor(); var caller = GetCaller();
var ctx = ctxAccessor.HttpContext; if (caller == null)
return new HttpResponseMessage(HttpStatusCode.Unauthorized);
using var db = Db.Connect(); using var db = Db.Connect();
var currentUser = (User)ctx.Items["User"];
var folderToDelete = db var folderToDelete = db
.GetAllAccessibleFolders(currentUser!) .GetAllAccessibleFolders(caller)
.FirstOrDefault(f => f.Id == folderId); .FirstOrDefault(f => f.Id == folderId);
if (folderToDelete is null) if (folderToDelete is null)
@ -299,5 +299,22 @@ public class Controller
} }
private static User? GetCaller()
{
var ctxAccessor = new HttpContextAccessor();
return ctxAccessor.HttpContext?.Items["User"] as User;
}
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;
}
} }

View File

@ -1,3 +1,6 @@
namespace Backend.Controllers; using System.Diagnostics.CodeAnalysis;
namespace Innovenergy.Backend.Controllers;
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.All)]
public record Credentials(String Username, String Password); public record Credentials(String Username, String Password);

View File

@ -1,11 +1,11 @@
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using Backend.Model; using Innovenergy.Backend.Model;
using Backend.Model.Relations; using Innovenergy.Backend.Model.Relations;
using Backend.Utils; using Innovenergy.Backend.Utils;
using InnovEnergy.Lib.Utils; using InnovEnergy.Lib.Utils;
using SQLite; using SQLite;
namespace Backend.Database; namespace Innovenergy.Backend.Database;
public partial class Db : IDisposable public partial class Db : IDisposable
{ {
@ -97,6 +97,8 @@ public partial class Db : IDisposable
return direct.Concat(fromFolders); return direct.Concat(fromFolders);
} }
public IEnumerable<Folder> GetAllAccessibleFolders(User user) public IEnumerable<Folder> GetAllAccessibleFolders(User user)
{ {

View File

@ -1,6 +1,6 @@
using Backend.Model.Relations; using Innovenergy.Backend.Model.Relations;
namespace Backend.Database; namespace Innovenergy.Backend.Database;
public partial class Db public partial class Db
{ {

View File

@ -1,9 +1,9 @@
using Backend.Model; using Innovenergy.Backend.Model;
using Backend.Utils; using Innovenergy.Backend.Utils;
using InnovEnergy.Lib.Utils; using InnovEnergy.Lib.Utils;
using SQLite; using SQLite;
namespace Backend.Database; namespace Innovenergy.Backend.Database;
public partial class Db public partial class Db
{ {
@ -37,6 +37,15 @@ public partial class Db
return Installations.Where(f => f.ParentId == parent.Id); return Installations.Where(f => f.ParentId == parent.Id);
} }
public IEnumerable<User> GetChildUsers(User parent)
{
return Users.Where(f => f.ParentId == parent.Id);
}
public IEnumerable<User> GetDescendantUsers(User parent)
{
return parent.Traverse(GetChildUsers);
}
public Result CreateFolder(Folder folder) public Result CreateFolder(Folder folder)
{ {

View File

@ -1,8 +1,8 @@
using Backend.Model; using Innovenergy.Backend.Model;
using Backend.Utils; using Innovenergy.Backend.Utils;
using SQLite; using SQLite;
namespace Backend.Database; namespace Innovenergy.Backend.Database;
public partial class Db public partial class Db
{ {

View File

@ -1,18 +1,16 @@
using System.Net;
using System.Net.Mail; using System.Net.Mail;
using System.Security.Cryptography; using System.Security.Cryptography;
using System.Text; using System.Text;
using System.Text.Json;
using Backend.Model;
using Backend.Utils;
using Flurl.Http; using Flurl.Http;
using Innovenergy.Backend.Model;
using Innovenergy.Backend.Utils;
using InnovEnergy.Lib.Utils; using InnovEnergy.Lib.Utils;
using Microsoft.AspNetCore.DataProtection;
using SQLite; using SQLite;
#pragma warning disable CS0472 #pragma warning disable CS0472
#pragma warning disable CS8602 #pragma warning disable CS8602
namespace Backend.Database; namespace Innovenergy.Backend.Database;
public partial class Db public partial class Db
{ {
@ -22,16 +20,16 @@ public partial class Db
public User? GetUserById(Int64 id) public User? GetUserById(Int64 id)
{ {
return Users.FirstOrDefault(u => u.Id == id); return Users.FirstOrDefault(u => u.Id == id);
} }
public Boolean IsParentOfChild(User parent, User child) public Boolean IsParentOfChild(User parent, User child)
{ {
var parentPointer = child.ParentId; var parentPointer = child.ParentId;
if (parent.Id == child.Id) if (parent.Id == child.Id)
return true; return true;
while (parentPointer != null && parentPointer != parent.Id) while (parentPointer != null && parentPointer != parent.Id)
{ {
parentPointer = GetUserById(parentPointer).ParentId; parentPointer = GetUserById(parentPointer).ParentId;
@ -46,10 +44,10 @@ public partial class Db
{ {
if (GetUserByEmail(user.Email) is not null) if (GetUserByEmail(user.Email) is not null)
return Result.Error("User with that email already exists"); return Result.Error("User with that email already exists");
//Salting and Hashing password //Salting and Hashing password
var salt = Crypto.GenerateSalt(); var salt = Crypto.GenerateSalt();
var hashedPassword = Crypto.ComputeHash(Encoding.UTF8.GetBytes(user.Password), var hashedPassword = Crypto.ComputeHash(Encoding.UTF8.GetBytes(user.Password),
Encoding.UTF8.GetBytes(salt + "innovEnergy")); Encoding.UTF8.GetBytes(salt + "innovEnergy"));
user.Salt = salt; user.Salt = salt;
@ -57,49 +55,51 @@ public partial class Db
return Create(user); return Create(user);
} }
public Object CreateAndSaveUserS3ApiKey(User user) public Object CreateAndSaveUserS3ApiKey(User user)
{ {
//EXOSCALE API URL //EXOSCALE API URL
const String url = "https://api-ch-dk-2.exoscale.com/v2/access-key"; const String url = "https://api-ch-dk-2.exoscale.com/v2/access-key";
const String secret = "S2K1okphiCSNK4mzqr4swguFzngWAMb1OoSlZsJa9F0"; const String secret = "S2K1okphiCSNK4mzqr4swguFzngWAMb1OoSlZsJa9F0";
const String apiKey = "EXOb98ec9008e3ec16e19d7b593"; const String apiKey = "EXOb98ec9008e3ec16e19d7b593";
var payload = new var payload = new
{ name = user.Email, {
operations = new List<String> {"getObject", "listBucket"}, name = user.Email,
content = new List<Object>{}}; operations = new List<String> { "getObject", "listBucket" },
content = new List<Object> { }
};
var installationIdList = User2Installation var installationIdList = User2Installation
.Where(i => i.UserId == user.Id) .Where(i => i.UserId == user.Id)
.SelectMany(i => Installations.Where(f => i.InstallationId == f.Id)) .SelectMany(i => Installations.Where(f => i.InstallationId == f.Id))
.ToList(); .ToList();
foreach (var installation in installationIdList) foreach (var installation in installationIdList)
{ {
payload.content.Add(new {domain = "sos", resource_type = "bucket", resource_name = installation.Name}); //TODO CHANGE NAME TO S3BUCKET payload.content.Add(new { domain = "sos", resource_type = "bucket", resource_name = installation.Name }); //TODO CHANGE NAME TO S3BUCKET
} }
using var hmacSha1 = new HMACSHA1(Encoding.UTF8.GetBytes(secret)); using var hmacSha1 = new HMACSHA1(Encoding.UTF8.GetBytes(secret));
var signature = Encoding.UTF8 var signature = Encoding.UTF8
.GetBytes(payload.ToString()) .GetBytes(payload.ToString())
.Apply(hmacSha1.ComputeHash) .Apply(hmacSha1.ComputeHash)
.Apply(Convert.ToBase64String); .Apply(Convert.ToBase64String);
var keyJson = url var keyJson = url
.WithHeader("Authorization", $"POST {apiKey};{signature}") .WithHeader("Authorization", $"POST {apiKey};{signature}")
.PostJsonAsync(payload) .PostJsonAsync(payload)
.ReceiveJson() .ReceiveJson()
.Result; .Result;
return SetUserS3ApiKey(user, keyJson.GetValue("key")); return SetUserS3ApiKey(user, keyJson.GetValue("key"));
} }
public Result SetUserS3ApiKey(User user,String key) public Result SetUserS3ApiKey(User user, String key)
{ {
user.S3Key = key; user.S3Key = key;
return Update(user); return Update(user);
} }
public Result UpdateUser(User user) public Result UpdateUser(User user)
{ {
var oldUser = GetUserById(user.Id); var oldUser = GetUserById(user.Id);
@ -108,42 +108,39 @@ public partial class Db
//Checking for unchangeable things //Checking for unchangeable things
// TODO: depends on privileges of caller // TODO: depends on privileges of caller
user.Id = oldUser.Id; user.Id = oldUser.Id;
user.ParentId = oldUser.ParentId; user.ParentId = oldUser.ParentId;
user.Email = oldUser.Email; user.Email = oldUser.Email;
return Update(user); return Update(user);
} }
public Result DeleteUser(User user) public Result DeleteUser(User user)
{ {
User2Folder .Delete(u => u.UserId == user.Id); User2Folder.Delete(u => u.UserId == user.Id);
User2Installation.Delete(u => u.UserId == user.Id); User2Installation.Delete(u => u.UserId == user.Id);
//Todo check for orphaned Installations/Folders //Todo check for orphaned Installations/Folders
// GetChildUsers() // GetChildUsers()
return Delete(user); return Delete(user);
} }
// TODO // TODO
private static Boolean IsValidEmail(String email) private static Boolean IsValidEmail(String email)
{ {
try try
{ {
var emailAddress = new MailAddress(email); var emailAddress = new MailAddress(email);
} }
catch catch
{ {
return false; return false;
} }
return true; return true;
} }
}
}

View File

@ -1,7 +1,7 @@
using Backend.Model.Relations; using Innovenergy.Backend.Model.Relations;
using SQLite; using SQLite;
namespace Backend.Database; namespace Innovenergy.Backend.Database;
public partial class Db public partial class Db
{ {

View File

@ -1,7 +1,7 @@
using Backend.Model.Relations; using Innovenergy.Backend.Model.Relations;
using SQLite; using SQLite;
namespace Backend.Database; namespace Innovenergy.Backend.Database;
public partial class Db public partial class Db
{ {

View File

@ -0,0 +1,24 @@
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
namespace Innovenergy.Backend;
/// <summary>
/// This is for convenient testing! Todo throw me out?
/// Operation filter to add the requirement of the custom header
/// </summary>
public class HeaderFilter : IOperationFilter
{
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
operation.Parameters ??= new List<OpenApiParameter>();
operation.Parameters.Add(new OpenApiParameter
{
Name = "auth",
In = ParameterLocation.Header,
Content = new Dictionary<String, OpenApiMediaType>(),
Required = false
});
}
}

View File

@ -1,6 +1,4 @@
using SQLite; namespace Innovenergy.Backend.Model;
namespace Backend.Model;
public class Folder : TreeNode public class Folder : TreeNode
{ {

View File

@ -1,6 +1,4 @@
using SQLite; namespace Innovenergy.Backend.Model;
namespace Backend.Model;
public class Installation : TreeNode public class Installation : TreeNode

View File

@ -1,7 +1,7 @@
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using SQLite; using SQLite;
namespace Backend.Model.Relations; namespace Innovenergy.Backend.Model.Relations;
public abstract class Relation<L,R> public abstract class Relation<L,R>
{ {

View File

@ -1,6 +1,6 @@
using SQLite; using SQLite;
namespace Backend.Model.Relations; namespace Innovenergy.Backend.Model.Relations;
public class Session : Relation<String, Int64> public class Session : Relation<String, Int64>
{ {

View File

@ -1,6 +1,6 @@
using SQLite; using SQLite;
namespace Backend.Model.Relations; namespace Innovenergy.Backend.Model.Relations;
internal class User2Folder : Relation<Int64, Int64> internal class User2Folder : Relation<Int64, Int64>
{ {

View File

@ -1,6 +1,6 @@
using SQLite; using SQLite;
namespace Backend.Model.Relations; namespace Innovenergy.Backend.Model.Relations;
internal class User2Installation : Relation<Int64, Int64> internal class User2Installation : Relation<Int64, Int64>
{ {

View File

@ -1,7 +1,6 @@
using SQLite; using SQLite;
namespace Innovenergy.Backend.Model;
namespace Backend.Model;
public abstract class TreeNode public abstract class TreeNode
{ {

View File

@ -1,6 +1,6 @@
using SQLite; using SQLite;
namespace Backend.Model; namespace Innovenergy.Backend.Model;
public class User : TreeNode public class User : TreeNode
{ {

View File

@ -1,6 +1,6 @@
using System.Security.Cryptography; using System.Security.Cryptography;
namespace Backend.Utils; namespace Innovenergy.Backend.Utils;
public static class Crypto public static class Crypto
{ {

View File

@ -1,4 +1,4 @@
namespace Backend.Utils; namespace Innovenergy.Backend.Utils;
public class Result public class Result
{ {

Binary file not shown.

View File

@ -1,104 +1,59 @@
using Backend.Controllers; using Innovenergy.Backend.Database;
using Backend.Database;
using Microsoft.OpenApi.Models; using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
namespace Innovenergy.Backend;
using (var db = Db.Connect()) public class Program
{ {
db.CreateFakeRelations(); public static void Main(string[] args)
}
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers(); // TODO: remove magic, specify controllers explicitly
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddHttpContextAccessor();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddCors(o => o.AddDefaultPolicy(p => p.WithOrigins("*").AllowAnyHeader().AllowAnyMethod()));
builder.Services.AddSwaggerGen(config =>
{
config.SwaggerDoc("v1", new OpenApiInfo{ Title = "My API", Version = "V1" });
config.OperationFilter<MyHeaderFilter>(); //Todo testing throw me out
});
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI(cfg => cfg.EnableFilter());
}
app.UseCors();
app.UseHttpsRedirection();
app.UseAuthorization();
app.Use(SetSessionUser);
app.MapControllers();
app.Run();
//================= Functions for above ===================
//Setting User for current Session
async Task SetSessionUser(HttpContext ctx, RequestDelegate next)
{
var headers = ctx.Request.Headers;
var hasToken = headers.TryGetValue("auth", out var token);
if (!ctx.Request.Path.ToString().Contains(nameof(Controller.Login)))
{ {
if (!hasToken) using (var db = Db.Connect())
db.CreateFakeRelations();
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers(); // TODO: remove magic, specify controllers explicitly
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddHttpContextAccessor();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddCors(o => o.AddDefaultPolicy(p => p.WithOrigins("*").AllowAnyHeader().AllowAnyMethod()));
builder.Services.AddSwaggerGen(config =>
{ {
ctx.Response.StatusCode = 403; config.SwaggerDoc("v1", new OpenApiInfo{ Title = "My API", Version = "V1" });
return; config.OperationFilter<HeaderFilter>(); //Todo testing throw me out
}
using var db = Db.Connect();
var user = db.GetUserByToken(token.ToString());
if (user is null)
{
ctx.Response.StatusCode = 403;
return;
}
ctx.Items["User"] = user;
}
await next(ctx);
}
/// <summary>
/// This is for convenient testing! Todo throw me out?
/// Operation filter to add the requirement of the custom header
/// </summary>
public class MyHeaderFilter : IOperationFilter
{
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
operation.Parameters ??= new List<OpenApiParameter>();
operation.Parameters.Add(new OpenApiParameter
{
Name = "auth",
In = ParameterLocation.Header,
Content = new Dictionary<String, OpenApiMediaType>(),
Required = false
}); });
var app = builder.Build();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI(cfg => cfg.EnableFilter());
}
app.UseCors();
app.UseHttpsRedirection();
app.UseAuthorization();
app.Use(SetSessionUser);
app.MapControllers();
app.Run();
}
private static async Task SetSessionUser(HttpContext ctx, RequestDelegate next)
{
var headers = ctx.Request.Headers;
var hasToken = headers.TryGetValue("auth", out var token);
if (hasToken)
{
using var db = Db.Connect();
ctx.Items["User"] = db.GetUserByToken(token.ToString());
}
await next(ctx);
} }
} }