Initial commit
This commit is contained in:
1
Projekte/WebDesktop 2.0
Submodule
1
Projekte/WebDesktop 2.0
Submodule
Submodule Projekte/WebDesktop 2.0 added at 15f48d259f
3
Projekte/WebDesktop/.gitignore
vendored
Normal file
3
Projekte/WebDesktop/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
.vs
|
||||
.vscode
|
||||
UploadData
|
||||
13
Projekte/WebDesktop/.idea/.idea.WebDesktop/.idea/.gitignore
generated
vendored
Normal file
13
Projekte/WebDesktop/.idea/.idea.WebDesktop/.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Rider ignored files
|
||||
/contentModel.xml
|
||||
/projectSettingsUpdater.xml
|
||||
/.idea.WebDesktop.iml
|
||||
/modules.xml
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
12
Projekte/WebDesktop/.idea/.idea.WebDesktop/.idea/dataSources.xml
generated
Normal file
12
Projekte/WebDesktop/.idea/.idea.WebDesktop/.idea/dataSources.xml
generated
Normal file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
|
||||
<data-source source="LOCAL" name="WebDesktop" uuid="e24e2ab8-b675-4c92-a735-b4c27afafc3f">
|
||||
<driver-ref>mariadb</driver-ref>
|
||||
<synchronize>true</synchronize>
|
||||
<jdbc-driver>org.mariadb.jdbc.Driver</jdbc-driver>
|
||||
<jdbc-url>jdbc:mariadb://leon-hoppe.de:3306/WebDesktop</jdbc-url>
|
||||
<working-dir>$ProjectFileDir$</working-dir>
|
||||
</data-source>
|
||||
</component>
|
||||
</project>
|
||||
6
Projekte/WebDesktop/.idea/.idea.WebDesktop/.idea/dictionaries
generated
Normal file
6
Projekte/WebDesktop/.idea/.idea.WebDesktop/.idea/dictionaries
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectDictionaryState">
|
||||
<dictionary name="leon" />
|
||||
</component>
|
||||
</project>
|
||||
7
Projekte/WebDesktop/.idea/.idea.WebDesktop/.idea/discord.xml
generated
Normal file
7
Projekte/WebDesktop/.idea/.idea.WebDesktop/.idea/discord.xml
generated
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="DiscordProjectSettings">
|
||||
<option name="show" value="PROJECT_FILES" />
|
||||
<option name="description" value="" />
|
||||
</component>
|
||||
</project>
|
||||
4
Projekte/WebDesktop/.idea/.idea.WebDesktop/.idea/encodings.xml
generated
Normal file
4
Projekte/WebDesktop/.idea/.idea.WebDesktop/.idea/encodings.xml
generated
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Encoding" addBOMForNewFiles="with BOM under Windows, with no BOM otherwise" />
|
||||
</project>
|
||||
11
Projekte/WebDesktop/.idea/.idea.WebDesktop/.idea/indexLayout.xml
generated
Normal file
11
Projekte/WebDesktop/.idea/.idea.WebDesktop/.idea/indexLayout.xml
generated
Normal file
@@ -0,0 +1,11 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="UserContentModel">
|
||||
<attachedFolders>
|
||||
<Path>WebDesktopFrontend</Path>
|
||||
<Path>WindowAPI</Path>
|
||||
</attachedFolders>
|
||||
<explicitIncludes />
|
||||
<explicitExcludes />
|
||||
</component>
|
||||
</project>
|
||||
9
Projekte/WebDesktop/.idea/.idea.WebDesktop/.idea/markdown.xml
generated
Normal file
9
Projekte/WebDesktop/.idea/.idea.WebDesktop/.idea/markdown.xml
generated
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="MarkdownSettings">
|
||||
<enabledExtensions>
|
||||
<entry key="MermaidLanguageExtension" value="false" />
|
||||
<entry key="PlantUMLLanguageExtension" value="false" />
|
||||
</enabledExtensions>
|
||||
</component>
|
||||
</project>
|
||||
6
Projekte/WebDesktop/.idea/.idea.WebDesktop/.idea/misc.xml
generated
Normal file
6
Projekte/WebDesktop/.idea/.idea.WebDesktop/.idea/misc.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="com.jetbrains.rider.android.RiderAndroidMiscFileCreationComponent">
|
||||
<option name="ENSURE_MISC_FILE_EXISTS" value="true" />
|
||||
</component>
|
||||
</project>
|
||||
6
Projekte/WebDesktop/.idea/.idea.WebDesktop/.idea/sqldialects.xml
generated
Normal file
6
Projekte/WebDesktop/.idea/.idea.WebDesktop/.idea/sqldialects.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="SqlDialectMappings">
|
||||
<file url="file://$PROJECT_DIR$/WebDesktopBackend/Startup.cs" dialect="GenericSQL" />
|
||||
</component>
|
||||
</project>
|
||||
6
Projekte/WebDesktop/.idea/.idea.WebDesktop/.idea/vcs.xml
generated
Normal file
6
Projekte/WebDesktop/.idea/.idea.WebDesktop/.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
4
Projekte/WebDesktop/.idea/.idea.WebDesktop/.idea/watcherTasks.xml
generated
Normal file
4
Projekte/WebDesktop/.idea/.idea.WebDesktop/.idea/watcherTasks.xml
generated
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectTasksOptions" suppressed-tasks="SCSS" />
|
||||
</project>
|
||||
BIN
Projekte/WebDesktop/Desktop layout.png
Normal file
BIN
Projekte/WebDesktop/Desktop layout.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 485 KiB |
16
Projekte/WebDesktop/WebDesktop.sln
Normal file
16
Projekte/WebDesktop/WebDesktop.sln
Normal file
@@ -0,0 +1,16 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WebDesktopBackend", "WebDesktopBackend/WebDesktopBackend.csproj", "{ECE6CBFE-02CC-4A54-BE36-57226B1B2357}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{ECE6CBFE-02CC-4A54-BE36-57226B1B2357}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{ECE6CBFE-02CC-4A54-BE36-57226B1B2357}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{ECE6CBFE-02CC-4A54-BE36-57226B1B2357}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{ECE6CBFE-02CC-4A54-BE36-57226B1B2357}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
25
Projekte/WebDesktop/WebDesktopBackend/.dockerignore
Normal file
25
Projekte/WebDesktop/WebDesktopBackend/.dockerignore
Normal file
@@ -0,0 +1,25 @@
|
||||
**/.dockerignore
|
||||
**/.env
|
||||
**/.git
|
||||
**/.gitignore
|
||||
**/.project
|
||||
**/.settings
|
||||
**/.toolstarget
|
||||
**/.vs
|
||||
**/.vscode
|
||||
**/.idea
|
||||
**/*.*proj.user
|
||||
**/*.dbmdl
|
||||
**/*.jfm
|
||||
**/azds.yaml
|
||||
**/bin
|
||||
**/charts
|
||||
**/docker-compose*
|
||||
**/Dockerfile*
|
||||
**/node_modules
|
||||
**/npm-debug.log
|
||||
**/obj
|
||||
**/secrets.dev.yaml
|
||||
**/values.dev.yaml
|
||||
LICENSE
|
||||
README.md
|
||||
3
Projekte/WebDesktop/WebDesktopBackend/.gitignore
vendored
Normal file
3
Projekte/WebDesktop/WebDesktopBackend/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
/obj
|
||||
/bin
|
||||
/Uploads
|
||||
13
Projekte/WebDesktop/WebDesktopBackend/.idea/.idea.WebDesktopBackend/.idea/.gitignore
generated
vendored
Normal file
13
Projekte/WebDesktop/WebDesktopBackend/.idea/.idea.WebDesktopBackend/.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Rider ignored files
|
||||
/.idea.WebDesktopBackend.iml
|
||||
/modules.xml
|
||||
/contentModel.xml
|
||||
/projectSettingsUpdater.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
||||
12
Projekte/WebDesktop/WebDesktopBackend/.idea/.idea.WebDesktopBackend/.idea/dataSources.xml
generated
Normal file
12
Projekte/WebDesktop/WebDesktopBackend/.idea/.idea.WebDesktopBackend/.idea/dataSources.xml
generated
Normal file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="DataSourceManagerImpl" format="xml" multifile-model="true">
|
||||
<data-source source="LOCAL" name="WebDesktop@213.136.89.237" uuid="7571b012-f7f2-4a6f-89f5-9a493192589b">
|
||||
<driver-ref>mariadb</driver-ref>
|
||||
<synchronize>true</synchronize>
|
||||
<jdbc-driver>org.mariadb.jdbc.Driver</jdbc-driver>
|
||||
<jdbc-url>jdbc:mariadb://213.136.89.237:3306/WebDesktop</jdbc-url>
|
||||
<working-dir>$ProjectFileDir$</working-dir>
|
||||
</data-source>
|
||||
</component>
|
||||
</project>
|
||||
7
Projekte/WebDesktop/WebDesktopBackend/.idea/.idea.WebDesktopBackend/.idea/discord.xml
generated
Normal file
7
Projekte/WebDesktop/WebDesktopBackend/.idea/.idea.WebDesktopBackend/.idea/discord.xml
generated
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="DiscordProjectSettings">
|
||||
<option name="show" value="PROJECT_FILES" />
|
||||
<option name="description" value="" />
|
||||
</component>
|
||||
</project>
|
||||
4
Projekte/WebDesktop/WebDesktopBackend/.idea/.idea.WebDesktopBackend/.idea/encodings.xml
generated
Normal file
4
Projekte/WebDesktop/WebDesktopBackend/.idea/.idea.WebDesktopBackend/.idea/encodings.xml
generated
Normal file
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Encoding" addBOMForNewFiles="with BOM under Windows, with no BOM otherwise" />
|
||||
</project>
|
||||
8
Projekte/WebDesktop/WebDesktopBackend/.idea/.idea.WebDesktopBackend/.idea/indexLayout.xml
generated
Normal file
8
Projekte/WebDesktop/WebDesktopBackend/.idea/.idea.WebDesktopBackend/.idea/indexLayout.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="UserContentModel">
|
||||
<attachedFolders />
|
||||
<explicitIncludes />
|
||||
<explicitExcludes />
|
||||
</component>
|
||||
</project>
|
||||
8
Projekte/WebDesktop/WebDesktopBackend/.idea/.idea.WebDesktopBackend/.idea/sqldialects.xml
generated
Normal file
8
Projekte/WebDesktop/WebDesktopBackend/.idea/.idea.WebDesktopBackend/.idea/sqldialects.xml
generated
Normal file
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="SqlDialectMappings">
|
||||
<file url="file://$PROJECT_DIR$/Persistance/TokenRepository.cs" dialect="GenericSQL" />
|
||||
<file url="file://$PROJECT_DIR$/Persistance/UserRepository.cs" dialect="GenericSQL" />
|
||||
<file url="file://$PROJECT_DIR$/Startup.cs" dialect="GenericSQL" />
|
||||
</component>
|
||||
</project>
|
||||
6
Projekte/WebDesktop/WebDesktopBackend/.idea/.idea.WebDesktopBackend/.idea/vcs.xml
generated
Normal file
6
Projekte/WebDesktop/WebDesktopBackend/.idea/.idea.WebDesktopBackend/.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$/.." vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
@@ -0,0 +1,23 @@
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using WebDesktopBackend.Entitys.Files;
|
||||
using WebDesktopBackend.LogicResults;
|
||||
using FileShare = WebDesktopBackend.Entitys.Files.FileShare;
|
||||
|
||||
namespace WebDesktopBackend.Contract.Logic {
|
||||
public interface IFileLogic {
|
||||
ILogicResult CreateDirectory(string directory, string name);
|
||||
Task<ILogicResult> UploadFile(IFormCollection data);
|
||||
Task<ILogicResult> UploadJson(string directory, string name, string content);
|
||||
ILogicResult<FileStream> DownloadFile(string directory, string file);
|
||||
Task<ILogicResult<string>> DownloadJson(string file);
|
||||
ILogicResult<DirectoryContent> GetDirectory(string directory);
|
||||
ILogicResult<DirectoryInformation> GetDirectoryInformation(string directory);
|
||||
ILogicResult<FileInformation> GetFileInformation(string directory, string file);
|
||||
ILogicResult MoveDirectory(string directory, string name, string to);
|
||||
ILogicResult MoveFile(string directory, string file, string to);
|
||||
ILogicResult Delete(string url);
|
||||
ILogicResult<FileShare> Share(string url);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using WebDesktopBackend.Entitys.Tokens;
|
||||
using WebDesktopBackend.Entitys.User;
|
||||
using WebDesktopBackend.LogicResults;
|
||||
|
||||
namespace WebDesktopBackend.Contract.Logic {
|
||||
public interface IUserLogic {
|
||||
ILogicResult<Tokens> Login(UserLogin login);
|
||||
ILogicResult<Tokens> Register(UserEditor editor);
|
||||
ILogicResult Logout();
|
||||
ILogicResult EditUser(string id, UserEditor editor);
|
||||
ILogicResult DeleteUser(string id);
|
||||
ILogicResult<User> GetUser(string id);
|
||||
ILogicResult<User[]> GetUsers();
|
||||
ILogicResult Valdiate();
|
||||
ILogicResult<AccessTokenResponse> GetToken(string refreshTokenId);
|
||||
ILogicResult<User> GetOwnUser();
|
||||
ILogicResult<string[]> GetPermissions(string id);
|
||||
ILogicResult<string[]> GetRawPermissions(string id);
|
||||
ILogicResult AddPermission(string id, string permission);
|
||||
ILogicResult DeletePermission(string id, string permission);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace WebDesktopBackend.Contract {
|
||||
public class Permissions {
|
||||
// --- USERS ---
|
||||
public const string ShowUsers = "users.show";
|
||||
public const string EditUsers = "users.edit";
|
||||
public const string DeleteUsers = "users.delete";
|
||||
public const string EditUserPermissions = "users.permissions";
|
||||
|
||||
// --- PROGRAMS ---
|
||||
public const string UseAdminProgram = "app.admin.use";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using WebDesktopBackend.Entitys.Files;
|
||||
|
||||
namespace WebDesktopBackend.Contract.Persistance {
|
||||
public interface IFileRepository {
|
||||
void InitUser(string userId);
|
||||
void DeleteUserFolder(string userId);
|
||||
bool CreateDirectory(string directory, string name);
|
||||
Task UploadFile(IFormFile file, string directory);
|
||||
Task UploadJson(string directory, string name, string data);
|
||||
FileStream DownloadFile(string path);
|
||||
Task<string> DownloadJson(string file);
|
||||
DirectoryContent GetDirectory(string directory);
|
||||
DirectoryInformation GetDirectoryInformation(string directory);
|
||||
FileInformation GetFileInformation(string file);
|
||||
void MoveDirectory(string directory, string to);
|
||||
void MoveFile(string file, string to);
|
||||
void Delete(string url);
|
||||
string GenerateShareId(string url, string owner);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
using WebDesktopBackend.Entitys.Permissions;
|
||||
|
||||
namespace WebDesktopBackend.Contract.Persistance {
|
||||
public interface IGroupRepository {
|
||||
PermissionGroup GetPermissionGroup(string name);
|
||||
PermissionGroup[] GetGroupsFromUser(string userId);
|
||||
PermissionGroup[] ExtractGroups(Permission[] permissions);
|
||||
Permission[] GetUserPermissions(string id);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using WebDesktopBackend.Entitys.Permissions;
|
||||
using WebDesktopBackend.Entitys.Tokens;
|
||||
|
||||
namespace WebDesktopBackend.Contract.Persistance {
|
||||
public interface ITokenRepository {
|
||||
RefreshToken GetRefreshToken(string id);
|
||||
AccessToken GetAccessToken(string id);
|
||||
bool ValidateAccessToken(string id);
|
||||
bool ValidateRefreshToken(string id);
|
||||
RefreshToken CreateRefreshToken(string userId);
|
||||
AccessToken CreateAccessToken(string refreshTokenId);
|
||||
void DeleteUserTokens(string id);
|
||||
void DeleteRefreshToken(string id);
|
||||
Permission[] GetUserPermissions(string id);
|
||||
void AddPermission(string id, string permission);
|
||||
void DeletePermission(string id, string permission);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
using WebDesktopBackend.Entitys.User;
|
||||
|
||||
namespace WebDesktopBackend.Contract.Persistance {
|
||||
public interface IUserRepository {
|
||||
User AddUser(UserEditor editor);
|
||||
void EditUser(string id, UserEditor editor);
|
||||
void DeleteUser(string id);
|
||||
User GetUser(string id);
|
||||
User GetUserByUsername(string username);
|
||||
User GetUserByEmail(string email);
|
||||
User GetUserFromLogin(UserLogin login);
|
||||
User[] GetUsers();
|
||||
bool Login(UserLogin login);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using WebDesktopBackend.Contract.Logic;
|
||||
using WebDesktopBackend.Entitys.Files;
|
||||
using WebDesktopBackend.LogicResults;
|
||||
using WebDesktopBackend.Security.Authorization;
|
||||
using FileShare = WebDesktopBackend.Entitys.Files.FileShare;
|
||||
|
||||
namespace WebDesktopBackend.Controller {
|
||||
[ApiController]
|
||||
[Route("files")]
|
||||
public class FileController : ControllerBase {
|
||||
private readonly IFileLogic _fileLogic;
|
||||
|
||||
public FileController(IFileLogic fileLogic) {
|
||||
_fileLogic = fileLogic;
|
||||
}
|
||||
|
||||
[HttpPost("upload/directory")]
|
||||
[Authorized]
|
||||
public ActionResult CreateDirectory([FromQuery] string directory, [FromQuery] string name) {
|
||||
return this.FromLogicResult(_fileLogic.CreateDirectory(directory, name));
|
||||
}
|
||||
|
||||
[HttpPost("upload/file")]
|
||||
[Authorized]
|
||||
[DisableRequestSizeLimit]
|
||||
public async Task<ActionResult> UploadFile() {
|
||||
try {
|
||||
return this.FromLogicResult(await _fileLogic.UploadFile(Request.Form));
|
||||
} catch (Exception) {
|
||||
return StatusCode((int)HttpStatusCode.BadRequest, "File upload Interupted");
|
||||
}
|
||||
}
|
||||
|
||||
[HttpPost("upload/json")]
|
||||
[Authorized]
|
||||
[DisableRequestSizeLimit]
|
||||
public async Task<ActionResult> UploadJson([FromQuery] string directory, [FromQuery] string name) {
|
||||
using var reader = new StreamReader(Request.Body, Encoding.UTF8);
|
||||
string content = await reader.ReadToEndAsync();
|
||||
return this.FromLogicResult(await _fileLogic.UploadJson(directory, name, content));
|
||||
}
|
||||
|
||||
[HttpGet("download/file")]
|
||||
[Authorized]
|
||||
public IActionResult DownloadFile([FromQuery] string directory, [FromQuery] string file) {
|
||||
var result = _fileLogic.DownloadFile(directory, file);
|
||||
if (!result.IsSuccessful)
|
||||
return this.FromLogicResult(result);
|
||||
|
||||
return File(result.Data, "APPLICATION/octet-stream", file);
|
||||
}
|
||||
|
||||
[HttpGet("download/json")]
|
||||
[Authorized]
|
||||
public async Task<ActionResult<string>> DownloadJson([FromQuery] string file) {
|
||||
return this.FromLogicResult(await _fileLogic.DownloadJson(file));
|
||||
}
|
||||
|
||||
[HttpGet("content")]
|
||||
[Authorized]
|
||||
public ActionResult<DirectoryContent> GetDirectoryContent([FromQuery] string directory) {
|
||||
return this.FromLogicResult(_fileLogic.GetDirectory(directory));
|
||||
}
|
||||
|
||||
[HttpGet("info/directory")]
|
||||
[Authorized]
|
||||
public ActionResult<DirectoryInformation> GetDirectoryInformation([FromQuery] string directory) {
|
||||
return this.FromLogicResult(_fileLogic.GetDirectoryInformation(directory));
|
||||
}
|
||||
|
||||
[HttpGet("info/file")]
|
||||
[Authorized]
|
||||
public ActionResult<DirectoryInformation> GetFileInformation([FromQuery] string directory, [FromQuery] string file) {
|
||||
return this.FromLogicResult(_fileLogic.GetFileInformation(directory, file));
|
||||
}
|
||||
|
||||
[HttpPut("move/directory")]
|
||||
[Authorized]
|
||||
public ActionResult MoveDirectory([FromQuery] string directory, [FromQuery] string name, [FromQuery] string to) {
|
||||
return this.FromLogicResult(_fileLogic.MoveDirectory(directory, name, to));
|
||||
}
|
||||
|
||||
[HttpPut("move/file")]
|
||||
[Authorized]
|
||||
public ActionResult MoveFile([FromQuery] string directory, [FromQuery] string file, [FromQuery] string to) {
|
||||
return this.FromLogicResult(_fileLogic.MoveFile(directory, file, to));
|
||||
}
|
||||
|
||||
[HttpDelete("delete")]
|
||||
[Authorized]
|
||||
public ActionResult DeleteFile([FromQuery] string url) {
|
||||
return this.FromLogicResult(_fileLogic.Delete(url));
|
||||
}
|
||||
|
||||
[HttpGet("share")]
|
||||
[Authorized]
|
||||
public ActionResult<FileShare> ShareFile([FromQuery] string url) {
|
||||
return this.FromLogicResult(_fileLogic.Share(url));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
using System;
|
||||
using System.Net.WebSockets;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using WebDesktopBackend.Extentions;
|
||||
using WebDesktopBackend.Security.Authorization;
|
||||
|
||||
namespace WebDesktopBackend.Controller {
|
||||
|
||||
[ApiController]
|
||||
[Route("update")]
|
||||
public class UpdateController : ControllerBase {
|
||||
|
||||
[HttpGet("test")]
|
||||
public ActionResult Test() {
|
||||
return Ok("Authorized");
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[Authorized("group.admin")]
|
||||
public async Task Update() {
|
||||
if (HttpContext.WebSockets.IsWebSocketRequest) {
|
||||
using var socket = await HttpContext.WebSockets.AcceptWebSocketAsync();
|
||||
using var target = await new ClientWebSocket().ConnectAsync(new Uri("ws://213.136.89.237:4042"));
|
||||
|
||||
var t1 = socket.AddMessageEventHandler(msg => {
|
||||
target.SendMessage(msg);
|
||||
});
|
||||
var t2 = target.AddMessageEventHandler(msg => {
|
||||
socket.SendMessage(msg);
|
||||
});
|
||||
|
||||
while (!socket.CloseStatus.HasValue) {
|
||||
await Task.Delay(500);
|
||||
}
|
||||
|
||||
t1.Cancel();
|
||||
t2.Cancel();
|
||||
await target.CloseAsync(WebSocketCloseStatus.NormalClosure, null, CancellationToken.None);
|
||||
} else {
|
||||
HttpContext.Response.StatusCode = StatusCodes.Status400BadRequest;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
using System;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using WebDesktopBackend.Contract;
|
||||
using WebDesktopBackend.Contract.Logic;
|
||||
using WebDesktopBackend.Entitys.Tokens;
|
||||
using WebDesktopBackend.Entitys.User;
|
||||
using WebDesktopBackend.LogicResults;
|
||||
using WebDesktopBackend.Security;
|
||||
using WebDesktopBackend.Security.Authorization;
|
||||
|
||||
namespace WebDesktopBackend.Controller {
|
||||
[ApiController]
|
||||
[Route("users")]
|
||||
public class UserController : ControllerBase {
|
||||
private readonly IUserLogic _logic;
|
||||
private readonly ITokenContext _context;
|
||||
|
||||
public UserController(IUserLogic logic, ITokenContext context) {
|
||||
_logic = logic;
|
||||
_context = context;
|
||||
}
|
||||
|
||||
[HttpPut("login")]
|
||||
public ActionResult<AccessTokenResponse> Login([FromBody] UserLogin login) {
|
||||
ILogicResult<Tokens> result = _logic.Login(login);
|
||||
if (result.State == LogicResultState.Ok) SetRefreshToken(result.Data.refreshToken);
|
||||
return this.FromLogicResult(new LogicResult<AccessTokenResponse> {State = result.State, Data = new AccessTokenResponse {Id = result.Data?.accessToken.Id}});
|
||||
}
|
||||
|
||||
[HttpPost("register")]
|
||||
public ActionResult<AccessTokenResponse> Register([FromBody] UserEditor editor) {
|
||||
ILogicResult<Tokens> result = _logic.Register(editor);
|
||||
SetRefreshToken(result.Data.refreshToken);
|
||||
return this.FromLogicResult(new LogicResult<AccessTokenResponse> {State = result.State, Data = new AccessTokenResponse {Id = result.Data.accessToken.Id}});
|
||||
}
|
||||
|
||||
[HttpDelete("logout")]
|
||||
[Authorized]
|
||||
public ActionResult Logout() {
|
||||
DeleteRefreshToken();
|
||||
return this.FromLogicResult(_logic.Logout());
|
||||
}
|
||||
|
||||
[HttpPut("{id}")]
|
||||
[Authorized(Permissions.EditUsers)]
|
||||
public ActionResult EditUser(string id, [FromBody] UserEditor editor) {
|
||||
return this.FromLogicResult(_logic.EditUser(id, editor));
|
||||
}
|
||||
|
||||
[HttpDelete("{id}")]
|
||||
[Authorized(Permissions.DeleteUsers)]
|
||||
public ActionResult DeleteUser(string id) {
|
||||
return this.FromLogicResult(_logic.DeleteUser(id));
|
||||
}
|
||||
|
||||
[HttpGet("{id}")]
|
||||
[Authorized(Permissions.ShowUsers)]
|
||||
public ActionResult<User> GetUser(string id) {
|
||||
return this.FromLogicResult(_logic.GetUser(id));
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
[Authorized(Permissions.ShowUsers)]
|
||||
public ActionResult<User[]> GetUsers() {
|
||||
return this.FromLogicResult(_logic.GetUsers());
|
||||
}
|
||||
|
||||
[HttpGet("validate")]
|
||||
[Authorized]
|
||||
public ActionResult Validate() {
|
||||
return this.FromLogicResult(_logic.Valdiate());
|
||||
}
|
||||
|
||||
[HttpGet("token")]
|
||||
public ActionResult<AccessTokenResponse> GetToken() {
|
||||
return this.FromLogicResult(_logic.GetToken(GetRefreshToken()));
|
||||
}
|
||||
|
||||
[HttpGet("ownuser")]
|
||||
[Authorized]
|
||||
public ActionResult<User> GetOwnUser() {
|
||||
return this.FromLogicResult(_logic.GetOwnUser());
|
||||
}
|
||||
|
||||
[HttpPut("ownuser")]
|
||||
[Authorized]
|
||||
public ActionResult<User> EditOwnUser([FromBody] UserEditor editor) {
|
||||
return this.FromLogicResult(_logic.EditUser(_context.UserId, editor));
|
||||
}
|
||||
|
||||
[HttpDelete("ownuser")]
|
||||
[Authorized]
|
||||
public ActionResult<User> DeleteOwnUser() {
|
||||
Logout();
|
||||
return this.FromLogicResult(_logic.DeleteUser(_context.UserId));
|
||||
}
|
||||
|
||||
[HttpGet("{id}/permissions")]
|
||||
[Authorized(Permissions.EditUserPermissions)]
|
||||
public ActionResult<string[]> GetPermissions(string id) {
|
||||
return this.FromLogicResult(_logic.GetPermissions(id));
|
||||
}
|
||||
|
||||
[HttpGet("{id}/permissions/raw")]
|
||||
[Authorized(Permissions.EditUserPermissions)]
|
||||
public ActionResult<string[]> GetRawPermissions(string id) {
|
||||
return this.FromLogicResult(_logic.GetRawPermissions(id));
|
||||
}
|
||||
|
||||
[HttpGet("permissions")]
|
||||
[Authorized]
|
||||
public ActionResult<string[]> GetPermissions() {
|
||||
return this.FromLogicResult(_logic.GetPermissions(HttpContext.User.GetUserId()));
|
||||
}
|
||||
|
||||
[HttpPost("{id}/permissions/{permission}")]
|
||||
[Authorized(Permissions.EditUserPermissions)]
|
||||
public ActionResult AddPermission(string id, string permission) {
|
||||
return this.FromLogicResult(_logic.AddPermission(id, permission));
|
||||
}
|
||||
|
||||
[HttpDelete("{id}/permissions/{permission}")]
|
||||
[Authorized(Permissions.EditUserPermissions)]
|
||||
public ActionResult DeletePermission(string id, string permission) {
|
||||
return this.FromLogicResult(_logic.DeletePermission(id, permission));
|
||||
}
|
||||
|
||||
private void DeleteRefreshToken()
|
||||
{
|
||||
HttpContext.Response.Cookies.Delete("refresh_token");
|
||||
}
|
||||
private void SetRefreshToken(RefreshToken token)
|
||||
{
|
||||
HttpContext.Response.Cookies.Append("refresh_token", token.Id, new CookieOptions()
|
||||
{
|
||||
MaxAge = token.ExpirationDate - DateTime.Now,
|
||||
HttpOnly = true,
|
||||
Secure = true
|
||||
});
|
||||
}
|
||||
|
||||
private string GetRefreshToken() {
|
||||
return HttpContext.Request.Cookies["refresh_token"];
|
||||
}
|
||||
}
|
||||
}
|
||||
62
Projekte/WebDesktop/WebDesktopBackend/DatabaseContext.cs
Normal file
62
Projekte/WebDesktop/WebDesktopBackend/DatabaseContext.cs
Normal file
@@ -0,0 +1,62 @@
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using WebDesktopBackend.Entitys.Files;
|
||||
using WebDesktopBackend.Entitys.Permissions;
|
||||
using WebDesktopBackend.Entitys.Tokens;
|
||||
using WebDesktopBackend.Entitys.User;
|
||||
|
||||
namespace WebDesktopBackend {
|
||||
public class DatabaseContext : DbContext {
|
||||
|
||||
public DbSet<User> Users { get; set; }
|
||||
public DbSet<RefreshToken> RefreshTokens { get; set; }
|
||||
public DbSet<AccessToken> AccessTokens { get; set; }
|
||||
public DbSet<Permission> Permissions { get; set; }
|
||||
|
||||
public DbSet<FileShare> FileShares { get; set; }
|
||||
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) {
|
||||
optionsBuilder.UseMySQL(MySql.ConnectionString);
|
||||
}
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder) {
|
||||
base.OnModelCreating(modelBuilder);
|
||||
|
||||
modelBuilder.Entity<User>(entry => {
|
||||
entry.HasKey(e => e.Id);
|
||||
entry.Property(e => e.FirstName);
|
||||
entry.Property(e => e.LastName);
|
||||
entry.Property(e => e.Email);
|
||||
entry.Property(e => e.Username);
|
||||
entry.Property(e => e.Password);
|
||||
entry.Property(e => e.Created);
|
||||
});
|
||||
|
||||
modelBuilder.Entity<RefreshToken>(entry => {
|
||||
entry.HasKey(e => e.Id);
|
||||
entry.Property(e => e.UserId);
|
||||
entry.Property(e => e.ExpirationDate);
|
||||
});
|
||||
|
||||
modelBuilder.Entity<AccessToken>(entry => {
|
||||
entry.HasKey(e => e.Id);
|
||||
entry.Property(e => e.RefreshTokenId);
|
||||
entry.Property(e => e.ExpirationDate);
|
||||
});
|
||||
|
||||
modelBuilder.Entity<Permission>(entry => {
|
||||
entry.HasKey(e => e.Id);
|
||||
entry.Property(e => e.Id).ValueGeneratedOnAdd();
|
||||
|
||||
entry.Property(e => e.UserId);
|
||||
entry.Property(e => e.PermissionName);
|
||||
entry.Property(e => e.Type);
|
||||
});
|
||||
|
||||
modelBuilder.Entity<FileShare>(entry => {
|
||||
entry.HasKey(e => e.Id);
|
||||
entry.Property(e => e.Owner);
|
||||
entry.Property(e => e.File);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
19
Projekte/WebDesktop/WebDesktopBackend/Dockerfile
Normal file
19
Projekte/WebDesktop/WebDesktopBackend/Dockerfile
Normal file
@@ -0,0 +1,19 @@
|
||||
FROM mcr.microsoft.com/dotnet/aspnet:6.0 AS base
|
||||
WORKDIR /app
|
||||
EXPOSE 4041
|
||||
|
||||
FROM mcr.microsoft.com/dotnet/sdk:6.0 AS build
|
||||
WORKDIR /src
|
||||
COPY ["WebDesktopBackend.csproj", "./"]
|
||||
RUN dotnet restore "WebDesktopBackend.csproj"
|
||||
COPY . .
|
||||
WORKDIR "/src/"
|
||||
RUN dotnet build "WebDesktopBackend.csproj" -c Release -o /app/build
|
||||
|
||||
FROM build AS publish
|
||||
RUN dotnet publish "WebDesktopBackend.csproj" -c Release -o /app/publish
|
||||
|
||||
FROM base AS final
|
||||
WORKDIR /app
|
||||
COPY --from=publish /app/publish .
|
||||
ENTRYPOINT ["dotnet", "WebDesktopBackend.dll"]
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace WebDesktopBackend.Entitys.Files {
|
||||
public class DirectoryContent {
|
||||
public string[] Files { get; set; }
|
||||
public string[] Directories { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using System;
|
||||
|
||||
namespace WebDesktopBackend.Entitys.Files {
|
||||
public class DirectoryInformation {
|
||||
public string Name { get; set; }
|
||||
public DateTime Created { get; set; }
|
||||
public long Size { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using System;
|
||||
|
||||
namespace WebDesktopBackend.Entitys.Files {
|
||||
public class FileInformation {
|
||||
public string Name { get; set; }
|
||||
public DateTime Created { get; set; }
|
||||
public long Size { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace WebDesktopBackend.Entitys.Files {
|
||||
public class FileShare {
|
||||
public string Id { get; set; }
|
||||
public string Owner { get; set; }
|
||||
public string File { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
namespace WebDesktopBackend.Entitys.Permissions {
|
||||
public class Permission {
|
||||
public const int NotSet = 0;
|
||||
public const int Allow = 1;
|
||||
public const int Deny = 2;
|
||||
|
||||
public int Id { get; set; }
|
||||
public string UserId { get; set; }
|
||||
public string PermissionName { get; set; }
|
||||
public int Type { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace WebDesktopBackend.Entitys.Permissions {
|
||||
public class PermissionGroup {
|
||||
public string Permission { get; set; }
|
||||
public string Name { get; set; }
|
||||
public string[] Permissions { get; set; }
|
||||
public string[] Inherits { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
using System;
|
||||
|
||||
namespace WebDesktopBackend.Entitys.Tokens {
|
||||
public class AccessToken {
|
||||
public string Id { get; set; }
|
||||
public string RefreshTokenId { get; set; }
|
||||
public DateTime ExpirationDate { get; set; }
|
||||
}
|
||||
|
||||
public class AccessTokenResponse {
|
||||
public string Id { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using System;
|
||||
|
||||
namespace WebDesktopBackend.Entitys.Tokens {
|
||||
public class RefreshToken {
|
||||
public string Id { get; set; }
|
||||
public string UserId { get; set; }
|
||||
public DateTime ExpirationDate { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace WebDesktopBackend.Entitys.Tokens {
|
||||
public class Tokens {
|
||||
public RefreshToken refreshToken { get; set; }
|
||||
public AccessToken accessToken { get; set; }
|
||||
}
|
||||
}
|
||||
20
Projekte/WebDesktop/WebDesktopBackend/Entitys/User/User.cs
Normal file
20
Projekte/WebDesktop/WebDesktopBackend/Entitys/User/User.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
using System;
|
||||
|
||||
namespace WebDesktopBackend.Entitys.User {
|
||||
public class User : UserEditor {
|
||||
public string Id { get; set; }
|
||||
public DateTime Created { get; set; }
|
||||
|
||||
public User CreateCopy() {
|
||||
return new User {
|
||||
Id = Id,
|
||||
Created = Created,
|
||||
FirstName = FirstName,
|
||||
LastName = LastName,
|
||||
Email = Email,
|
||||
Username = Username,
|
||||
Password = "ENCRYPTED"
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
namespace WebDesktopBackend.Entitys.User {
|
||||
public class UserEditor {
|
||||
public string FirstName { get; set; }
|
||||
public string LastName { get; set; }
|
||||
public string Email { get; set; }
|
||||
public string Username { get; set; }
|
||||
public string Password { get; set; }
|
||||
|
||||
public void EditUser(User user) {
|
||||
Trim();
|
||||
user.FirstName = string.IsNullOrEmpty(FirstName) ? user.FirstName : FirstName;
|
||||
user.LastName = string.IsNullOrEmpty(LastName) ? user.LastName : LastName;
|
||||
user.Email = string.IsNullOrEmpty(Email) ? user.Email : Email;
|
||||
user.Username = string.IsNullOrEmpty(Username) ? user.Username : Username;
|
||||
user.Password = string.IsNullOrEmpty(Password) ? user.Password : Password;
|
||||
}
|
||||
|
||||
public void Trim() {
|
||||
FirstName = FirstName.Trim();
|
||||
LastName = LastName.Trim();
|
||||
Email = Email.Trim();
|
||||
Username = Username.Trim();
|
||||
Password = Password.Trim();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace WebDesktopBackend.Entitys.User {
|
||||
public class UserLogin {
|
||||
public string Username { get; set; }
|
||||
public string Email { get; set; }
|
||||
public string Password { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using System.IO;
|
||||
|
||||
namespace WebDesktopBackend.Extentions {
|
||||
public static class DirectoryInfoExtentions {
|
||||
/// <summary>
|
||||
/// Returns the size of the Directory (from: https://stackoverflow.com/a/32364847)
|
||||
/// </summary>
|
||||
/// <param name="directoryInfo">The Directory</param>
|
||||
/// <param name="recursive">When set to true, the Functions includes also Subdirectories</param>
|
||||
/// <returns>The size of the Directory</returns>
|
||||
public static long GetDirectorySize(this DirectoryInfo directoryInfo, bool recursive = true) {
|
||||
var startDirectorySize = default(long);
|
||||
if (directoryInfo == null || !directoryInfo.Exists)
|
||||
return startDirectorySize; //Return 0 while Directory does not exist.
|
||||
|
||||
//Add size of files in the Current Directory to main size.
|
||||
foreach (var fileInfo in directoryInfo.GetFiles())
|
||||
System.Threading.Interlocked.Add(ref startDirectorySize, fileInfo.Length);
|
||||
|
||||
if (recursive) //Loop on Sub Direcotries in the Current Directory and Calculate it's files size.
|
||||
System.Threading.Tasks.Parallel.ForEach(directoryInfo.GetDirectories(), (subDirectory) =>
|
||||
System.Threading.Interlocked.Add(ref startDirectorySize, GetDirectorySize(subDirectory, recursive)));
|
||||
|
||||
return startDirectorySize; //Return full Size of this Directory.
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
using System;
|
||||
using System.Net.WebSockets;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace WebDesktopBackend.Extentions {
|
||||
public static class WebSocketExtentions {
|
||||
|
||||
public static async Task SendMessage(this WebSocket socket, string message, Encoding encoding = null) {
|
||||
encoding ??= Encoding.Default;
|
||||
await socket.SendAsync(new ArraySegment<byte>(encoding.GetBytes(message)), WebSocketMessageType.Text, true, CancellationToken.None);
|
||||
}
|
||||
|
||||
public static async Task<string> RecieveMessage(this WebSocket socket, Encoding encoding = null) {
|
||||
encoding ??= Encoding.Default;
|
||||
byte[] buffer = new byte[1024 * 4];
|
||||
var result = await socket.ReceiveAsync(new ArraySegment<byte>(buffer), CancellationToken.None);
|
||||
return encoding.GetString(new ArraySegment<byte>(buffer, 0, result.Count));
|
||||
}
|
||||
|
||||
public static async Task<WebSocket> ConnectAsync(this ClientWebSocket socket, Uri endpoint) {
|
||||
await socket.ConnectAsync(endpoint, CancellationToken.None);
|
||||
return socket;
|
||||
}
|
||||
|
||||
public static CancellationTokenSource AddMessageEventHandler(this WebSocket socket, Action<string> handler) {
|
||||
var source = new CancellationTokenSource();
|
||||
|
||||
Task.Run(async () => {
|
||||
while (!socket.CloseStatus.HasValue) {
|
||||
string msg = await socket.RecieveMessage();
|
||||
handler.Invoke(msg);
|
||||
}
|
||||
}, source.Token);
|
||||
|
||||
return source;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
144
Projekte/WebDesktop/WebDesktopBackend/Logic/FileLogic.cs
Normal file
144
Projekte/WebDesktop/WebDesktopBackend/Logic/FileLogic.cs
Normal file
@@ -0,0 +1,144 @@
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Options;
|
||||
using WebDesktopBackend.Contract.Logic;
|
||||
using WebDesktopBackend.Contract.Persistance;
|
||||
using WebDesktopBackend.Entitys.Files;
|
||||
using WebDesktopBackend.Extentions;
|
||||
using WebDesktopBackend.LogicResults;
|
||||
using WebDesktopBackend.Options;
|
||||
using WebDesktopBackend.Security;
|
||||
using FileShare = WebDesktopBackend.Entitys.Files.FileShare;
|
||||
|
||||
namespace WebDesktopBackend.Logic {
|
||||
public class FileLogic : IFileLogic {
|
||||
private readonly IFileRepository _fileRepository;
|
||||
private readonly ITokenContext _context;
|
||||
private readonly FileSystemOptions _options;
|
||||
|
||||
public FileLogic(IFileRepository fileRepository, ITokenContext context, IOptions<FileSystemOptions> options) {
|
||||
_fileRepository = fileRepository;
|
||||
_context = context;
|
||||
_options = options.Value;
|
||||
}
|
||||
|
||||
public ILogicResult CreateDirectory(string directory, string name) {
|
||||
bool success = _fileRepository.CreateDirectory(_options.RootDirectory + _context.UserId + Clean(directory), Clean(name));
|
||||
if (success) return LogicResult.Ok();
|
||||
return LogicResult.Conflict();
|
||||
}
|
||||
|
||||
public async Task<ILogicResult> UploadFile(IFormCollection data) {
|
||||
IFormFile file = data.Files[0];
|
||||
if (!CheckUserDirectorySize(file.Length)) return LogicResult.Forbidden("Max Directory size reached");
|
||||
|
||||
string dir = _options.RootDirectory + _context.UserId + Clean(data["directory"]);
|
||||
await _fileRepository.UploadFile(file, dir);
|
||||
return LogicResult.Ok();
|
||||
}
|
||||
|
||||
public async Task<ILogicResult> UploadJson(string directory, string name, string content) {
|
||||
await _fileRepository.UploadJson(_options.RootDirectory + _context.UserId + Clean(directory), Clean(name) + ".json", content);
|
||||
return LogicResult.Ok();
|
||||
}
|
||||
|
||||
public ILogicResult<FileStream> DownloadFile(string directory, string file) {
|
||||
string path = _options.RootDirectory + _context.UserId + Clean(directory) + "/" + Clean(file);
|
||||
if (!new FileInfo(path).Exists)
|
||||
return LogicResult<FileStream>.NotFound();
|
||||
|
||||
return LogicResult<FileStream>.Ok(_fileRepository.DownloadFile(path));
|
||||
}
|
||||
|
||||
public async Task<ILogicResult<string>> DownloadJson(string file) {
|
||||
string path = _options.RootDirectory + _context.UserId + Clean(file);
|
||||
if (!new FileInfo(path).Exists)
|
||||
return LogicResult<string>.NotFound();
|
||||
|
||||
return LogicResult<string>.Ok(await _fileRepository.DownloadJson(path));
|
||||
}
|
||||
|
||||
public ILogicResult<DirectoryContent> GetDirectory(string directory) {
|
||||
string path = _options.RootDirectory + _context.UserId + Clean(directory);
|
||||
if (!new DirectoryInfo(path).Exists)
|
||||
return LogicResult<DirectoryContent>.NotFound();
|
||||
|
||||
return LogicResult<DirectoryContent>.Ok(_fileRepository.GetDirectory(path));
|
||||
}
|
||||
|
||||
public ILogicResult<DirectoryInformation> GetDirectoryInformation(string directory) {
|
||||
string path = _options.RootDirectory + _context.UserId + Clean(directory);
|
||||
if (!new DirectoryInfo(path).Exists)
|
||||
return LogicResult<DirectoryInformation>.NotFound();
|
||||
|
||||
return LogicResult<DirectoryInformation>.Ok(_fileRepository.GetDirectoryInformation(path));
|
||||
}
|
||||
|
||||
public ILogicResult<FileInformation> GetFileInformation(string directory, string file) {
|
||||
string path = _options.RootDirectory + _context.UserId + Clean(directory) + "/" + Clean(file);
|
||||
if (!new FileInfo(path).Exists)
|
||||
return LogicResult<FileInformation>.NotFound();
|
||||
|
||||
return LogicResult<FileInformation>.Ok(_fileRepository.GetFileInformation(path));
|
||||
}
|
||||
|
||||
public ILogicResult MoveDirectory(string directory, string name, string to) {
|
||||
string path = _options.RootDirectory + _context.UserId + Clean(directory) + Clean(name);
|
||||
if (!new DirectoryInfo(path).Exists)
|
||||
return LogicResult.NotFound();
|
||||
|
||||
to = _options.RootDirectory + _context.UserId + to;
|
||||
if (!new DirectoryInfo(to).Exists)
|
||||
return LogicResult.NotFound();
|
||||
|
||||
_fileRepository.MoveDirectory(path, to + "/" + Clean(name));
|
||||
return LogicResult.Ok();
|
||||
}
|
||||
|
||||
public ILogicResult MoveFile(string directory, string file, string to) {
|
||||
string path = _options.RootDirectory + _context.UserId + Clean(directory) + Clean(file);
|
||||
if (!new FileInfo(path).Exists)
|
||||
return LogicResult.NotFound();
|
||||
|
||||
to = _options.RootDirectory + _context.UserId + to;
|
||||
if (!new DirectoryInfo(to).Exists)
|
||||
return LogicResult.NotFound();
|
||||
|
||||
_fileRepository.MoveFile(path, to + "/" + Clean(file));
|
||||
return LogicResult.Ok();
|
||||
}
|
||||
|
||||
public ILogicResult Delete(string url) {
|
||||
_fileRepository.Delete(_options.RootDirectory + _context.UserId + Clean(url));
|
||||
return LogicResult.Ok();
|
||||
}
|
||||
|
||||
public ILogicResult<FileShare> Share(string url) {
|
||||
string share = _fileRepository.GenerateShareId(Clean(url), _context.UserId);
|
||||
|
||||
if (share != null) {
|
||||
var result = new FileShare() {
|
||||
Id = share,
|
||||
Owner = _context.UserId,
|
||||
File = url
|
||||
};
|
||||
|
||||
return LogicResult<FileShare>.Ok(result);
|
||||
}
|
||||
|
||||
return LogicResult<FileShare>.Conflict();
|
||||
}
|
||||
|
||||
private bool CheckUserDirectorySize(long fileSize = 0) {
|
||||
DirectoryInfo info = new DirectoryInfo(_options.RootDirectory + _context.UserId);
|
||||
if (!info.Exists) return true;
|
||||
if (info.GetDirectorySize() > _options.MaxSizePerUserInMb * 1000000 - fileSize) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
private string Clean(in string input) {
|
||||
return input.Replace("../", "/").Replace("./", "/");
|
||||
}
|
||||
}
|
||||
}
|
||||
166
Projekte/WebDesktop/WebDesktopBackend/Logic/UserLogic.cs
Normal file
166
Projekte/WebDesktop/WebDesktopBackend/Logic/UserLogic.cs
Normal file
@@ -0,0 +1,166 @@
|
||||
using System.Linq;
|
||||
using WebDesktopBackend.Contract.Logic;
|
||||
using WebDesktopBackend.Contract.Persistance;
|
||||
using WebDesktopBackend.Entitys.Tokens;
|
||||
using WebDesktopBackend.Entitys.User;
|
||||
using WebDesktopBackend.LogicResults;
|
||||
using WebDesktopBackend.Security;
|
||||
|
||||
namespace WebDesktopBackend.Logic {
|
||||
public class UserLogic : IUserLogic {
|
||||
private readonly IUserRepository _users;
|
||||
private readonly ITokenRepository _tokens;
|
||||
private readonly IGroupRepository _groups;
|
||||
private readonly IFileRepository _files;
|
||||
private readonly ITokenContext _context;
|
||||
|
||||
public UserLogic(IUserRepository users, ITokenRepository tokens, ITokenContext context, IGroupRepository groups, IFileRepository files) {
|
||||
_users = users;
|
||||
_tokens = tokens;
|
||||
_context = context;
|
||||
_groups = groups;
|
||||
_files = files;
|
||||
}
|
||||
|
||||
public ILogicResult<Tokens> Login(UserLogin login) {
|
||||
if (!_users.Login(login)) return LogicResult<Tokens>.Conflict();
|
||||
User user = _users.GetUserFromLogin(login);
|
||||
_tokens.DeleteUserTokens(_context.UserId);
|
||||
RefreshToken refreshToken = _tokens.CreateRefreshToken(user.Id);
|
||||
AccessToken accessToken = _tokens.CreateAccessToken(refreshToken.Id);
|
||||
return LogicResult<Tokens>.Ok(new Tokens {refreshToken = refreshToken, accessToken = accessToken});
|
||||
}
|
||||
|
||||
public ILogicResult<Tokens> Register(UserEditor editor) {
|
||||
editor.Trim();
|
||||
if (!ValidateUserdata(editor)) return LogicResult<Tokens>.BadRequest();
|
||||
User user = _users.AddUser(editor);
|
||||
_files.InitUser(user.Id);
|
||||
RefreshToken refreshToken = _tokens.CreateRefreshToken(user.Id);
|
||||
AccessToken accessToken = _tokens.CreateAccessToken(refreshToken.Id);
|
||||
return LogicResult<Tokens>.Ok(new Tokens {refreshToken = refreshToken, accessToken = accessToken});
|
||||
}
|
||||
|
||||
public ILogicResult Logout() {
|
||||
_tokens.DeleteRefreshToken(_context.RefreshTokenId);
|
||||
return LogicResult.Ok();
|
||||
}
|
||||
|
||||
public ILogicResult EditUser(string id, UserEditor editor) {
|
||||
editor.Trim();
|
||||
if (!ValidateEdit(editor)) return LogicResult.BadRequest();
|
||||
if (_users.GetUser(id) == null) return LogicResult.NotFound();
|
||||
_users.EditUser(id, editor);
|
||||
return LogicResult.Ok();
|
||||
}
|
||||
|
||||
public ILogicResult DeleteUser(string id) {
|
||||
_tokens.DeleteUserTokens(id);
|
||||
_users.DeleteUser(id);
|
||||
_files.DeleteUserFolder(id);
|
||||
return LogicResult.Ok();
|
||||
}
|
||||
|
||||
public ILogicResult<User> GetUser(string id) {
|
||||
User user = _users.GetUser(id);
|
||||
if (user == null) return LogicResult<User>.NotFound();
|
||||
return LogicResult<User>.Ok(user.CreateCopy());
|
||||
}
|
||||
|
||||
public ILogicResult<User[]> GetUsers() {
|
||||
User[] users = _users.GetUsers();
|
||||
User[] exports = new User[users.Length];
|
||||
for (var i = 0; i < users.Length; i++) {
|
||||
exports[i] = users[i].CreateCopy();
|
||||
}
|
||||
|
||||
return LogicResult<User[]>.Ok(exports);
|
||||
}
|
||||
|
||||
public ILogicResult Valdiate() {
|
||||
if (string.IsNullOrEmpty(_context.RefreshTokenId) || string.IsNullOrEmpty(_context.AccessTokenId)) return LogicResult.Forbidden();
|
||||
if (!_tokens.ValidateRefreshToken(_context.RefreshTokenId)) {
|
||||
_tokens.DeleteRefreshToken(_context.RefreshTokenId);
|
||||
return LogicResult.Forbidden();
|
||||
}
|
||||
return _tokens.ValidateAccessToken(_context.AccessTokenId) ? LogicResult.Ok() : LogicResult.Forbidden();
|
||||
}
|
||||
|
||||
public ILogicResult<AccessTokenResponse> GetToken(string refreshTokenId) {
|
||||
if (refreshTokenId == null) return LogicResult<AccessTokenResponse>.Forbidden();
|
||||
if (!_tokens.ValidateRefreshToken(refreshTokenId)) {
|
||||
_tokens.DeleteRefreshToken(refreshTokenId);
|
||||
return LogicResult<AccessTokenResponse>.Forbidden();
|
||||
}
|
||||
return LogicResult<AccessTokenResponse>.Ok(new AccessTokenResponse {Id = _tokens.CreateAccessToken(refreshTokenId).Id});
|
||||
}
|
||||
|
||||
public ILogicResult<User> GetOwnUser() {
|
||||
return LogicResult<User>.Ok(_users.GetUser(_context.UserId).CreateCopy());
|
||||
}
|
||||
|
||||
public ILogicResult<string[]> GetPermissions(string id) {
|
||||
return LogicResult<string[]>.Ok(_groups.GetUserPermissions(id).Select(perm => perm.PermissionName).ToArray());
|
||||
}
|
||||
|
||||
public ILogicResult<string[]> GetRawPermissions(string id) {
|
||||
return LogicResult<string[]>.Ok(_tokens.GetUserPermissions(id).Select(perm => perm.PermissionName).ToArray());
|
||||
}
|
||||
|
||||
public ILogicResult AddPermission(string id, string permission) {
|
||||
_tokens.AddPermission(id, permission);
|
||||
return LogicResult.Ok();
|
||||
}
|
||||
|
||||
public ILogicResult DeletePermission(string id, string permission) {
|
||||
_tokens.DeletePermission(id, permission);
|
||||
return LogicResult.Ok();
|
||||
}
|
||||
|
||||
private bool ValidateUserdata(UserEditor editor) {
|
||||
if (string.IsNullOrEmpty(editor.FirstName)) return false;
|
||||
if (string.IsNullOrEmpty(editor.LastName)) return false;
|
||||
if (string.IsNullOrEmpty(editor.Email)) return false;
|
||||
if (string.IsNullOrEmpty(editor.Username)) return false;
|
||||
if (string.IsNullOrEmpty(editor.Password)) return false;
|
||||
|
||||
if (editor.FirstName.Length > 255) return false;
|
||||
if (editor.LastName.Length > 255) return false;
|
||||
if (editor.Email.Length > 255) return false;
|
||||
if (editor.Username.Length > 255) return false;
|
||||
if (editor.Password.Length > 255) return false;
|
||||
|
||||
if (!editor.Email.Contains('@') || !editor.Email.Contains('.')) return false;
|
||||
if (editor.Username.Contains('@')) return false;
|
||||
if (editor.Password.Length < 8) return false;
|
||||
if (_users.GetUserByUsername(editor.Username) != null) return false;
|
||||
if (_users.GetUserByEmail(editor.Email) != null) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private bool ValidateEdit(UserEditor editor) {
|
||||
if (editor.FirstName.Length > 255) return false;
|
||||
if (editor.LastName.Length > 255) return false;
|
||||
if (editor.Email.Length > 255) return false;
|
||||
if (editor.Username.Length > 255) return false;
|
||||
if (editor.Password.Length > 255) return false;
|
||||
|
||||
if (!string.IsNullOrEmpty(editor.Email)) {
|
||||
if (!editor.Email.Contains('@') || !editor.Email.Contains('.')) return false;
|
||||
if (_users.GetUserByEmail(editor.Email) != null) return false;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(editor.Username)) {
|
||||
if (editor.Username.Contains('@')) return false;
|
||||
if (_users.GetUserByUsername(editor.Username) != null) return false;
|
||||
}
|
||||
|
||||
if (!string.IsNullOrEmpty(editor.Password)) {
|
||||
if (editor.Password.Length < 8) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
using System;
|
||||
using System.Net;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace WebDesktopBackend.LogicResults {
|
||||
public static class ControllerBaseExtention {
|
||||
public static ActionResult FromLogicResult(this ControllerBase controller, ILogicResult result)
|
||||
{
|
||||
switch (result.State)
|
||||
{
|
||||
case LogicResultState.Ok:
|
||||
return controller.Ok();
|
||||
|
||||
case LogicResultState.BadRequest:
|
||||
return controller.StatusCode((int)HttpStatusCode.BadRequest, result.Message);
|
||||
|
||||
case LogicResultState.Forbidden:
|
||||
return controller.StatusCode((int)HttpStatusCode.Forbidden, result.Message);
|
||||
|
||||
case LogicResultState.NotFound:
|
||||
return controller.StatusCode((int)HttpStatusCode.NotFound, result.Message);
|
||||
|
||||
case LogicResultState.Conflict:
|
||||
return controller.StatusCode((int)HttpStatusCode.Conflict, result.Message);
|
||||
|
||||
default:
|
||||
throw new Exception("An unhandled result has occurred as a result of a service call.");
|
||||
}
|
||||
}
|
||||
|
||||
public static ActionResult FromLogicResult<T>(this ControllerBase controller, ILogicResult<T> result)
|
||||
{
|
||||
switch (result.State)
|
||||
{
|
||||
case LogicResultState.Ok:
|
||||
return controller.Ok(result.Data);
|
||||
|
||||
case LogicResultState.BadRequest:
|
||||
return controller.StatusCode((int)HttpStatusCode.BadRequest, result.Message);
|
||||
|
||||
case LogicResultState.Forbidden:
|
||||
return controller.StatusCode((int)HttpStatusCode.Forbidden, result.Message);
|
||||
|
||||
case LogicResultState.NotFound:
|
||||
return controller.StatusCode((int)HttpStatusCode.NotFound, result.Message);
|
||||
|
||||
case LogicResultState.Conflict:
|
||||
return controller.StatusCode((int)HttpStatusCode.Conflict, result.Message);
|
||||
|
||||
default:
|
||||
throw new Exception("An unhandled result has occurred as a result of a service call.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
namespace WebDesktopBackend.LogicResults {
|
||||
public interface ILogicResult
|
||||
{
|
||||
LogicResultState State { get; set; }
|
||||
|
||||
string Message { get; set; }
|
||||
|
||||
bool IsSuccessful { get; }
|
||||
}
|
||||
|
||||
public interface ILogicResult<T>
|
||||
{
|
||||
LogicResultState State { get; set; }
|
||||
|
||||
T Data { get; set; }
|
||||
|
||||
string Message { get; set; }
|
||||
|
||||
bool IsSuccessful { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,230 @@
|
||||
namespace WebDesktopBackend.LogicResults {
|
||||
internal class LogicResult : ILogicResult
|
||||
{
|
||||
public LogicResultState State { get; set; }
|
||||
|
||||
public string Message { get; set; }
|
||||
|
||||
public bool IsSuccessful
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.State == LogicResultState.Ok;
|
||||
}
|
||||
}
|
||||
|
||||
public static LogicResult Ok()
|
||||
{
|
||||
return new LogicResult()
|
||||
{
|
||||
State = LogicResultState.Ok
|
||||
};
|
||||
}
|
||||
|
||||
public static LogicResult BadRequest()
|
||||
{
|
||||
return new LogicResult()
|
||||
{
|
||||
State = LogicResultState.BadRequest
|
||||
};
|
||||
}
|
||||
|
||||
public static LogicResult BadRequest(string message)
|
||||
{
|
||||
return new LogicResult()
|
||||
{
|
||||
State = LogicResultState.BadRequest,
|
||||
Message = message
|
||||
};
|
||||
}
|
||||
|
||||
public static LogicResult Forbidden()
|
||||
{
|
||||
return new LogicResult()
|
||||
{
|
||||
State = LogicResultState.Forbidden
|
||||
};
|
||||
}
|
||||
|
||||
public static LogicResult Forbidden(string message)
|
||||
{
|
||||
return new LogicResult()
|
||||
{
|
||||
State = LogicResultState.Forbidden,
|
||||
Message = message
|
||||
};
|
||||
}
|
||||
|
||||
public static LogicResult NotFound()
|
||||
{
|
||||
return new LogicResult()
|
||||
{
|
||||
State = LogicResultState.NotFound
|
||||
};
|
||||
}
|
||||
|
||||
public static LogicResult NotFound(string message)
|
||||
{
|
||||
return new LogicResult()
|
||||
{
|
||||
State = LogicResultState.NotFound,
|
||||
Message = message
|
||||
};
|
||||
}
|
||||
|
||||
public static LogicResult Conflict()
|
||||
{
|
||||
return new LogicResult()
|
||||
{
|
||||
State = LogicResultState.Conflict
|
||||
};
|
||||
}
|
||||
|
||||
public static LogicResult Conflict(string message)
|
||||
{
|
||||
return new LogicResult()
|
||||
{
|
||||
State = LogicResultState.Conflict,
|
||||
Message = message
|
||||
};
|
||||
}
|
||||
|
||||
public static LogicResult Forward(LogicResult result)
|
||||
{
|
||||
return new LogicResult()
|
||||
{
|
||||
State = result.State,
|
||||
Message = result.Message
|
||||
};
|
||||
}
|
||||
|
||||
public static LogicResult Forward<T>(ILogicResult<T> result)
|
||||
{
|
||||
return new LogicResult()
|
||||
{
|
||||
State = result.State,
|
||||
Message = result.Message
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
internal class LogicResult<T> : ILogicResult<T>
|
||||
{
|
||||
public LogicResultState State { get; set; }
|
||||
|
||||
public T Data { get; set; }
|
||||
|
||||
public string Message { get; set; }
|
||||
|
||||
public bool IsSuccessful
|
||||
{
|
||||
get
|
||||
{
|
||||
return this.State == LogicResultState.Ok;
|
||||
}
|
||||
}
|
||||
|
||||
public static LogicResult<T> Ok()
|
||||
{
|
||||
return new LogicResult<T>()
|
||||
{
|
||||
State = LogicResultState.Ok
|
||||
};
|
||||
}
|
||||
|
||||
public static LogicResult<T> Ok(T result)
|
||||
{
|
||||
return new LogicResult<T>()
|
||||
{
|
||||
State = LogicResultState.Ok,
|
||||
Data = result
|
||||
};
|
||||
}
|
||||
|
||||
public static LogicResult<T> BadRequest()
|
||||
{
|
||||
return new LogicResult<T>()
|
||||
{
|
||||
State = LogicResultState.BadRequest
|
||||
};
|
||||
}
|
||||
|
||||
public static LogicResult<T> BadRequest(string message)
|
||||
{
|
||||
return new LogicResult<T>()
|
||||
{
|
||||
State = LogicResultState.BadRequest,
|
||||
Message = message
|
||||
};
|
||||
}
|
||||
|
||||
public static LogicResult<T> Forbidden()
|
||||
{
|
||||
return new LogicResult<T>()
|
||||
{
|
||||
State = LogicResultState.Forbidden
|
||||
};
|
||||
}
|
||||
|
||||
public static LogicResult<T> Forbidden(string message)
|
||||
{
|
||||
return new LogicResult<T>()
|
||||
{
|
||||
State = LogicResultState.Forbidden,
|
||||
Message = message
|
||||
};
|
||||
}
|
||||
|
||||
public static LogicResult<T> NotFound()
|
||||
{
|
||||
return new LogicResult<T>()
|
||||
{
|
||||
State = LogicResultState.NotFound
|
||||
};
|
||||
}
|
||||
|
||||
public static LogicResult<T> NotFound(string message)
|
||||
{
|
||||
return new LogicResult<T>()
|
||||
{
|
||||
State = LogicResultState.NotFound,
|
||||
Message = message
|
||||
};
|
||||
}
|
||||
|
||||
public static LogicResult<T> Conflict()
|
||||
{
|
||||
return new LogicResult<T>()
|
||||
{
|
||||
State = LogicResultState.Conflict
|
||||
};
|
||||
}
|
||||
|
||||
public static LogicResult<T> Conflict(string message)
|
||||
{
|
||||
return new LogicResult<T>()
|
||||
{
|
||||
State = LogicResultState.Conflict,
|
||||
Message = message
|
||||
};
|
||||
}
|
||||
|
||||
public static LogicResult<T> Forward(ILogicResult result)
|
||||
{
|
||||
return new LogicResult<T>()
|
||||
{
|
||||
State = result.State,
|
||||
Message = result.Message
|
||||
};
|
||||
}
|
||||
|
||||
public static LogicResult<T> Forward<T2>(ILogicResult<T2> result)
|
||||
{
|
||||
return new LogicResult<T>()
|
||||
{
|
||||
State = result.State,
|
||||
Message = result.Message
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace WebDesktopBackend.LogicResults {
|
||||
public enum LogicResultState {
|
||||
Ok,
|
||||
BadRequest,
|
||||
Forbidden,
|
||||
NotFound,
|
||||
Conflict
|
||||
}
|
||||
}
|
||||
30
Projekte/WebDesktop/WebDesktopBackend/MySql.cs
Normal file
30
Projekte/WebDesktop/WebDesktopBackend/MySql.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using MySql.Data.MySqlClient;
|
||||
|
||||
namespace WebDesktopBackend {
|
||||
public class MySql : IDisposable {
|
||||
public const string ConnectionString = "SERVER=213.136.89.237;DATABASE=WebDesktop;UID=WebDesktop;PASSWORD=Hft6bP@V3IkYvqS1";
|
||||
private readonly MySqlConnection _connection;
|
||||
private readonly List<string> _querys;
|
||||
|
||||
public MySql() {
|
||||
_querys = new List<string>();
|
||||
_connection = new MySqlConnection(ConnectionString);
|
||||
_connection.Open();
|
||||
}
|
||||
|
||||
public void Insert(string qry) {
|
||||
if (!qry.EndsWith(";")) qry += ";";
|
||||
_querys.Add(qry);
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
MySqlCommand cmd = new MySqlCommand(string.Join(" ", _querys), _connection);
|
||||
cmd.ExecuteNonQuery();
|
||||
cmd.Dispose();
|
||||
|
||||
_connection?.Dispose();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace WebDesktopBackend.Options {
|
||||
public class FileSystemOptions : OptionsFromConfiguration {
|
||||
public override string Position => "FileSystem";
|
||||
|
||||
public string RootDirectory { get; set; }
|
||||
public int MaxSizePerUserInMb { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace WebDesktopBackend.Options
|
||||
{
|
||||
public abstract class OptionsFromConfiguration
|
||||
{
|
||||
public abstract string Position { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using System;
|
||||
|
||||
namespace WebDesktopBackend.Options
|
||||
{
|
||||
public static class OptionsFromConfigurationExtensions
|
||||
{
|
||||
public static T AddOptionsFromConfiguration<T>(this IServiceCollection services, IConfiguration configuration)
|
||||
where T : OptionsFromConfiguration
|
||||
{
|
||||
T optionsInstance = (T)Activator.CreateInstance(typeof(T));
|
||||
if (optionsInstance == null) return null;
|
||||
string position = optionsInstance.Position;
|
||||
services.Configure((Action<T>)(options =>
|
||||
{
|
||||
IConfigurationSection section = configuration.GetSection(position);
|
||||
if (section != null)
|
||||
{
|
||||
section.Bind(options);
|
||||
}
|
||||
}));
|
||||
return optionsInstance;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,134 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Options;
|
||||
using WebDesktopBackend.Contract.Persistance;
|
||||
using WebDesktopBackend.Entitys.Files;
|
||||
using WebDesktopBackend.Extentions;
|
||||
using WebDesktopBackend.Options;
|
||||
using FileShare = WebDesktopBackend.Entitys.Files.FileShare;
|
||||
|
||||
namespace WebDesktopBackend.Persistance {
|
||||
public class FileRepository : IFileRepository {
|
||||
private readonly FileSystemOptions _options;
|
||||
private readonly DatabaseContext _context;
|
||||
|
||||
public FileRepository(IOptions<FileSystemOptions> options, DatabaseContext context) {
|
||||
_options = options.Value;
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public void InitUser(string userId) {
|
||||
CreateDirectory(_options.RootDirectory, userId);
|
||||
}
|
||||
|
||||
public void DeleteUserFolder(string userId) {
|
||||
Delete(_options.RootDirectory + userId);
|
||||
}
|
||||
|
||||
public bool CreateDirectory(string directory, string name) {
|
||||
DirectoryInfo info = new DirectoryInfo(directory + "/" + name);
|
||||
if (info.Exists) return false;
|
||||
info.Create();
|
||||
return true;
|
||||
}
|
||||
|
||||
public async Task UploadFile(IFormFile file, string directory) {
|
||||
DirectoryInfo dir = new DirectoryInfo(directory);
|
||||
if (!dir.Exists)
|
||||
dir.Create();
|
||||
|
||||
FileInfo fileInfo = new FileInfo(dir + "/" + file.FileName);
|
||||
if (fileInfo.Exists)
|
||||
fileInfo.Delete();
|
||||
|
||||
FileStream stream = fileInfo.OpenWrite();
|
||||
await file.CopyToAsync(stream);
|
||||
stream.Close();
|
||||
}
|
||||
|
||||
public async Task UploadJson(string directory, string name, string data) {
|
||||
DirectoryInfo dir = new DirectoryInfo(directory);
|
||||
if (!dir.Exists)
|
||||
dir.Create();
|
||||
|
||||
FileInfo file = new FileInfo(directory + "/" + name);
|
||||
if (file.Exists)
|
||||
file.Delete();
|
||||
|
||||
byte[] bytes = Encoding.UTF8.GetBytes(data);
|
||||
FileStream stream = file.Create();
|
||||
await stream.WriteAsync(bytes, 0, bytes.Length);
|
||||
stream.Close();
|
||||
}
|
||||
|
||||
public FileStream DownloadFile(string path) {
|
||||
FileInfo file = new FileInfo(path);
|
||||
return file.OpenRead();
|
||||
}
|
||||
|
||||
public Task<string> DownloadJson(string file) {
|
||||
return File.ReadAllTextAsync(file);
|
||||
}
|
||||
|
||||
public DirectoryContent GetDirectory(string directory) {
|
||||
DirectoryInfo dir = new DirectoryInfo(directory);
|
||||
|
||||
return new DirectoryContent() {
|
||||
Files = dir.GetFiles().Select(file => file.Name).ToArray(),
|
||||
Directories = dir.GetDirectories().Select(info => info.Name).ToArray()
|
||||
};
|
||||
}
|
||||
|
||||
public DirectoryInformation GetDirectoryInformation(string directory) {
|
||||
DirectoryInfo info = new DirectoryInfo(directory);
|
||||
return new DirectoryInformation {
|
||||
Name = info.Name,
|
||||
Created = Directory.GetCreationTime(directory),
|
||||
Size = info.GetDirectorySize()
|
||||
};
|
||||
}
|
||||
|
||||
public FileInformation GetFileInformation(string file) {
|
||||
FileInfo info = new FileInfo(file);
|
||||
return new FileInformation() {
|
||||
Name = info.Name,
|
||||
Created = File.GetCreationTime(file),
|
||||
Size = info.Length
|
||||
};
|
||||
}
|
||||
|
||||
public void MoveDirectory(string directory, string to) {
|
||||
DirectoryInfo info = new DirectoryInfo(directory);
|
||||
info.MoveTo(to);
|
||||
}
|
||||
|
||||
public void MoveFile(string file, string to) {
|
||||
FileInfo info = new FileInfo(file);
|
||||
info.MoveTo(to);
|
||||
}
|
||||
|
||||
public void Delete(string url) {
|
||||
if (File.Exists(url))
|
||||
File.Delete(url);
|
||||
|
||||
if (Directory.Exists(url))
|
||||
Directory.Delete(url, true);
|
||||
}
|
||||
|
||||
public string GenerateShareId(string url, string owner) {
|
||||
FileShare share = new FileShare();
|
||||
share.File = url;
|
||||
share.Owner = owner;
|
||||
share.Id = Guid.NewGuid().ToString();
|
||||
|
||||
_context.FileShares.Add(share);
|
||||
_context.SaveChanges();
|
||||
|
||||
return share.Id;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using WebDesktopBackend.Contract.Persistance;
|
||||
using WebDesktopBackend.Entitys.Permissions;
|
||||
|
||||
namespace WebDesktopBackend.Persistance {
|
||||
public class GroupRepository : IGroupRepository {
|
||||
private readonly ITokenRepository _tokens;
|
||||
private readonly PermissionGroup[] _groups;
|
||||
|
||||
public GroupRepository(ITokenRepository tokens) {
|
||||
_tokens = tokens;
|
||||
_groups = Program.Groups;
|
||||
}
|
||||
|
||||
public PermissionGroup GetPermissionGroup(string name) {
|
||||
return _groups.SingleOrDefault(group => group.Permission.Equals(name));
|
||||
}
|
||||
|
||||
public PermissionGroup[] GetGroupsFromUser(string userId) {
|
||||
Permission[] permissions = _tokens.GetUserPermissions(userId);
|
||||
return ExtractGroups(permissions);
|
||||
}
|
||||
|
||||
public PermissionGroup[] ExtractGroups(Permission[] permissions) {
|
||||
List<PermissionGroup> permissionGroups = new List<PermissionGroup>();
|
||||
foreach (var permission in permissions) {
|
||||
if (permission.PermissionName.StartsWith("group.")) {
|
||||
foreach (var permissionGroup in _groups) {
|
||||
if (permission.PermissionName.Equals(permissionGroup.Permission)) {
|
||||
permissionGroups.Add(permissionGroup);
|
||||
|
||||
if (permissionGroup.Inherits is not null) {
|
||||
foreach (var inherit in permissionGroup.Inherits) {
|
||||
permissionGroups.Add(GetPermissionGroup(inherit));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return permissionGroups.ToArray();
|
||||
}
|
||||
|
||||
public Permission[] GetUserPermissions(string id) {
|
||||
List<Permission> permissions = _tokens.GetUserPermissions(id)
|
||||
.Where(perm => perm.Type == Permission.Allow).ToList();
|
||||
|
||||
PermissionGroup[] groups = ExtractGroups(permissions.ToArray());
|
||||
foreach (var group in groups) {
|
||||
if (group.Permissions is null) continue;
|
||||
permissions.AddRange(group.Permissions
|
||||
.Select(perm => new Permission {Id = -1, UserId = id, Type = Permission.Allow, PermissionName = perm}));
|
||||
}
|
||||
|
||||
return permissions.ToArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Microsoft.Extensions.Options;
|
||||
using WebDesktopBackend.Contract.Persistance;
|
||||
using WebDesktopBackend.Entitys.Permissions;
|
||||
using WebDesktopBackend.Entitys.Tokens;
|
||||
using WebDesktopBackend.Security.Authentication;
|
||||
|
||||
namespace WebDesktopBackend.Persistance {
|
||||
public class TokenRepository : ITokenRepository {
|
||||
private readonly JwtTokenAuthenticationOptions _options;
|
||||
private readonly DatabaseContext _context;
|
||||
|
||||
public TokenRepository(IOptions<JwtTokenAuthenticationOptions> options, DatabaseContext context) {
|
||||
_options = options.Value;
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public RefreshToken GetRefreshToken(string id) {
|
||||
if (string.IsNullOrEmpty(id)) return null;
|
||||
return _context.RefreshTokens.Where(token => token.Id == id).SingleOrDefault();
|
||||
}
|
||||
|
||||
public AccessToken GetAccessToken(string id) {
|
||||
if (string.IsNullOrEmpty(id)) return null;
|
||||
return _context.AccessTokens.Where(token => token.Id == id).SingleOrDefault();
|
||||
}
|
||||
|
||||
public bool ValidateAccessToken(string id) {
|
||||
AccessToken token = GetAccessToken(id);
|
||||
if (token == null) return false;
|
||||
TimeSpan span = token.ExpirationDate - DateTime.Now;
|
||||
return span.TotalMilliseconds > 0;
|
||||
}
|
||||
|
||||
public bool ValidateRefreshToken(string id) {
|
||||
RefreshToken token = GetRefreshToken(id);
|
||||
if (token == null) return false;
|
||||
TimeSpan span = token.ExpirationDate - DateTime.Now;
|
||||
return span.TotalMilliseconds > 0;
|
||||
}
|
||||
|
||||
public RefreshToken CreateRefreshToken(string userId) {
|
||||
RefreshToken token = new RefreshToken { UserId = userId, Id = Guid.NewGuid().ToString(), ExpirationDate = DateTime.Now.Add(new TimeSpan(int.Parse(_options.RefreshTokenExpirationTimeInHours), 0, 0)) };
|
||||
_context.RefreshTokens.Add(token);
|
||||
_context.SaveChanges();
|
||||
return token;
|
||||
}
|
||||
|
||||
public AccessToken CreateAccessToken(string refreshTokenId) {
|
||||
AccessToken token = new AccessToken { RefreshTokenId = refreshTokenId, Id = Guid.NewGuid().ToString(), ExpirationDate = DateTime.Now.Add(new TimeSpan(0, int.Parse(_options.AccessTokenExpirationTimeInMinutes), 0)) };
|
||||
_context.AccessTokens.Add(token);
|
||||
_context.SaveChanges();
|
||||
return token;
|
||||
}
|
||||
|
||||
public void DeleteUserTokens(string id) {
|
||||
List<RefreshToken> refreshTokens = _context.RefreshTokens.Where(token => token.UserId == id).ToList();
|
||||
refreshTokens.ForEach(token => DeleteRefreshToken(token.Id));
|
||||
}
|
||||
|
||||
public void DeleteRefreshToken(string id) {
|
||||
_context.RefreshTokens.RemoveRange(_context.RefreshTokens.Where(token => token.Id == id));
|
||||
_context.AccessTokens.RemoveRange(_context.AccessTokens.Where(token => token.RefreshTokenId == id));
|
||||
}
|
||||
|
||||
public Permission[] GetUserPermissions(string id) {
|
||||
return _context.Permissions.Where(permission => permission.UserId == id).ToArray();
|
||||
}
|
||||
|
||||
public void AddPermission(string id, string permission) {
|
||||
_context.Permissions.Add(new Permission
|
||||
{ PermissionName = permission, UserId = id, Type = Permission.Allow });
|
||||
_context.SaveChanges();
|
||||
}
|
||||
|
||||
public void DeletePermission(string id, string permission) {
|
||||
_context.Permissions.Remove(_context.Permissions.Single(perm =>
|
||||
perm.UserId == id && perm.PermissionName == permission));
|
||||
_context.SaveChanges();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
using System;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Security.Cryptography;
|
||||
using System.Text;
|
||||
using Microsoft.AspNetCore.Cryptography.KeyDerivation;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using WebDesktopBackend.Contract.Persistance;
|
||||
using WebDesktopBackend.Entitys.Permissions;
|
||||
using WebDesktopBackend.Entitys.User;
|
||||
|
||||
namespace WebDesktopBackend.Persistance {
|
||||
public class UserRepository : IUserRepository {
|
||||
private readonly DatabaseContext _context;
|
||||
private readonly ITokenRepository _tokens;
|
||||
private readonly IConfiguration _configuration;
|
||||
|
||||
public UserRepository(DatabaseContext context, ITokenRepository tokens, IConfiguration configuration) {
|
||||
_context = context;
|
||||
_tokens = tokens;
|
||||
_configuration = configuration;
|
||||
}
|
||||
|
||||
public User AddUser(UserEditor editor) {
|
||||
User user = new User { Id = Guid.NewGuid().ToString(), Created = DateTime.Now };
|
||||
editor.EditUser(user);
|
||||
user.Password = Hash128(user.Password);
|
||||
_context.Users.Add(user);
|
||||
_context.Permissions.Add(new Permission()
|
||||
{ PermissionName = "group.user", UserId = user.Id, Type = Permission.Allow });
|
||||
_context.SaveChanges();
|
||||
return user;
|
||||
}
|
||||
|
||||
public void EditUser(string id, UserEditor editor) {
|
||||
User user = GetUser(id);
|
||||
if (!string.IsNullOrEmpty(editor.Password))
|
||||
editor.Password = Hash128(editor.Password);
|
||||
editor.EditUser(user);
|
||||
_context.SaveChanges();
|
||||
}
|
||||
|
||||
public void DeleteUser(string id) {
|
||||
_context.Users.RemoveRange(_context.Users.Where(user => user.Id == id));
|
||||
_context.Permissions.RemoveRange(_context.Permissions.Where(permission => permission.UserId == id));
|
||||
_tokens.DeleteUserTokens(id);
|
||||
_context.SaveChanges();
|
||||
}
|
||||
|
||||
public User GetUser(string id) {
|
||||
return _context.Users.SingleOrDefault(user => user.Id == id);
|
||||
}
|
||||
|
||||
public User GetUserByUsername(string username) {
|
||||
return _context.Users.SingleOrDefault(user => user.Username == username);
|
||||
}
|
||||
|
||||
public User GetUserByEmail(string email) {
|
||||
return _context.Users.SingleOrDefault(user => user.Email == email);
|
||||
}
|
||||
|
||||
public User GetUserFromLogin(UserLogin login) {
|
||||
if (!string.IsNullOrEmpty(login.Username)) return GetUserByUsername(login.Username);
|
||||
if (!string.IsNullOrEmpty(login.Email)) return GetUserByEmail(login.Email);
|
||||
return null;
|
||||
}
|
||||
|
||||
public User[] GetUsers() {
|
||||
return _context.Users.OrderBy(user => user.Created).ToArray();
|
||||
}
|
||||
|
||||
public bool Login(UserLogin login) {
|
||||
User user = GetUserFromLogin(login);
|
||||
if (user == null || string.IsNullOrEmpty(user.Password)) return false;
|
||||
return user.Password.Equals(Hash128(login.Password));
|
||||
}
|
||||
|
||||
private string Hash128(string plainText) {
|
||||
try {
|
||||
byte[] salt = _configuration.GetSection("PasswordSalt").Get<byte[]>();
|
||||
|
||||
string hashed = Convert.ToBase64String(KeyDerivation.Pbkdf2(
|
||||
password: plainText,
|
||||
salt: salt,
|
||||
prf: KeyDerivationPrf.HMACSHA256,
|
||||
iterationCount: 100000,
|
||||
numBytesRequested: 256 / 8
|
||||
));
|
||||
|
||||
return hashed;
|
||||
} catch (Exception) { return ""; }
|
||||
}
|
||||
}
|
||||
}
|
||||
45
Projekte/WebDesktop/WebDesktopBackend/Program.cs
Normal file
45
Projekte/WebDesktop/WebDesktopBackend/Program.cs
Normal file
@@ -0,0 +1,45 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using WebDesktopBackend.Entitys.Permissions;
|
||||
|
||||
namespace WebDesktopBackend {
|
||||
public class Program {
|
||||
public static PermissionGroup[] Groups;
|
||||
|
||||
public static void Main(string[] args) {
|
||||
var configuration = new ConfigurationBuilder()
|
||||
.AddJsonFile("appsettings.json", optional: false)
|
||||
.Build();
|
||||
|
||||
//COMPILE GROUPS
|
||||
var groupsSections = configuration.GetSection("Groups").GetChildren();
|
||||
List<PermissionGroup> groups = new List<PermissionGroup>();
|
||||
foreach (var section in groupsSections) {
|
||||
PermissionGroup group = new PermissionGroup();
|
||||
group.Name = section.GetValue<string>("Name");
|
||||
group.Permission = section.GetValue<string>("Permission");
|
||||
group.Permissions = section.GetSection("Permissions").Get<string[]>();
|
||||
group.Inherits = section.GetSection("Inherits").Get<string[]>();
|
||||
groups.Add(group);
|
||||
}
|
||||
Groups = groups.ToArray();
|
||||
|
||||
CreateHostBuilder(args, configuration)
|
||||
.Build()
|
||||
.Run();
|
||||
}
|
||||
|
||||
private static IHostBuilder CreateHostBuilder(string[] args, IConfiguration configuration) {
|
||||
var config = configuration.GetSection("WebServer");
|
||||
|
||||
return Host.CreateDefaultBuilder(args).ConfigureWebHostDefaults(webBuilder => {
|
||||
webBuilder.UseStartup<Startup>();
|
||||
webBuilder.UseUrls("http://0.0.0.0:" + config.GetValue<int>("Port"));
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"$schema": "http://json.schemastore.org/launchsettings.json",
|
||||
"iisSettings": {
|
||||
"windowsAuthentication": false,
|
||||
"anonymousAuthentication": true,
|
||||
"iisExpress": {
|
||||
"applicationUrl": "http://localhost:21799",
|
||||
"sslPort": 44331
|
||||
}
|
||||
},
|
||||
"profiles": {
|
||||
"IIS Express": {
|
||||
"commandName": "IISExpress",
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "swagger",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"WebDesktopBackend": {
|
||||
"commandName": "Project",
|
||||
"dotnetRunMessages": "true",
|
||||
"launchBrowser": false,
|
||||
"launchUrl": "swagger",
|
||||
"applicationUrl": "https://localhost:5001;http://localhost:5000",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
namespace WebDesktopBackend.Security.Authentication
|
||||
{
|
||||
public static class JwtTokenAuthentication
|
||||
{
|
||||
public const string Scheme = "JwtTokenAuthentication";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using WebDesktopBackend.Options;
|
||||
|
||||
namespace WebDesktopBackend.Security.Authentication
|
||||
{
|
||||
public static class JwtTokenAuthenticationExtensions
|
||||
{
|
||||
public static AuthenticationBuilder AddJwtTokenAuthentication(this AuthenticationBuilder builder, IConfiguration configuration)
|
||||
{
|
||||
builder.Services.AddOptionsFromConfiguration<JwtTokenAuthenticationOptions>(configuration);
|
||||
|
||||
return builder.AddScheme<JwtTokenAuthenticationHandlerOptions, JwtTokenAuthenticationHandler>(
|
||||
JwtTokenAuthentication.Scheme,
|
||||
_ => { });
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
using WebDesktopBackend.Entitys.Tokens;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using System.Text.Encodings.Web;
|
||||
using System.Threading.Tasks;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using WebDesktopBackend.Contract.Persistance;
|
||||
using WebDesktopBackend.Entitys.Permissions;
|
||||
using WebDesktopBackend.Security.Authorization;
|
||||
|
||||
#pragma warning disable CS1998 // Async method lacks 'await' operators and will run synchronously
|
||||
|
||||
namespace WebDesktopBackend.Security.Authentication
|
||||
{
|
||||
public class JwtTokenAuthenticationHandler : AuthenticationHandler<JwtTokenAuthenticationHandlerOptions>
|
||||
{
|
||||
private readonly ITokenRepository _tokens;
|
||||
private readonly IGroupRepository _groups;
|
||||
|
||||
public JwtTokenAuthenticationHandler(
|
||||
IOptionsMonitor<JwtTokenAuthenticationHandlerOptions> options,
|
||||
ILoggerFactory logger,
|
||||
UrlEncoder encoder,
|
||||
ISystemClock clock,
|
||||
ITokenRepository tokens,
|
||||
IGroupRepository groups)
|
||||
: base(options, logger, encoder, clock)
|
||||
{
|
||||
_tokens = tokens;
|
||||
_groups = groups;
|
||||
}
|
||||
|
||||
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
|
||||
{
|
||||
var accessToken = GetAccessToken();
|
||||
if (accessToken == null) return AuthenticateResult.Fail("Access Token invalid");
|
||||
var refreshToken = _tokens.GetRefreshToken(accessToken.RefreshTokenId);
|
||||
if (refreshToken == null) return AuthenticateResult.Fail("Refresh Token invalid");
|
||||
if (!_tokens.ValidateRefreshToken(refreshToken.Id)) return AuthenticateResult.Fail("Refresh Token invalid");
|
||||
bool valid = _tokens.ValidateAccessToken(accessToken.Id);
|
||||
return valid ?
|
||||
AuthenticateResult.Success(GetAuthenticationTicket(accessToken, refreshToken)) :
|
||||
AuthenticateResult.Fail("Access Token invalid");
|
||||
}
|
||||
|
||||
private AuthenticationTicket GetAuthenticationTicket(AccessToken accessToken, RefreshToken refreshToken) {
|
||||
List<Claim> claims = GenerateClaims(accessToken, refreshToken);
|
||||
ClaimsPrincipal principal = new ClaimsPrincipal();
|
||||
principal.AddIdentity(new ClaimsIdentity(claims, JwtTokenAuthentication.Scheme));
|
||||
AuthenticationTicket ticket = new AuthenticationTicket(principal, Scheme.Name);
|
||||
return ticket;
|
||||
}
|
||||
|
||||
private List<Claim> GenerateClaims(AccessToken accessToken, RefreshToken refreshToken) {
|
||||
List<Claim> claims = new List<Claim>() {
|
||||
new (CustomClaimTypes.AccessTokenId, accessToken.Id),
|
||||
new (CustomClaimTypes.RefreshTokenId, refreshToken.Id),
|
||||
new (CustomClaimTypes.UserId, refreshToken.UserId),
|
||||
};
|
||||
|
||||
Permission[] permissions = _groups.GetUserPermissions(refreshToken.UserId)
|
||||
.Where(perm => perm.Type == Permission.Allow).ToArray();
|
||||
claims.AddRange(permissions.Select(permission => new Claim(CustomClaimTypes.Permission, permission.PermissionName)));
|
||||
|
||||
return claims;
|
||||
}
|
||||
|
||||
private AccessToken GetAccessToken() {
|
||||
string key = Request.Headers["Authorization"];
|
||||
if (string.IsNullOrEmpty(key)) {
|
||||
key = Request.Query["token"];
|
||||
}
|
||||
AccessToken token = _tokens.GetAccessToken(key);
|
||||
return token;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#pragma warning restore CS1998 // Async method lacks 'await' operators and will run synchronously
|
||||
@@ -0,0 +1,10 @@
|
||||
using Microsoft.AspNetCore.Authentication;
|
||||
|
||||
namespace WebDesktopBackend.Security.Authentication
|
||||
{
|
||||
public class JwtTokenAuthenticationHandlerOptions : AuthenticationSchemeOptions
|
||||
{
|
||||
// Options for the authentication handler.
|
||||
// Currently: None
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
using WebDesktopBackend.Options;
|
||||
|
||||
namespace WebDesktopBackend.Security.Authentication
|
||||
{
|
||||
public class JwtTokenAuthenticationOptions : OptionsFromConfiguration
|
||||
{
|
||||
public override string Position => "JwtTokenAuthentication:Jwt";
|
||||
|
||||
public string RefreshTokenExpirationTimeInHours { get; set; }
|
||||
public string AccessTokenExpirationTimeInMinutes { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
|
||||
namespace WebDesktopBackend.Security.Authorization
|
||||
{
|
||||
public sealed class AuthorizedAttribute : TypeFilterAttribute
|
||||
{
|
||||
public AuthorizedAttribute(params string[] permission) : base(typeof(AuthorizedFilter)) {
|
||||
Arguments = new object[] { permission };
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.AspNetCore.Mvc.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc.Filters;
|
||||
using System.Linq;
|
||||
|
||||
namespace WebDesktopBackend.Security.Authorization
|
||||
{
|
||||
public class AuthorizedFilter : IAuthorizationFilter
|
||||
{
|
||||
private readonly string[] _permissions;
|
||||
|
||||
public AuthorizedFilter(params string[] permissions)
|
||||
{
|
||||
_permissions = permissions;
|
||||
}
|
||||
|
||||
public void OnAuthorization(AuthorizationFilterContext context)
|
||||
{
|
||||
if (EndpointHasAllowAnonymousFilter(context))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (!IsAuthenticated(context))
|
||||
{
|
||||
context.Result = new UnauthorizedResult();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!ContainsRequiredRole(context))
|
||||
{
|
||||
context.Result = new ForbidResult();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool EndpointHasAllowAnonymousFilter(AuthorizationFilterContext context)
|
||||
{
|
||||
return context.Filters.Any(item => item is IAllowAnonymousFilter);
|
||||
}
|
||||
|
||||
private bool IsAuthenticated(AuthorizationFilterContext context)
|
||||
{
|
||||
return context.HttpContext.User.Identity.IsAuthenticated;
|
||||
}
|
||||
|
||||
private bool ContainsRequiredRole(AuthorizationFilterContext context) {
|
||||
if (_permissions.Length == 0)
|
||||
return true;
|
||||
|
||||
if (context.HttpContext.User.HasClaim(CustomClaimTypes.Permission, "*"))
|
||||
return true;
|
||||
|
||||
foreach (var permission in _permissions) {
|
||||
string[] splice = permission.Split(".");
|
||||
string cache = "";
|
||||
foreach (var s in splice) {
|
||||
cache += s + ".";
|
||||
if (context.HttpContext.User.HasClaim(CustomClaimTypes.Permission, cache + "*"))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
|
||||
namespace WebDesktopBackend.Security.Authorization
|
||||
{
|
||||
public static class ClaimsPrincipalExtensions
|
||||
{
|
||||
public static string GetAccessTokenId(this ClaimsPrincipal principal) => principal.FindFirstValue(CustomClaimTypes.AccessTokenId);
|
||||
public static string GetRefreshTokenId(this ClaimsPrincipal principal) => principal.FindFirstValue(CustomClaimTypes.RefreshTokenId);
|
||||
public static string GetUserId(this ClaimsPrincipal principal) => principal.FindFirstValue(CustomClaimTypes.UserId);
|
||||
|
||||
public static string[] GetPermissions(this ClaimsPrincipal principal) => principal.Claims
|
||||
.Where(claim => claim.Type.Equals(CustomClaimTypes.Permission))
|
||||
.Select(claim => claim.Value)
|
||||
.ToArray();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace WebDesktopBackend.Security.Authorization
|
||||
{
|
||||
public static class CustomClaimTypes
|
||||
{
|
||||
public const string AccessTokenId = "WebDesktop.AccessTokenId";
|
||||
public const string RefreshTokenId = "WebDesktop.RefreshTokenId";
|
||||
public const string UserId = "WebDesktop.UserId";
|
||||
public const string Permission = "WebDesktop.Permission";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace WebDesktopBackend.Security
|
||||
{
|
||||
public interface ITokenContext {
|
||||
bool IsAuthenticated {get;}
|
||||
string UserId {get;}
|
||||
string AccessTokenId {get;}
|
||||
string RefreshTokenId {get;}
|
||||
string[] Permissions { get; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using WebDesktopBackend.Security.Authorization;
|
||||
|
||||
namespace WebDesktopBackend.Security
|
||||
{
|
||||
internal class TokenContext : ITokenContext
|
||||
{
|
||||
private readonly IHttpContextAccessor _accessor;
|
||||
|
||||
public TokenContext(IHttpContextAccessor accessor) {
|
||||
_accessor = accessor;
|
||||
}
|
||||
|
||||
public bool IsAuthenticated => _accessor.HttpContext.User.Identity.IsAuthenticated;
|
||||
|
||||
public string UserId => _accessor.HttpContext?.User.GetUserId();
|
||||
|
||||
public string AccessTokenId => _accessor.HttpContext?.User.GetAccessTokenId();
|
||||
|
||||
public string RefreshTokenId => _accessor.HttpContext?.User.GetRefreshTokenId();
|
||||
|
||||
public string[] Permissions => _accessor.HttpContext?.User.GetPermissions();
|
||||
}
|
||||
}
|
||||
95
Projekte/WebDesktop/WebDesktopBackend/Startup.cs
Normal file
95
Projekte/WebDesktop/WebDesktopBackend/Startup.cs
Normal file
@@ -0,0 +1,95 @@
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Http.Features;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using WebDesktopBackend.Contract.Logic;
|
||||
using WebDesktopBackend.Contract.Persistance;
|
||||
using WebDesktopBackend.Controller;
|
||||
using WebDesktopBackend.Logic;
|
||||
using WebDesktopBackend.Options;
|
||||
using WebDesktopBackend.Persistance;
|
||||
using WebDesktopBackend.Security;
|
||||
using WebDesktopBackend.Security.Authentication;
|
||||
|
||||
namespace WebDesktopBackend {
|
||||
public class Startup {
|
||||
|
||||
public Startup(IConfiguration configuration) {
|
||||
Configuration = configuration;
|
||||
CreateTables();
|
||||
}
|
||||
|
||||
public IConfiguration Configuration { get; }
|
||||
|
||||
// This method gets called by the runtime. Use this method to add services to the container.
|
||||
public void ConfigureServices(IServiceCollection services) {
|
||||
services.AddDbContext<DatabaseContext>();
|
||||
services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
|
||||
services.AddScoped<ITokenContext, TokenContext>();
|
||||
|
||||
//Repositorys
|
||||
services.AddScoped<ITokenRepository, TokenRepository>();
|
||||
services.AddScoped<IUserRepository, UserRepository>();
|
||||
services.AddScoped<IFileRepository, FileRepository>();
|
||||
services.AddScoped<IGroupRepository, GroupRepository>();
|
||||
|
||||
//Logic
|
||||
services.AddScoped<IUserLogic, UserLogic>();
|
||||
services.AddScoped<IFileLogic, FileLogic>();
|
||||
|
||||
services.AddOptionsFromConfiguration<JwtTokenAuthenticationOptions>(Configuration);
|
||||
services.AddOptionsFromConfiguration<FileSystemOptions>(Configuration);
|
||||
services.AddCors();
|
||||
services.AddAuthentication(JwtTokenAuthentication.Scheme).AddJwtTokenAuthentication(Configuration);
|
||||
services.AddControllers();
|
||||
services.Configure<FormOptions>(x => {
|
||||
x.KeyLengthLimit = int.MaxValue;
|
||||
x.ValueCountLimit = int.MaxValue;
|
||||
x.ValueLengthLimit = int.MaxValue;
|
||||
x.MultipartBodyLengthLimit = long.MaxValue;
|
||||
x.BufferBodyLengthLimit = long.MaxValue;
|
||||
x.MultipartBoundaryLengthLimit = int.MaxValue;
|
||||
x.MultipartHeadersCountLimit = int.MaxValue;
|
||||
x.MultipartHeadersLengthLimit = int.MaxValue;
|
||||
});
|
||||
}
|
||||
|
||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) {
|
||||
if (env.IsProduction()) {
|
||||
app.UseHttpsRedirection();
|
||||
}
|
||||
|
||||
app.UseCors(
|
||||
options => options
|
||||
.WithOrigins(Configuration.GetSection("WebServer:Origins").Get<string[]>())
|
||||
.AllowAnyMethod()
|
||||
.AllowAnyHeader()
|
||||
.AllowCredentials()
|
||||
);
|
||||
|
||||
app.UseRouting();
|
||||
|
||||
app.UseAuthentication();
|
||||
app.UseAuthorization();
|
||||
|
||||
app.UseWebSockets();
|
||||
|
||||
app.UseEndpoints(endpoints => { endpoints.MapControllers(); });
|
||||
}
|
||||
|
||||
private void CreateTables() {
|
||||
using var mySql = new MySql();
|
||||
mySql.Insert("CREATE TABLE IF NOT EXISTS Users (Id VARCHAR(50) PRIMARY KEY, FirstName VARCHAR(255), LastName VARCHAR(255), Email VARCHAR(255), Username VARCHAR(255), Password VARCHAR(255), Created TIMESTAMP)");
|
||||
mySql.Insert("CREATE TABLE IF NOT EXISTS RefreshTokens (Id VARCHAR(50) PRIMARY KEY, UserId VARCHAR(50), ExpirationDate TIMESTAMP)");
|
||||
mySql.Insert("CREATE TABLE IF NOT EXISTS AccessTokens (Id VARCHAR(50) PRIMARY KEY, RefreshTokenId VARCHAR(50), ExpirationDate TIMESTAMP)");
|
||||
mySql.Insert("CREATE TABLE IF NOT EXISTS Permissions (Id INT PRIMARY KEY AUTO_INCREMENT, UserId VARCHAR(50), PermissionName VARCHAR(100), Type INT(1))");
|
||||
mySql.Insert("CREATE TABLE IF NOT EXISTS FileShares (Id VARCHAR(50) PRIMARY KEY, Owner VARCHAR(50), Files TEXT)");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||
<LangVersion>9</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="idunno.Authentication.Certificate" Version="2.2.3" />
|
||||
<PackageReference Include="MySql.EntityFrameworkCore" Version="6.0.0" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.3.1" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,9 @@
|
||||
{
|
||||
"BlenderRenderer": {
|
||||
"BlenderPath": "C:\\Program Files\\Blender Foundation\\Blender 3.0\\",
|
||||
"BlenderVersion": "3.0"
|
||||
},
|
||||
"FileSystem": {
|
||||
"RootDirectory": "./Uploads/"
|
||||
}
|
||||
}
|
||||
46
Projekte/WebDesktop/WebDesktopBackend/appsettings.json
Normal file
46
Projekte/WebDesktop/WebDesktopBackend/appsettings.json
Normal file
@@ -0,0 +1,46 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Information",
|
||||
"WebDesktopBackend.Security.Authentication.JwtTokenAuthenticationHandler": "None"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*",
|
||||
"JwtTokenAuthentication": {
|
||||
"Jwt": {
|
||||
"RefreshTokenExpirationTimeInHours": 12,
|
||||
"AccessTokenExpirationTimeInMinutes": 5
|
||||
}
|
||||
},
|
||||
"PasswordSalt": [237,209,57,94,32,3,146,144,139,153,6,50,215,113,235,250],
|
||||
"WebServer": {
|
||||
"Origins": ["https://leon-hoppe.de", "http://localhost:8080", "http://localhost:4200"],
|
||||
"Port": 4041
|
||||
},
|
||||
"Groups": [
|
||||
{
|
||||
"Permission": "group.admin",
|
||||
"Name": "Admin",
|
||||
"Inherits": [],
|
||||
"Permissions": ["*"]
|
||||
},
|
||||
{
|
||||
"Permission": "group.moderator",
|
||||
"Name": "Moderator",
|
||||
"Inherits": [],
|
||||
"Permissions": ["app.*"]
|
||||
},
|
||||
{
|
||||
"Permission": "group.user",
|
||||
"Name": "User",
|
||||
"Inherits": [],
|
||||
"Permissions": []
|
||||
}
|
||||
],
|
||||
"FileSystem": {
|
||||
"RootDirectory": "/home/files/",
|
||||
"MaxSizePerUserInMb": 1024
|
||||
}
|
||||
}
|
||||
7
Projekte/WebDesktop/WebDesktopBackend/global.json
Normal file
7
Projekte/WebDesktop/WebDesktopBackend/global.json
Normal file
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"sdk": {
|
||||
"version": "6.0",
|
||||
"rollForward": "latestMajor",
|
||||
"allowPrerelease": false
|
||||
}
|
||||
}
|
||||
16
Projekte/WebDesktop/WebDesktopFrontend/.browserslistrc
Normal file
16
Projekte/WebDesktop/WebDesktopFrontend/.browserslistrc
Normal file
@@ -0,0 +1,16 @@
|
||||
# This file is used by the build system to adjust CSS and JS output to support the specified browsers below.
|
||||
# For additional information regarding the format and rule options, please see:
|
||||
# https://github.com/browserslist/browserslist#queries
|
||||
|
||||
# For the full list of supported browsers by the Angular framework, please see:
|
||||
# https://angular.io/guide/browser-support
|
||||
|
||||
# You can see what browsers were selected by your queries by running:
|
||||
# npx browserslist
|
||||
|
||||
last 1 Chrome version
|
||||
last 1 Firefox version
|
||||
last 2 Edge major versions
|
||||
last 2 Safari major versions
|
||||
last 2 iOS major versions
|
||||
Firefox ESR
|
||||
9
Projekte/WebDesktop/WebDesktopFrontend/.dockerignore
Normal file
9
Projekte/WebDesktop/WebDesktopFrontend/.dockerignore
Normal file
@@ -0,0 +1,9 @@
|
||||
.git
|
||||
.firebase
|
||||
.editorconfig
|
||||
/node_modules
|
||||
/e2e
|
||||
/docs
|
||||
.gitignore
|
||||
*.zip
|
||||
*.md
|
||||
16
Projekte/WebDesktop/WebDesktopFrontend/.editorconfig
Normal file
16
Projekte/WebDesktop/WebDesktopFrontend/.editorconfig
Normal file
@@ -0,0 +1,16 @@
|
||||
# Editor configuration, see https://editorconfig.org
|
||||
root = true
|
||||
|
||||
[*]
|
||||
charset = utf-8
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
insert_final_newline = true
|
||||
trim_trailing_whitespace = true
|
||||
|
||||
[*.ts]
|
||||
quote_type = single
|
||||
|
||||
[*.md]
|
||||
max_line_length = off
|
||||
trim_trailing_whitespace = false
|
||||
46
Projekte/WebDesktop/WebDesktopFrontend/.gitignore
vendored
Normal file
46
Projekte/WebDesktop/WebDesktopFrontend/.gitignore
vendored
Normal file
@@ -0,0 +1,46 @@
|
||||
# See http://help.github.com/ignore-files/ for more about ignoring files.
|
||||
|
||||
# compiled output
|
||||
/dist
|
||||
/tmp
|
||||
/out-tsc
|
||||
# Only exists if Bazel was run
|
||||
/bazel-out
|
||||
|
||||
# dependencies
|
||||
/node_modules
|
||||
|
||||
# profiling files
|
||||
chrome-profiler-events*.json
|
||||
|
||||
# IDEs and editors
|
||||
/.idea
|
||||
.project
|
||||
.classpath
|
||||
.c9/
|
||||
*.launch
|
||||
.settings/
|
||||
*.sublime-workspace
|
||||
|
||||
# IDE - VSCode
|
||||
.vscode/*
|
||||
!.vscode/settings.json
|
||||
!.vscode/tasks.json
|
||||
!.vscode/launch.json
|
||||
!.vscode/extensions.json
|
||||
.history/*
|
||||
|
||||
# misc
|
||||
/.angular/cache
|
||||
/.sass-cache
|
||||
/connect.lock
|
||||
/coverage
|
||||
/libpeerconnection.log
|
||||
npm-debug.log
|
||||
yarn-error.log
|
||||
testem.log
|
||||
/typings
|
||||
|
||||
# System Files
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
10
Projekte/WebDesktop/WebDesktopFrontend/Dockerfile
Normal file
10
Projekte/WebDesktop/WebDesktopFrontend/Dockerfile
Normal file
@@ -0,0 +1,10 @@
|
||||
#stage 1
|
||||
FROM node:latest as node
|
||||
WORKDIR /app
|
||||
COPY . .
|
||||
RUN npm install
|
||||
RUN npm run build --prod
|
||||
#stage 2
|
||||
FROM nginx:alpine
|
||||
COPY nginx.conf /etc/nginx/sites-available/default
|
||||
COPY --from=node /app/dist/WebDesktopFrontend /usr/share/nginx/html
|
||||
27
Projekte/WebDesktop/WebDesktopFrontend/README.md
Normal file
27
Projekte/WebDesktop/WebDesktopFrontend/README.md
Normal file
@@ -0,0 +1,27 @@
|
||||
# WebDesktopFrontend
|
||||
|
||||
This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 13.0.1.
|
||||
|
||||
## Development server
|
||||
|
||||
Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files.
|
||||
|
||||
## Code scaffolding
|
||||
|
||||
Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`.
|
||||
|
||||
## Build
|
||||
|
||||
Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory.
|
||||
|
||||
## Running unit tests
|
||||
|
||||
Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io).
|
||||
|
||||
## Running end-to-end tests
|
||||
|
||||
Run `ng e2e` to execute the end-to-end tests via a platform of your choice. To use this command, you need to first add a package that implements end-to-end testing capabilities.
|
||||
|
||||
## Further help
|
||||
|
||||
To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI Overview and Command Reference](https://angular.io/cli) page.
|
||||
244
Projekte/WebDesktop/WebDesktopFrontend/angular.json
Normal file
244
Projekte/WebDesktop/WebDesktopFrontend/angular.json
Normal file
@@ -0,0 +1,244 @@
|
||||
{
|
||||
"$schema": "./node_modules/@angular/cli/lib/config/schema.json",
|
||||
"version": 1,
|
||||
"newProjectRoot": "projects",
|
||||
"projects": {
|
||||
"WebDesktopFrontend": {
|
||||
"projectType": "application",
|
||||
"schematics": {
|
||||
"@schematics/angular:application": {
|
||||
"strict": true,
|
||||
"style": "scss"
|
||||
}
|
||||
},
|
||||
"root": "",
|
||||
"sourceRoot": "src",
|
||||
"prefix": "app",
|
||||
"architect": {
|
||||
"build": {
|
||||
"builder": "@angular-devkit/build-angular:browser",
|
||||
"options": {
|
||||
"outputPath": "dist/WebDesktopFrontend",
|
||||
"index": "src/index.html",
|
||||
"main": "src/main.ts",
|
||||
"polyfills": "src/polyfills.ts",
|
||||
"tsConfig": "tsconfig.app.json",
|
||||
"assets": [
|
||||
"src/favicon.ico",
|
||||
"src/assets",
|
||||
"src/manifest.webmanifest"
|
||||
],
|
||||
"styles": [
|
||||
"./node_modules/@angular/material/prebuilt-themes/indigo-pink.css",
|
||||
"./node_modules/bootstrap/dist/css/bootstrap.css",
|
||||
|
||||
"src/fonts.scss",
|
||||
"src/variables.scss",
|
||||
"src/styles.scss",
|
||||
"src/themes.scss"
|
||||
],
|
||||
"scripts": [
|
||||
"./node_modules/bootstrap/dist/js/bootstrap.bundle.js",
|
||||
"./node_modules/codemirror/lib/codemirror.js",
|
||||
"./node_modules/codemirror/addon/mode/simple.js",
|
||||
|
||||
"./node_modules/codemirror/mode/apl/apl.js",
|
||||
"./node_modules/codemirror/mode/asciiarmor/asciiarmor.js",
|
||||
"./node_modules/codemirror/mode/asn.1/asn.1.js",
|
||||
"./node_modules/codemirror/mode/asterisk/asterisk.js",
|
||||
"./node_modules/codemirror/mode/brainfuck/brainfuck.js",
|
||||
"./node_modules/codemirror/mode/clike/clike.js",
|
||||
"./node_modules/codemirror/mode/clojure/clojure.js",
|
||||
"./node_modules/codemirror/mode/cmake/cmake.js",
|
||||
"./node_modules/codemirror/mode/cobol/cobol.js",
|
||||
"./node_modules/codemirror/mode/coffeescript/coffeescript.js",
|
||||
"./node_modules/codemirror/mode/commonlisp/commonlisp.js",
|
||||
"./node_modules/codemirror/mode/crystal/crystal.js",
|
||||
"./node_modules/codemirror/mode/css/css.js",
|
||||
"./node_modules/codemirror/mode/cypher/cypher.js",
|
||||
"./node_modules/codemirror/mode/d/d.js",
|
||||
"./node_modules/codemirror/mode/dart/dart.js",
|
||||
"./node_modules/codemirror/mode/diff/diff.js",
|
||||
"./node_modules/codemirror/mode/django/django.js",
|
||||
"./node_modules/codemirror/mode/dockerfile/dockerfile.js",
|
||||
"./node_modules/codemirror/mode/dtd/dtd.js",
|
||||
"./node_modules/codemirror/mode/dylan/dylan.js",
|
||||
"./node_modules/codemirror/mode/ebnf/ebnf.js",
|
||||
"./node_modules/codemirror/mode/ecl/ecl.js",
|
||||
"./node_modules/codemirror/mode/eiffel/eiffel.js",
|
||||
"./node_modules/codemirror/mode/elm/elm.js",
|
||||
"./node_modules/codemirror/mode/erlang/erlang.js",
|
||||
"./node_modules/codemirror/mode/factor/factor.js",
|
||||
"./node_modules/codemirror/mode/fcl/fcl.js",
|
||||
"./node_modules/codemirror/mode/forth/forth.js",
|
||||
"./node_modules/codemirror/mode/fortran/fortran.js",
|
||||
"./node_modules/codemirror/mode/gas/gas.js",
|
||||
"./node_modules/codemirror/mode/gfm/gfm.js",
|
||||
"./node_modules/codemirror/mode/gherkin/gherkin.js",
|
||||
"./node_modules/codemirror/mode/go/go.js",
|
||||
"./node_modules/codemirror/mode/groovy/groovy.js",
|
||||
"./node_modules/codemirror/mode/haml/haml.js",
|
||||
"./node_modules/codemirror/mode/handlebars/handlebars.js",
|
||||
"./node_modules/codemirror/mode/haskell/haskell.js",
|
||||
"./node_modules/codemirror/mode/haskell-literate/haskell-literate.js",
|
||||
"./node_modules/codemirror/mode/haxe/haxe.js",
|
||||
"./node_modules/codemirror/mode/htmlembedded/htmlembedded.js",
|
||||
"./node_modules/codemirror/mode/htmlmixed/htmlmixed.js",
|
||||
"./node_modules/codemirror/mode/http/http.js",
|
||||
"./node_modules/codemirror/mode/idl/idl.js",
|
||||
"./node_modules/codemirror/mode/javascript/javascript.js",
|
||||
"./node_modules/codemirror/mode/jinja2/jinja2.js",
|
||||
"./node_modules/codemirror/mode/jsx/jsx.js",
|
||||
"./node_modules/codemirror/mode/julia/julia.js",
|
||||
"./node_modules/codemirror/mode/livescript/livescript.js",
|
||||
"./node_modules/codemirror/mode/lua/lua.js",
|
||||
"./node_modules/codemirror/mode/markdown/markdown.js",
|
||||
"./node_modules/codemirror/mode/mathematica/mathematica.js",
|
||||
"./node_modules/codemirror/mode/mbox/mbox.js",
|
||||
"./node_modules/codemirror/mode/mirc/mirc.js",
|
||||
"./node_modules/codemirror/mode/mllike/mllike.js",
|
||||
"./node_modules/codemirror/mode/modelica/modelica.js",
|
||||
"./node_modules/codemirror/mode/mscgen/mscgen.js",
|
||||
"./node_modules/codemirror/mode/mumps/mumps.js",
|
||||
"./node_modules/codemirror/mode/nginx/nginx.js",
|
||||
"./node_modules/codemirror/mode/nsis/nsis.js",
|
||||
"./node_modules/codemirror/mode/ntriples/ntriples.js",
|
||||
"./node_modules/codemirror/mode/octave/octave.js",
|
||||
"./node_modules/codemirror/mode/oz/oz.js",
|
||||
"./node_modules/codemirror/mode/pascal/pascal.js",
|
||||
"./node_modules/codemirror/mode/pegjs/pegjs.js",
|
||||
"./node_modules/codemirror/mode/perl/perl.js",
|
||||
"./node_modules/codemirror/mode/php/php.js",
|
||||
"./node_modules/codemirror/mode/pig/pig.js",
|
||||
"./node_modules/codemirror/mode/powershell/powershell.js",
|
||||
"./node_modules/codemirror/mode/properties/properties.js",
|
||||
"./node_modules/codemirror/mode/protobuf/protobuf.js",
|
||||
"./node_modules/codemirror/mode/pug/pug.js",
|
||||
"./node_modules/codemirror/mode/puppet/puppet.js",
|
||||
"./node_modules/codemirror/mode/python/python.js",
|
||||
"./node_modules/codemirror/mode/q/q.js",
|
||||
"./node_modules/codemirror/mode/r/r.js",
|
||||
"./node_modules/codemirror/mode/rpm/rpm.js",
|
||||
"./node_modules/codemirror/mode/rst/rst.js",
|
||||
"./node_modules/codemirror/mode/ruby/ruby.js",
|
||||
"./node_modules/codemirror/mode/rust/rust.js",
|
||||
"./node_modules/codemirror/mode/sas/sas.js",
|
||||
"./node_modules/codemirror/mode/sass/sass.js",
|
||||
"./node_modules/codemirror/mode/scheme/scheme.js",
|
||||
"./node_modules/codemirror/mode/shell/shell.js",
|
||||
"./node_modules/codemirror/mode/sieve/sieve.js",
|
||||
"./node_modules/codemirror/mode/slim/slim.js",
|
||||
"./node_modules/codemirror/mode/smalltalk/smalltalk.js",
|
||||
"./node_modules/codemirror/mode/smarty/smarty.js",
|
||||
"./node_modules/codemirror/mode/solr/solr.js",
|
||||
"./node_modules/codemirror/mode/soy/soy.js",
|
||||
"./node_modules/codemirror/mode/sparql/sparql.js",
|
||||
"./node_modules/codemirror/mode/spreadsheet/spreadsheet.js",
|
||||
"./node_modules/codemirror/mode/sql/sql.js",
|
||||
"./node_modules/codemirror/mode/stex/stex.js",
|
||||
"./node_modules/codemirror/mode/stylus/stylus.js",
|
||||
"./node_modules/codemirror/mode/swift/swift.js",
|
||||
"./node_modules/codemirror/mode/tcl/tcl.js",
|
||||
"./node_modules/codemirror/mode/textile/textile.js",
|
||||
"./node_modules/codemirror/mode/tiddlywiki/tiddlywiki.js",
|
||||
"./node_modules/codemirror/mode/tiki/tiki.js",
|
||||
"./node_modules/codemirror/mode/toml/toml.js",
|
||||
"./node_modules/codemirror/mode/tornado/tornado.js",
|
||||
"./node_modules/codemirror/mode/troff/troff.js",
|
||||
"./node_modules/codemirror/mode/ttcn/ttcn.js",
|
||||
"./node_modules/codemirror/mode/ttcn-cfg/ttcn-cfg.js",
|
||||
"./node_modules/codemirror/mode/turtle/turtle.js",
|
||||
"./node_modules/codemirror/mode/twig/twig.js",
|
||||
"./node_modules/codemirror/mode/vb/vb.js",
|
||||
"./node_modules/codemirror/mode/vbscript/vbscript.js",
|
||||
"./node_modules/codemirror/mode/velocity/velocity.js",
|
||||
"./node_modules/codemirror/mode/verilog/verilog.js",
|
||||
"./node_modules/codemirror/mode/vhdl/vhdl.js",
|
||||
"./node_modules/codemirror/mode/vue/vue.js",
|
||||
"./node_modules/codemirror/mode/wast/wast.js",
|
||||
"./node_modules/codemirror/mode/webidl/webidl.js",
|
||||
"./node_modules/codemirror/mode/xml/xml.js",
|
||||
"./node_modules/codemirror/mode/xquery/xquery.js",
|
||||
"./node_modules/codemirror/mode/yacas/yacas.js",
|
||||
"./node_modules/codemirror/mode/yaml/yaml.js",
|
||||
"./node_modules/codemirror/mode/yaml-frontmatter/yaml-frontmatter.js",
|
||||
"./node_modules/codemirror/mode/z80/z80.js"
|
||||
],
|
||||
"serviceWorker": true,
|
||||
"ngswConfigPath": "ngsw-config.json"
|
||||
},
|
||||
"configurations": {
|
||||
"production": {
|
||||
"budgets": [
|
||||
{
|
||||
"type": "initial",
|
||||
"maximumWarning": "500mb",
|
||||
"maximumError": "500mb"
|
||||
},
|
||||
{
|
||||
"type": "anyComponentStyle",
|
||||
"maximumWarning": "1024mb",
|
||||
"maximumError": "1024mb"
|
||||
}
|
||||
],
|
||||
"fileReplacements": [
|
||||
{
|
||||
"replace": "src/environments/environment.ts",
|
||||
"with": "src/environments/environment.prod.ts"
|
||||
}
|
||||
],
|
||||
"outputHashing": "all"
|
||||
},
|
||||
"development": {
|
||||
"buildOptimizer": false,
|
||||
"optimization": false,
|
||||
"vendorChunk": true,
|
||||
"extractLicenses": false,
|
||||
"sourceMap": true,
|
||||
"namedChunks": true
|
||||
}
|
||||
},
|
||||
"defaultConfiguration": "production"
|
||||
},
|
||||
"serve": {
|
||||
"builder": "@angular-devkit/build-angular:dev-server",
|
||||
"configurations": {
|
||||
"production": {
|
||||
"browserTarget": "WebDesktopFrontend:build:production"
|
||||
},
|
||||
"development": {
|
||||
"browserTarget": "WebDesktopFrontend:build:development"
|
||||
}
|
||||
},
|
||||
"defaultConfiguration": "development"
|
||||
},
|
||||
"extract-i18n": {
|
||||
"builder": "@angular-devkit/build-angular:extract-i18n",
|
||||
"options": {
|
||||
"browserTarget": "WebDesktopFrontend:build"
|
||||
}
|
||||
},
|
||||
"test": {
|
||||
"builder": "@angular-devkit/build-angular:karma",
|
||||
"options": {
|
||||
"main": "src/test.ts",
|
||||
"polyfills": "src/polyfills.ts",
|
||||
"tsConfig": "tsconfig.spec.json",
|
||||
"karmaConfig": "karma.conf.js",
|
||||
"assets": [
|
||||
"src/favicon.ico",
|
||||
"src/assets",
|
||||
"src/manifest.webmanifest"
|
||||
],
|
||||
"styles": [
|
||||
"./node_modules/@angular/material/prebuilt-themes/indigo-pink.css",
|
||||
"src/styles.css"
|
||||
],
|
||||
"scripts": []
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"defaultProject": "WebDesktopFrontend"
|
||||
}
|
||||
44
Projekte/WebDesktop/WebDesktopFrontend/karma.conf.js
Normal file
44
Projekte/WebDesktop/WebDesktopFrontend/karma.conf.js
Normal file
@@ -0,0 +1,44 @@
|
||||
// Karma configuration file, see link for more information
|
||||
// https://karma-runner.github.io/1.0/config/configuration-file.html
|
||||
|
||||
module.exports = function (config) {
|
||||
config.set({
|
||||
basePath: '',
|
||||
frameworks: ['jasmine', '@angular-devkit/build-angular'],
|
||||
plugins: [
|
||||
require('karma-jasmine'),
|
||||
require('karma-chrome-launcher'),
|
||||
require('karma-jasmine-html-reporter'),
|
||||
require('karma-coverage'),
|
||||
require('@angular-devkit/build-angular/plugins/karma')
|
||||
],
|
||||
client: {
|
||||
jasmine: {
|
||||
// you can add configuration options for Jasmine here
|
||||
// the possible options are listed at https://jasmine.github.io/api/edge/Configuration.html
|
||||
// for example, you can disable the random execution with `random: false`
|
||||
// or set a specific seed with `seed: 4321`
|
||||
},
|
||||
clearContext: false // leave Jasmine Spec Runner output visible in browser
|
||||
},
|
||||
jasmineHtmlReporter: {
|
||||
suppressAll: true // removes the duplicated traces
|
||||
},
|
||||
coverageReporter: {
|
||||
dir: require('path').join(__dirname, './coverage/WebDesktopFrontend'),
|
||||
subdir: '.',
|
||||
reporters: [
|
||||
{ type: 'html' },
|
||||
{ type: 'text-summary' }
|
||||
]
|
||||
},
|
||||
reporters: ['progress', 'kjhtml'],
|
||||
port: 9876,
|
||||
colors: true,
|
||||
logLevel: config.LOG_INFO,
|
||||
autoWatch: true,
|
||||
browsers: ['Chrome'],
|
||||
singleRun: false,
|
||||
restartOnFileChange: true
|
||||
});
|
||||
};
|
||||
13
Projekte/WebDesktop/WebDesktopFrontend/nginx.conf
Normal file
13
Projekte/WebDesktop/WebDesktopFrontend/nginx.conf
Normal file
@@ -0,0 +1,13 @@
|
||||
server {
|
||||
listen 80 default_server;
|
||||
listen [::]:80 default_server;
|
||||
|
||||
root /usr/share/nginx/html;
|
||||
index index.html index.htm index.nginx-debian.html;
|
||||
|
||||
server_name _;
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
}
|
||||
30
Projekte/WebDesktop/WebDesktopFrontend/ngsw-config.json
Normal file
30
Projekte/WebDesktop/WebDesktopFrontend/ngsw-config.json
Normal file
@@ -0,0 +1,30 @@
|
||||
{
|
||||
"$schema": "node_modules/@angular/service-worker/config/schema.json",
|
||||
"index": "/index.html",
|
||||
"assetGroups": [
|
||||
{
|
||||
"name": "app",
|
||||
"installMode": "prefetch",
|
||||
"resources": {
|
||||
"files": [
|
||||
"/favicon.ico",
|
||||
"/index.html",
|
||||
"/manifest.webmanifest",
|
||||
"/*.css",
|
||||
"/*.js"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "assets",
|
||||
"installMode": "lazy",
|
||||
"updateMode": "prefetch",
|
||||
"resources": {
|
||||
"files": [
|
||||
"/assets/**",
|
||||
"/*.(svg|cur|jpg|jpeg|png|apng|webp|avif|gif|otf|ttf|woff|woff2)"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
26320
Projekte/WebDesktop/WebDesktopFrontend/package-lock.json
generated
Normal file
26320
Projekte/WebDesktop/WebDesktopFrontend/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
50
Projekte/WebDesktop/WebDesktopFrontend/package.json
Normal file
50
Projekte/WebDesktop/WebDesktopFrontend/package.json
Normal file
@@ -0,0 +1,50 @@
|
||||
{
|
||||
"name": "web-desktop-frontend",
|
||||
"version": "0.0.1",
|
||||
"scripts": {
|
||||
"ng": "ng",
|
||||
"start": "ng serve",
|
||||
"build": "ng build",
|
||||
"watch": "ng build --watch --configuration development",
|
||||
"test": "ng test"
|
||||
},
|
||||
"private": true,
|
||||
"dependencies": {
|
||||
"@angular/animations": "~13.0.0",
|
||||
"@angular/cdk": "^13.0.0",
|
||||
"@angular/common": "~13.0.0",
|
||||
"@angular/compiler": "~13.0.0",
|
||||
"@angular/core": "~13.0.0",
|
||||
"@angular/forms": "~13.0.0",
|
||||
"@angular/material": "^13.0.2",
|
||||
"@angular/platform-browser": "~13.0.0",
|
||||
"@angular/platform-browser-dynamic": "~13.0.0",
|
||||
"@angular/router": "~13.0.0",
|
||||
"@angular/service-worker": "~13.0.0",
|
||||
"@ctrl/ngx-codemirror": "^5.1.1",
|
||||
"bootstrap": "^5.1.3",
|
||||
"codemirror": "^5.65.2",
|
||||
"file-saver": "^2.0.5",
|
||||
"npm": "^8.3.0",
|
||||
"rxjs": "~7.4.0",
|
||||
"sweetalert2": "^11.4.4",
|
||||
"tslib": "^2.3.0",
|
||||
"zone.js": "~0.11.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular-devkit/build-angular": "~13.0.1",
|
||||
"@angular/cli": "~13.0.1",
|
||||
"@angular/compiler-cli": "~13.0.0",
|
||||
"@types/codemirror": "^5.60.5",
|
||||
"@types/file-saver": "^2.0.5",
|
||||
"@types/jasmine": "~3.10.0",
|
||||
"@types/node": "^12.11.1",
|
||||
"jasmine-core": "~3.10.0",
|
||||
"karma": "~6.3.0",
|
||||
"karma-chrome-launcher": "~3.1.0",
|
||||
"karma-coverage": "~2.0.3",
|
||||
"karma-jasmine": "~4.0.0",
|
||||
"karma-jasmine-html-reporter": "~1.7.0",
|
||||
"typescript": "~4.4.3"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { CrudService } from "../crud.service";
|
||||
import {DirectoryContent, DirectoryInformation, FileInformation} from "../entitys/files";
|
||||
import {Observable} from "rxjs";
|
||||
import {HttpEvent} from "@angular/common/http";
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class FileAPI {
|
||||
|
||||
constructor(private service: CrudService) {}
|
||||
|
||||
public async createDirectory(directory: string, name: string): Promise<boolean> {
|
||||
try {
|
||||
await this.service.sendPostRequest<boolean>("files/upload/directory?directory=" + directory + "&name=" + name, null, {authorized: true});
|
||||
return true;
|
||||
}catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public async uploadFile(file: File, directory: string): Promise<Observable<HttpEvent<object>>> {
|
||||
const form = new FormData();
|
||||
form.append("file", file, file.name);
|
||||
form.append("directory", directory);
|
||||
|
||||
await this.service.getNewToken();
|
||||
return this.service.HttpClient.post(this.service.endpoint + "files/upload/file", form, {headers: this.service.httpHeader, reportProgress: true, observe: "events"});
|
||||
}
|
||||
|
||||
public async quickUpload(file: File, directory: string) {
|
||||
const form = new FormData();
|
||||
form.append("file", file, file.name);
|
||||
form.append("directory", directory);
|
||||
|
||||
await this.service.sendPostRequest("files/upload/file", form);
|
||||
}
|
||||
|
||||
public async uploadJson(directory: string, name: string, content: any) {
|
||||
await this.service.sendPostRequest("files/upload/json?directory=" + directory + "&name=" + name, content, {authorized: true});
|
||||
}
|
||||
|
||||
public async downloadJson<T>(file: string) {
|
||||
return await this.service.sendGetRequest<T>("files/download/json?file=" + file, {authorized: true});
|
||||
}
|
||||
|
||||
public async downloadFile(directory: string, file: string): Promise<Observable<HttpEvent<object>>> {
|
||||
await this.service.getNewToken();
|
||||
return this.service.HttpClient.get(this.service.endpoint + "files/download/file?directory=" + directory + "&file=" + file, {headers: this.service.httpHeader, responseType: 'blob', reportProgress: true, observe: "events"});
|
||||
}
|
||||
|
||||
public async delete(url: string) {
|
||||
await this.service.sendDeleteRequest("files/delete?url=" + url, {authorized: true});
|
||||
}
|
||||
|
||||
public async getDirectoryContent(directory: string): Promise<DirectoryContent> {
|
||||
try {
|
||||
return await this.service.sendGetRequest<DirectoryContent>("files/content?directory=" + directory, {authorized: true});
|
||||
} catch {
|
||||
return { files: [], directories: [] };
|
||||
}
|
||||
}
|
||||
|
||||
public async getDirectoryInfo(directory: string): Promise<DirectoryInformation> {
|
||||
const info = await this.service.sendGetRequest<DirectoryInformation>("files/info/directory?directory=" + directory, {authorized: true});
|
||||
info.created = new Date(info.created);
|
||||
return info;
|
||||
}
|
||||
|
||||
public async getFileInfo(directory: string, file: string): Promise<FileInformation> {
|
||||
const info = await this.service.sendGetRequest<FileInformation>("files/info/files?directory=" + directory + "&file=" + file, {authorized: true});
|
||||
info.created = new Date(info.created);
|
||||
return info;
|
||||
}
|
||||
|
||||
public async moveDirectory(directory: string, name: string, to: string) {
|
||||
await this.service.sendPutRequest("files/move/directory?directory=" + directory + "&name=" + name + "&to=" + to, null, {authorized: true});
|
||||
}
|
||||
|
||||
public async moveFile(directory: string, file: string, to: string) {
|
||||
await this.service.sendPutRequest("files/move/file?directory=" + directory + "&file=" + file + "&to=" + to, null, {authorized: true});
|
||||
}
|
||||
|
||||
public toFile(content: string, name: string): File {
|
||||
const blob = new Blob([content], {type: "text/plain"});
|
||||
return new File([blob], name, {type: "text/plain"});
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import {Injectable} from '@angular/core';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class SettingsService {
|
||||
public static eventChange: CustomEvent = new CustomEvent<any>("settings_change");
|
||||
|
||||
constructor() { }
|
||||
|
||||
public setSetting(key: string, value: any) {
|
||||
localStorage.setItem(key, JSON.stringify(value));
|
||||
|
||||
document.dispatchEvent(SettingsService.eventChange);
|
||||
}
|
||||
|
||||
public getSetting<T>(key: string, defaultValue?: T): T {
|
||||
return JSON.parse(localStorage.getItem(key)) || defaultValue;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
import { Injectable } from '@angular/core';
|
||||
import { CrudService } from '../crud.service';
|
||||
import { Token } from '../entitys/token';
|
||||
import { User, UserEditor, UserLogin } from '../entitys/user';
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
export class UserAPI {
|
||||
private user: User;
|
||||
|
||||
constructor(private service: CrudService) { }
|
||||
|
||||
async login(login: UserLogin): Promise<boolean> {
|
||||
try {
|
||||
const accessToken = await this.service.sendPutRequest<Token>("users/login", JSON.stringify(login), {withCredentials: true});
|
||||
this.service.setAccessToken(accessToken.id);
|
||||
return true;
|
||||
}catch(err) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async register(editor: UserEditor): Promise<boolean> {
|
||||
try {
|
||||
const accessToken = await this.service.sendPostRequest<Token>("users/register", JSON.stringify(editor), {withCredentials: true});
|
||||
this.service.setAccessToken(accessToken.id);
|
||||
return true;
|
||||
}catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async logout(): Promise<void> {
|
||||
await this.service.sendDeleteRequest("users/logout", {authorized: true, withCredentials: true});
|
||||
}
|
||||
|
||||
async editUser(id: string, editor: UserEditor): Promise<boolean> {
|
||||
try {
|
||||
await this.service.sendPutRequest("users/" + id, JSON.stringify(editor), {authorized: true});
|
||||
return true;
|
||||
}catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async editOwnUser(editor: UserEditor) {
|
||||
try {
|
||||
await this.service.sendPutRequest("users/ownuser", JSON.stringify(editor), {authorized: true});
|
||||
return true;
|
||||
}catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async deleteOwnUser() {
|
||||
try {
|
||||
await this.service.sendDeleteRequest("users/ownuser", {authorized: true, withCredentials: true});
|
||||
localStorage.clear();
|
||||
return true;
|
||||
}catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async deleteUser(id: string): Promise<boolean> {
|
||||
try {
|
||||
await this.service.sendDeleteRequest("users/" + id, {authorized: true});
|
||||
return true;
|
||||
}catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async getUser(data: {id?: string, username?: string, email?: string}): Promise<User | null> {
|
||||
if (data.id !== undefined) {
|
||||
return this.service.sendGetRequest("users/" + data.id);
|
||||
}
|
||||
const users = await this.getUsers();
|
||||
if (data.username !== undefined) {
|
||||
return users.find(user => user.username == data.username) as User;
|
||||
}
|
||||
if (data.email !== undefined) {
|
||||
return users.find(user => user.email == data.email) as User;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
async getUsers(): Promise<User[]> {
|
||||
return this.service.sendGetRequest<User[]>("users");
|
||||
}
|
||||
|
||||
async isLoggedIn(): Promise<boolean> {
|
||||
try {
|
||||
return await this.service.sendGetRequest("users/validate", {authorized: true, withCredentials: true, dontResendOnExpiration: true, returnNewTokenResponse: true}) === true;
|
||||
}catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async loadUser(): Promise<boolean> {
|
||||
if (!(await this.isLoggedIn())) return false;
|
||||
this.user = await this.service.sendGetRequest<User>("users/ownuser", {authorized: true});
|
||||
this.user.created = new Date(this.user.created.toString());
|
||||
return true;
|
||||
}
|
||||
|
||||
async checkForPermission(permission: string): Promise<boolean> {
|
||||
await this.getCurrentUser();
|
||||
const permissions = await this.service.sendGetRequest<string[]>("users/permissions", {authorized: true});
|
||||
if (permissions.includes("*") || permissions.includes(permission))
|
||||
return true;
|
||||
|
||||
const splice = permission.split(".");
|
||||
let builder: string = "";
|
||||
for (let pp of splice) {
|
||||
builder += pp + ".";
|
||||
if (permissions.includes(builder + "*"))
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
async getOwnPermissions(): Promise<string[]> {
|
||||
await this.getCurrentUser();
|
||||
return this.service.sendGetRequest<string[]>("users/permissions", {authorized: true});
|
||||
}
|
||||
|
||||
async getPermissions(id: string): Promise<string[]> {
|
||||
return this.service.sendGetRequest<string[]>("users/" + id + "/permissions/raw", {authorized: true});
|
||||
}
|
||||
|
||||
async addPermission(id: string, permission: string): Promise<boolean> {
|
||||
try {
|
||||
await this.service.sendPostRequest("users/" + id + "/permissions/" + permission, undefined, {authorized: true});
|
||||
return true;
|
||||
}catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async removePermission(id: string, permission: string): Promise<boolean> {
|
||||
try {
|
||||
await this.service.sendDeleteRequest("users/" + id + "/permissions/" + permission, {authorized: true});
|
||||
return true;
|
||||
}catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public async getCurrentUser(forceReload: boolean = false): Promise<User> {
|
||||
if (this.user != undefined && !forceReload)
|
||||
return this.user;
|
||||
else {
|
||||
await this.loadUser();
|
||||
return this.user;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import { NgModule } from '@angular/core';
|
||||
import { RouterModule, Routes } from '@angular/router';
|
||||
import { LoginComponent } from "./login/login.component";
|
||||
import { Desktop } from "./desktop/desktop.component";
|
||||
import { RegisterComponent } from './register/register.component';
|
||||
|
||||
const routes: Routes = [
|
||||
{ path: 'login', component: LoginComponent },
|
||||
{ path: 'register', component: RegisterComponent },
|
||||
{ path: '', component: Desktop },
|
||||
{ path: '*', redirectTo: '' }
|
||||
];
|
||||
|
||||
@NgModule({
|
||||
imports: [RouterModule.forRoot(routes)],
|
||||
exports: [RouterModule]
|
||||
})
|
||||
export class AppRoutingModule { }
|
||||
@@ -0,0 +1 @@
|
||||
<router-outlet *ngIf="ready"></router-outlet>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user