public static async Task AuthorizeUploadTus(AuthorizeContext arg) { CommonController.LogDebug("Authorizing upload request"); var user = await UserSessions.GetLoggedInUser(arg.HttpContext); if (user != null) { var video = new Video { id = new Guid(arg.HttpContext.Request.Headers["guid"]), userid = user.userid, privacy = VideoPrivacy.Processing, }; CommonController.LogDebug("Video metadata:"); CommonController.LogDebug($"\t id: {video.id}"); CommonController.LogDebug($"\t userid: {video.userid}"); CommonController.LogDebug($"\t privacy: {video.privacy}"); var cachedOwner = (User)uploadAuthorisationCache[video.id.ToString()]; bool exists; bool owns; if (cachedOwner == null) { CommonController.LogDebug("No owner found in cache"); using var connection = Database.OpenNewConnection(); exists = await VideoExists(video.id, connection); owns = await UserOwnsVideo(video.id, user.userid, connection); await AddOrUpdateVideo(video, connection); } //NOTE(Simon): At this point the video has definitely been created, so it exists and is owned by the cached user else if (cachedOwner.userid == user.userid) { CommonController.LogDebug("User is owner of video. allow upload"); exists = true; owns = true; } else { CommonController.LogDebug("User not authorized to update this video"); arg.FailRequest("This user is not authorized to update this video"); return; } if (exists && owns || !exists) { CommonController.LogDebug("Adding user to cache"); uploadAuthorisationCache.Add(video.id.ToString(), user, new CacheItemPolicy { SlidingExpiration = TimeSpan.FromMinutes(10) }); return; } } arg.FailRequest("This user is not authorized to update this video"); CommonController.LogDebug("User not authorized to upload videos"); }
private static async Task DeleteVideoConfirmGet(HttpContext context) { CommonController.SetHTMLContentType(context); var userTask = UserSessions.GetLoggedInUser(context); using var connection = Database.OpenNewConnection(); var user = await userTask; if (user != null && GuidHelpers.TryDecode(context.Request.Query["id"], out var videoId)) { var video = await GetVideo(videoId, connection); if (UserOwnsVideo(video, user.userid)) { await DeleteVideo(video.id, connection); context.Response.Redirect("/my_videos"); } else { await CommonController.Write404(context); } } else { await CommonController.Write404(context); } }
private static async Task EditVideoGet(HttpContext context) { CommonController.SetHTMLContentType(context); var userTask = UserSessions.GetLoggedInUser(context); using var connection = Database.OpenNewConnection(); var user = await userTask; if (user != null && GuidHelpers.TryDecode(context.Request.Query["id"], out var videoId)) { var video = await GetVideo(videoId, connection); if (UserOwnsVideo(video, user.userid)) { var templateContext = new TemplateContext(new { video }); await context.Response.WriteAsync(await HTMLRenderer.Render(context, "Templates\\editVideo.liquid", templateContext)); } else { await CommonController.Write404(context); } } else { await CommonController.Write404(context); } }
private static async Task MyVideosGet(HttpContext context) { CommonController.SetHTMLContentType(context); var userTask = UserSessions.GetLoggedInUser(context); using var connection = Database.OpenNewConnection(); var user = await userTask; if (user != null) { const int countPerPage = 20; int page = 1; if (context.Request.Query.ContainsKey("page")) { Int32.TryParse(context.Request.Query["page"], out page); } int offset = (page - 1) * countPerPage; var numVideos = await NumVideosForUser(user.userid, connection); var VideosTask = VideosForUser(user.userid, countPerPage, offset, connection); var pagination = new Pagination(numVideos, countPerPage, offset); var templateContext = new TemplateContext(new { videos = await VideosTask, pagination }); await context.Response.WriteAsync(await HTMLRenderer.Render(context, "Templates\\myVideos.liquid", templateContext)); } else { await CommonController.Write404(context); } }
private static async Task VideoGet(HttpContext context) { CommonController.SetHTMLContentType(context); if (GuidHelpers.TryDecode(context.Request.Query["id"], out var videoId)) { using var connection = Database.OpenNewConnection(); var video = await GetVideo(videoId, connection); var user = await UserSessions.GetLoggedInUser(context); if (video != null && UserCanViewVideo(video, user)) { bool userOwnsVideo = UserOwnsVideo(video, user.userid); var relatedVideos = new List <Video>(); var templateContext = new TemplateContext(new { video, relatedVideos, userOwnsVideo }); await context.Response.WriteAsync(await HTMLRenderer.Render(context, "Templates\\video.liquid", templateContext)); await AddVideoView(video.id, context, connection); } else { await CommonController.Write404(context); } } else { await CommonController.Write404(context); } }
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); CommonController.baseURL = "https://localhost:5001"; } else { app.UseHsts(); CommonController.baseURL = "https://vivista.net"; } CommonController.wwwroot = env.WebRootPath; app.UseStaticFiles(); app.UseTus(context => { if (!context.Request.Path.StartsWithSegments(new PathString("/api/file")) || context.Request.Method == "GET") { return(null); } var guid = new Guid(context.Request.Headers["guid"]); string path = Path.Combine(VideoController.baseFilePath, guid.ToString()); return(new DefaultTusConfiguration { Store = new TusDiskStore(path), UrlPath = "/api/file", Events = new Events { OnAuthorizeAsync = VideoController.AuthorizeUploadTus, OnBeforeCreateAsync = async createContext => Directory.CreateDirectory(path), //NOTE(Simon): Do not allow deleting by someone trying to exploit the protocol OnBeforeDeleteAsync = async deleteContext => deleteContext.FailRequest(""), OnFileCompleteAsync = VideoController.ProcessUploadTus, } }); }); //Task.Run(PeriodicFunction); app.Run(async(context) => { var watch = Stopwatch.StartNew(); var authenticateTask = UserSessions.GetLoggedInUser(context); PrintDebugData(context); SetJSONContentType(context); await authenticateTask; watch.Stop(); CommonController.LogDebug($"request preamble: {watch.Elapsed.TotalMilliseconds} ms"); await router.RouteAsync(context.Request, context); }); }
public static async Task <string> Render(HttpContext httpContext, string templateName, TemplateContext context, BaseLayout layout = BaseLayout.Web) { CommonController.LogDebug($"Begin Rendering {templateName}"); var watch = Stopwatch.StartNew(); if (templateCache.TryGetValue(templateName, out var template)) { CommonController.LogDebug("Template found"); var user = await UserSessions.GetLoggedInUser(httpContext); string result; if (context == null) { context = new TemplateContext(); context.Options.MemberAccessStrategy = defaultAccessStrategy; context.Options.Scope.SetValue("User", FluidValue.Create(user, context.Options)); AddDefaultFilters(context); result = await template.RenderAsync(); } else { //NOTE(Simon): This lib requires users to register all used models by their type name. //NOTE(cont.): This block prevents having to do it manually. context.Options.MemberAccessStrategy = defaultAccessStrategy; context.Options.Scope.SetValue("User", FluidValue.Create(user, context.Options)); AddDefaultFilters(context); result = await template.RenderAsync(context); } if (layout != BaseLayout.None) { var content = new TemplateContext(new { content = result }); context.Options.MemberAccessStrategy = defaultAccessStrategy; content.Options.Scope.SetValue("User", FluidValue.Create(user, context.Options)); AddDefaultFilters(context); result = await layoutCache[layout].RenderAsync(content); } watch.Stop(); CommonController.LogDebug($"rendering: {watch.Elapsed.TotalMilliseconds} ms"); return(result); } else { Console.WriteLine("Template not found"); throw new Exception($"Something went wrong while rendering {templateName}"); } }
private static async Task LogoutGet(HttpContext context) { if (await UserSessions.GetLoggedInUser(context) != null) { using var connection = Database.OpenNewConnection(); await UserSessions.InvalidateSession(context.Request.Cookies["session"], connection); context.Response.Cookies.Delete("session"); context.Response.Redirect("/"); } }
private static async Task UpdatePasswordPost(HttpContext context) { SetHTMLContentType(context); string currentPassword = context.Request.Form["current-password"]; string password = context.Request.Form["new-password"]; string passwordConfirmation = context.Request.Form["password-confirmation"]; var(success, result) = ValidatePassword(password, passwordConfirmation); if (!success) { var templateContext = new TemplateContext(new { success = false, message = result }); await context.Response.WriteAsync(await HTMLRenderer.Render(context, "Templates\\settings.liquid", templateContext)); } else { using var connection = Database.OpenNewConnection(); var user = await UserSessions.GetLoggedInUser(context); var currentPassCorrect = await AuthenticateUser(user.email, currentPassword, connection); if (currentPassCorrect) { if (await UpdatePassword(user.email, password, connection)) { var templateContext = new TemplateContext(new { success = true, message = "" }); await context.Response.WriteAsync(await HTMLRenderer.Render(context, "Templates\\settings.liquid", templateContext)); } else { var templateContext = new TemplateContext(new { success = false, message = "Something went wrong while updating password. Please try again later" }); await context.Response.WriteAsync(await HTMLRenderer.Render(context, "Templates\\settings.liquid", templateContext)); } } else { var templateContext = new TemplateContext(new { success = false, message = "Current password is wrong" }); await context.Response.WriteAsync(await HTMLRenderer.Render(context, "Templates\\settings.liquid", templateContext)); } } }
private static async Task EditVideoPost(HttpContext context) { CommonController.SetHTMLContentType(context); var userTask = UserSessions.GetLoggedInUser(context); using var connection = Database.OpenNewConnection(); var user = await userTask; if (user != null && GuidHelpers.TryDecode(context.Request.Query["id"], out var videoId)) { var video = await GetVideo(videoId, connection); if (UserOwnsVideo(video, user.userid)) { var form = context.Request.Form; video.title = form["title"]; video.description = form["description"]; //TODO(Simon): Deduplicate tags. Should be cleaned by frontend, but may be malicious data. string[] tags = form["tags"].ToString().Split(','); var deduplicatedTags = new HashSet <string>(tags); video.tags = deduplicatedTags.ToList(); if (Int32.TryParse(form["privacy"], out var privacyInt)) { video.privacy = (VideoPrivacy)privacyInt; } await AddOrUpdateVideo(video, connection); context.Response.Redirect("/my_videos"); } else { await CommonController.Write404(context); } } else { await CommonController.Write404(context); } }
private static async Task RegisterPost(HttpContext context) { SetHTMLContentType(context); string result; bool success; var model = new RegisterModel { username = context.Request.Form["username"].ToString(), email = context.Request.Form["email"].ToString().NormalizeEmail(), error = "" }; try { (success, result) = await RegisterWithForm(context); } catch (Exception e) { model.error = "An unknown error happened while registering this account. Please try again later."; var templateContext = new TemplateContext(model); await context.Response.WriteAsync(await HTMLRenderer.Render(context, "Templates\\register.liquid", templateContext)); return; } if (!success) { model.error = result; var templateContext = new TemplateContext(model); await context.Response.WriteAsync(await HTMLRenderer.Render(context, "Templates\\register.liquid", templateContext)); } else { UserSessions.SetSessionCookie(context, result); await context.Response.WriteAsync(await HTMLRenderer.Render(context, "Templates\\registerSuccess.liquid", null)); } }
private static async Task <(bool success, string result)> LoginWithForm(HttpContext context) { using var connection = Database.OpenNewConnection(); var form = context.Request.Form; string email = form["email"].ToString().NormalizeEmail(); string password = form["password"]; bool success = await AuthenticateUser(email, password, connection); if (success) { var userid = await UserIdFromEmail(email, connection); var token = await UserSessions.CreateNewSession(userid, connection); return(true, token); } else { return(false, "This combination of email and password was not found"); } }
private static async Task LoginPost(HttpContext context) { SetHTMLContentType(context); string result; bool success; var model = new LoginModel { email = context.Request.Form["email"].ToString().NormalizeEmail(), }; try { (success, result) = await LoginWithForm(context); } catch (Exception e) { model.error = "An unknown error happened while logging in. Please try again later."; var templateContext = new TemplateContext(model); await context.Response.WriteAsync(await HTMLRenderer.Render(context, "Templates\\login.liquid", templateContext)); return; } if (!success) { model.error = result; var templateContext = new TemplateContext(model); await context.Response.WriteAsync(await HTMLRenderer.Render(context, "Templates\\login.liquid", templateContext)); } else { UserSessions.SetSessionCookie(context, result); context.Response.Redirect("/"); } }
private static async Task UpdateVideoPrivacyPost(HttpContext context) { CommonController.SetHTMLContentType(context); var userTask = UserSessions.GetLoggedInUser(context); using var connection = Database.OpenNewConnection(); var user = await userTask; if (user != null && GuidHelpers.TryDecode(context.Request.Query["id"], out var videoid)) { var video = await GetVideo(videoid, connection); if (UserOwnsVideo(video, user.userid)) { if (Int32.TryParse(context.Request.Form["video-privacy"], out int privacy)) { await SetVideoPrivacy(video.id, (VideoPrivacy)privacy, connection); } } } context.Response.Redirect("/my_videos"); }
private static async Task <(bool success, string result)> RegisterWithForm(HttpContext context) { if (context.Request.HasFormContentType) { var form = context.Request.Form; string username = form["username"].ToString().Trim(); string password = form["password"].ToString(); string passwordConfirmation = form["password-confirmation"].ToString(); string email = form["email"].ToString().NormalizeEmail(); if (username.Length < 3) { return(false, "Username too short"); } var(success, result) = ValidatePassword(password, passwordConfirmation); if (!success) { return(false, result); } //NOTE(Simon): Shortest possible is [email protected] if (email.Length < 5) { return(false, "Email too short"); } using var connection = Database.OpenNewConnection(); bool userExists = await UserExists(email, connection); string hashedPassword = BCrypt.Net.BCrypt.HashPassword(password, bcryptWorkFactor); if (!userExists) { string verificationToken = Tokens.NewVerifyEmailToken(); int querySuccess = await connection.ExecuteAsync(@"insert into users (username, email, pass, verification_token) values (@username, @email, @hashedPassword, @verificationToken)" , new { username, email, hashedPassword, verificationToken }); if (querySuccess == 0) { throw new Exception("Something went wrong while writing new user to db"); } await EmailClient.SendEmailConfirmationMail(email, verificationToken); } else { return(false, "This user already exists"); } //NOTE(Simon): Create session token to immediately log user in. { int userid = await UserIdFromEmail(email, connection); string token = await UserSessions.CreateNewSession(userid, connection); return(true, token); } } else { return(false, "Request did not contain a form"); } }
private static async Task FinishUploadApi(HttpContext context) { var form = context.Request.Form; if (Guid.TryParse(form["id"], out var guid)) { var userTask = UserSessions.GetLoggedInUser(context); using var connection = Database.OpenNewConnection(); var user = await userTask; var video = await GetVideo(guid, connection); if (user != null && await UserOwnsVideo(guid, user.userid, connection)) { var directoryPath = Path.Combine(baseFilePath, guid.ToString()); var videoPath = Path.Combine(directoryPath, "main.mp4"); var thumbPath = Path.Combine(directoryPath, "thumb.jpg"); var process = new Process(); process.StartInfo.UseShellExecute = false; process.StartInfo.RedirectStandardOutput = true; process.StartInfo.RedirectStandardError = true; string ffmpegArgs = $"-hide_banner -loglevel error -y -ss 00:00:05 -i {videoPath} -frames:v 1 -q:v 3 -s 720x360 {thumbPath}"; if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { //process.StartInfo.FileName = @"\bin\ffmpeg.exe"; process.StartInfo.FileName = "/usr/bin/ffmpeg"; process.StartInfo.Arguments = $"{ffmpegArgs}"; //process.StartInfo.Arguments = $"-c ffmpeg {ffmpegArgs}"; CommonController.LogDebug($"Running following command: {process.StartInfo.Arguments}"); } else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { process.StartInfo.FileName = "cmd.exe"; process.StartInfo.Arguments = $"/C ffmpeg.exe {ffmpegArgs}"; CommonController.LogDebug($"Running following command: {process.StartInfo.Arguments}"); } process.Start(); await process.WaitForExitAsync(); CommonController.LogDebug($"Ffmpeg exit code: {process.ExitCode}"); if (process.ExitCode == 0) { CommonController.LogDebug("Thumbnail generated succesfully"); video.privacy = VideoPrivacy.Unlisted; video.downloadsize = (int)GetDirectorySize(new DirectoryInfo(directoryPath)); video.length = await FfmpegGetVideoLength(videoPath); var meta = ReadMetaFile(Path.Combine(directoryPath, "meta.json")); video.title = meta.title; video.description = meta.description; CommonController.LogDebug($"Video Privacy: {video.privacy}"); CommonController.LogDebug($"Video Size: {video.downloadsize}"); CommonController.LogDebug($"Video length: {video.length}"); if (await AddOrUpdateVideo(video, connection)) { CommonController.LogDebug("Database row updated succesfully"); await context.Response.WriteAsJsonAsync(new { success = true }); CleanPartialUploads(video.id); } else { Console.WriteLine("Error while updating database row for video"); await CommonController.WriteError(context, "{}", StatusCodes.Status500InternalServerError); } } else { Console.WriteLine("Error while generating thumbnail"); Console.WriteLine($"\t {process.StandardOutput.ReadToEnd()}"); Console.WriteLine($"\t {process.StandardError.ReadToEnd()}"); await CommonController.WriteError(context, "{}", StatusCodes.Status500InternalServerError); } } else { CommonController.LogDebug("User does not own video"); await CommonController.WriteError(context, "{}", StatusCodes.Status401Unauthorized); } } else { await CommonController.WriteError(context, "{}", StatusCodes.Status400BadRequest); } }