using System.Net; using System.Text; using System.Web.Http; using InnovEnergy.App.Backend.Database; using InnovEnergy.App.Backend.Model; using InnovEnergy.App.Backend.Model.Relations; using InnovEnergy.App.Backend.Utils; using Microsoft.AspNetCore.Mvc; using HttpContextAccessor = Microsoft.AspNetCore.Http.HttpContextAccessor; namespace InnovEnergy.App.Backend.Controllers; [ApiController] [Microsoft.AspNetCore.Mvc.Route("api/")] public class Controller { [Returns] [Returns(HttpStatusCode.Unauthorized)] [Returns(HttpStatusCode.BadRequest)] [Microsoft.AspNetCore.Mvc.HttpPost($"{nameof(Login)}")] public Object Login(Credentials credentials) { if (String.IsNullOrWhiteSpace(credentials.Username) || String.IsNullOrWhiteSpace(credentials.Password)) return new HttpResponseException(HttpStatusCode.BadRequest); using var db = Db.Connect(); var user = db.GetUserByEmail(credentials.Username); 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(HttpStatusCode.Unauthorized)] [Microsoft.AspNetCore.Mvc.HttpPost($"{nameof(Logout)}")] public Object Logout() { var caller = GetCaller(); if (caller is null) return new HttpResponseMessage(HttpStatusCode.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] [Returns(HttpStatusCode.Unauthorized)] [Microsoft.AspNetCore.Mvc.HttpGet($"{nameof(GetUserById)}")] public Object GetUserById(Int64 id) { var caller = GetCaller(); if (caller is null) return new HttpResponseMessage(HttpStatusCode.Unauthorized); using var db = Db.Connect(); var user = db .GetDescendantUsers(caller) .FirstOrDefault(u => u.Id == id); return user as Object ?? new HttpResponseMessage(HttpStatusCode.Unauthorized); } [Returns] [Returns(HttpStatusCode.Unauthorized)] [Microsoft.AspNetCore.Mvc.HttpGet($"{nameof(GetInstallationById)}")] public Object GetInstallationById(Int64 id) { var caller = GetCaller(); if (caller == null) return new HttpResponseMessage(HttpStatusCode.Unauthorized); using var db = Db.Connect(); var installation = db .GetAllAccessibleInstallations(caller) .FirstOrDefault(i => i.Id == id); return installation as Object ?? new HttpResponseMessage(HttpStatusCode.NotFound); } [Returns] [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] // assuming swagger knows about arrays but not lists (JSON) [Returns(HttpStatusCode.Unauthorized)] [Microsoft.AspNetCore.Mvc.HttpGet($"{nameof(GetAllInstallations)}/")] public Object GetAllInstallations() { var caller = GetCaller(); if (caller == null) return new HttpResponseMessage(HttpStatusCode.Unauthorized); using var db = Db.Connect(); return db .GetAllAccessibleInstallations(caller) .ToList(); // important! } [Returns] // assuming swagger knows about arrays but not lists (JSON) [Returns(HttpStatusCode.Unauthorized)] [Microsoft.AspNetCore.Mvc.HttpGet($"{nameof(GetAllFolders)}/")] public Object GetAllFolders() { var caller = GetCaller(); if (caller == null) return new HttpResponseMessage(HttpStatusCode.Unauthorized); using var db = Db.Connect(); return db .GetAllAccessibleFolders(caller) .ToList(); // important! } [Returns] // assuming swagger knows about arrays but not lists (JSON) [Returns(HttpStatusCode.Unauthorized)] [Microsoft.AspNetCore.Mvc.HttpGet($"{nameof(GetTree)}/")] public Object GetTree() { var caller = GetCaller(); if (caller == null) return new HttpResponseMessage(HttpStatusCode.Unauthorized); using var db = Db.Connect(); var folders = db .GetDirectlyAccessibleFolders(caller) // ReSharper disable once AccessToDisposedClosure .Select(f => PopulateChildren(db, f)); var installations = db.GetDirectlyAccessibleInstallations(caller); return folders .Concat(installations) .ToList(); // important! } [Returns] // assuming swagger knows about arrays but not lists (JSON) [Returns(HttpStatusCode.Unauthorized)] [Microsoft.AspNetCore.Mvc.HttpGet($"{nameof(GetAllFoldersAndInstallations)}/")] public Object GetAllFoldersAndInstallations() { var caller = GetCaller(); if (caller == null) return new HttpResponseMessage(HttpStatusCode.Unauthorized); using var db = Db.Connect(); var folders = db.GetAllAccessibleFolders(caller) as IEnumerable; var installations = db.GetAllAccessibleInstallations(caller); return folders .Concat(installations) .ToList(); // important! } private static Folder PopulateChildren(Db db, Folder folder, HashSet? hs = null) { // TODO: remove cycle detector hs ??= new HashSet(); 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(installations).ToList(); return folder; } [Returns(HttpStatusCode.OK)] [Returns(HttpStatusCode.Unauthorized)] [Microsoft.AspNetCore.Mvc.HttpPost($"{nameof(CreateUser)}/")] public Object CreateUser(User newUser) { var caller = GetCaller(); using var db = Db.Connect(); if (caller == null || !caller.HasWriteAccess) return new HttpResponseMessage(HttpStatusCode.Unauthorized); newUser.ParentId = caller.Id; return db.CreateUser(newUser); } [Returns(HttpStatusCode.OK)] [Returns(HttpStatusCode.Unauthorized)] [Microsoft.AspNetCore.Mvc.HttpPost($"{nameof(CreateInstallation)}/")] public Object CreateInstallation(Installation installation) { var caller = GetCaller(); 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); } [Returns(HttpStatusCode.OK)] [Returns(HttpStatusCode.Unauthorized)] [Microsoft.AspNetCore.Mvc.HttpPost($"{nameof(CreateFolder)}/")] public Object CreateFolder(Folder folder) { var caller = GetCaller(); 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); } [Returns(HttpStatusCode.OK)] [Returns(HttpStatusCode.Unauthorized)] [Microsoft.AspNetCore.Mvc.HttpPut($"{nameof(UpdateUser)}/")] public Object UpdateUser(User updatedUser) { var caller = GetCaller(); using var db = Db.Connect(); if (caller == null || !db.IsParentOfChild(caller.Id, updatedUser) || !caller.HasWriteAccess) return new HttpResponseMessage(HttpStatusCode.Unauthorized); return db.UpdateUser(updatedUser); } [Returns(HttpStatusCode.OK)] [Returns(HttpStatusCode.Unauthorized)] [Microsoft.AspNetCore.Mvc.HttpPut($"{nameof(UpdateInstallation)}/")] public Object UpdateInstallation(Installation installation) { var caller = GetCaller(); if (caller is null || !caller.HasWriteAccess) return new HttpResponseMessage(HttpStatusCode.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(HttpStatusCode.Unauthorized)] [Microsoft.AspNetCore.Mvc.HttpPut($"{nameof(UpdateFolder)}/")] public Object UpdateFolder(Folder folder) { var caller = GetCaller(); if (caller is null || !caller.HasWriteAccess) return new HttpResponseMessage(HttpStatusCode.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(HttpStatusCode.Unauthorized)] [Microsoft.AspNetCore.Mvc.HttpDelete($"{nameof(DeleteUser)}/")] public Object DeleteUser(Int64 userId) { var caller = GetCaller(); 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 new HttpResponseMessage(HttpStatusCode.Unauthorized); return db.DeleteUser(userToBeDeleted); } [Returns(HttpStatusCode.OK)] [Returns(HttpStatusCode.Unauthorized)] [Microsoft.AspNetCore.Mvc.HttpDelete($"{nameof(DeleteInstallation)}/")] public Object DeleteInstallation(Int64 installationId) { var caller = GetCaller(); if (caller is null || !caller.HasWriteAccess) return new HttpResponseMessage(HttpStatusCode.Unauthorized); using var db = Db.Connect(); var installationToBeDeleted = db .GetAllAccessibleInstallations(caller) .FirstOrDefault(i => i.Id == installationId); if (installationToBeDeleted is null) return new HttpResponseMessage(HttpStatusCode.Unauthorized); return db.DeleteInstallation(installationToBeDeleted); } [ProducesResponseType(200)] [ProducesResponseType(401)] [Microsoft.AspNetCore.Mvc.HttpDelete($"{nameof(DeleteFolder)}/")] public Object DeleteFolder(Int64 folderId) { var caller = GetCaller(); if (caller == null) return new HttpResponseMessage(HttpStatusCode.Unauthorized); using var db = Db.Connect(); var folderToDelete = db .GetAllAccessibleFolders(caller) .FirstOrDefault(f => f.Id == folderId); if (folderToDelete is null) return new HttpResponseMessage(HttpStatusCode.Unauthorized); return db.DeleteFolder(folderToDelete); } 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; } }