Example #1
0
        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");
        }
Example #2
0
        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);
            }
        }
Example #3
0
        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);
            }
        }
Example #4
0
        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);
            }
        }
Example #5
0
        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);
            }
        }
Example #6
0
        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);
            });
        }
Example #7
0
        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));
                }
            }
        }
Example #10
0
        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("/");
            }
        }
Example #14
0
        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");
            }
        }
Example #16
0
        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);
            }
        }