Archived
Private
Public Access
1
0

Initial commit

This commit is contained in:
2022-09-04 12:45:01 +02:00
commit f4a01d6a69
11601 changed files with 4206660 additions and 0 deletions

View 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

View File

@@ -0,0 +1,3 @@
/obj
/bin
/Uploads

View 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

View 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>

View 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>

View 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>

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="UserContentModel">
<attachedFolders />
<explicitIncludes />
<explicitExcludes />
</component>
</project>

View 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>

View 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>

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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";
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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));
}
}
}

View File

@@ -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;
}
}
}
}

View File

@@ -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"];
}
}
}

View 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);
});
}
}
}

View 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"]

View File

@@ -0,0 +1,6 @@
namespace WebDesktopBackend.Entitys.Files {
public class DirectoryContent {
public string[] Files { get; set; }
public string[] Directories { get; set; }
}
}

View File

@@ -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; }
}
}

View File

@@ -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; }
}
}

View File

@@ -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; }
}
}

View File

@@ -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; }
}
}

View File

@@ -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; }
}
}

View File

@@ -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; }
}
}

View File

@@ -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; }
}
}

View File

@@ -0,0 +1,6 @@
namespace WebDesktopBackend.Entitys.Tokens {
public class Tokens {
public RefreshToken refreshToken { get; set; }
public AccessToken accessToken { get; set; }
}
}

View 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"
};
}
}
}

View File

@@ -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();
}
}
}

View File

@@ -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; }
}
}

View File

@@ -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.
}
}
}

View File

@@ -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;
}
}
}

View 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("./", "/");
}
}
}

View 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;
}
}
}

View File

@@ -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.");
}
}
}
}

View File

@@ -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; }
}
}

View File

@@ -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
};
}
}
}

View File

@@ -0,0 +1,9 @@
namespace WebDesktopBackend.LogicResults {
public enum LogicResultState {
Ok,
BadRequest,
Forbidden,
NotFound,
Conflict
}
}

View 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();
}
}
}

View File

@@ -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; }
}
}

View File

@@ -0,0 +1,7 @@
namespace WebDesktopBackend.Options
{
public abstract class OptionsFromConfiguration
{
public abstract string Position { get; }
}
}

View File

@@ -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;
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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();
}
}
}

View File

@@ -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();
}
}
}

View File

@@ -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 ""; }
}
}
}

View 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"));
});
}
}
}

View File

@@ -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"
}
}
}
}

View File

@@ -0,0 +1,7 @@
namespace WebDesktopBackend.Security.Authentication
{
public static class JwtTokenAuthentication
{
public const string Scheme = "JwtTokenAuthentication";
}
}

View File

@@ -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,
_ => { });
}
}
}

View File

@@ -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

View File

@@ -0,0 +1,10 @@
using Microsoft.AspNetCore.Authentication;
namespace WebDesktopBackend.Security.Authentication
{
public class JwtTokenAuthenticationHandlerOptions : AuthenticationSchemeOptions
{
// Options for the authentication handler.
// Currently: None
}
}

View File

@@ -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; }
}
}

View File

@@ -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 };
}
}
}

View File

@@ -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;
}
}
}

View File

@@ -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();
}
}

View File

@@ -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";
}
}

View File

@@ -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; }
}
}

View File

@@ -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();
}
}

View 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)");
}
}
}

View File

@@ -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>

View File

@@ -0,0 +1,9 @@
{
"BlenderRenderer": {
"BlenderPath": "C:\\Program Files\\Blender Foundation\\Blender 3.0\\",
"BlenderVersion": "3.0"
},
"FileSystem": {
"RootDirectory": "./Uploads/"
}
}

View 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
}
}

View File

@@ -0,0 +1,7 @@
{
"sdk": {
"version": "6.0",
"rollForward": "latestMajor",
"allowPrerelease": false
}
}