Пример #1
0
        public async Task <IActionResult> OnPost()
        {
            var lang        = GetLanguage();
            var validator   = new UserProfileDataValidationService(ModelState, LanguageProvider, lang);
            var validations = new[]
            {
                validator.ValidateUsername(nameof(UserName), UserName),
                validator.ValidateEmail(nameof(Email), Email),
                validator.ValidatePassword(nameof(Password), Password),
                validator.ValidateSecondPassword(nameof(SecondPassword), SecondPassword, Password),
                validator.ValidateTermsAgreement(nameof(Agree), Agree)
            };

            if (!validations.All(x => x))
            {
                return(Page());
            }

            try
            {
                var response = await _gClient.PostAsync(
                    requestUri : _recaptchaOptions.RelativeUri,
                    content : new StringContent(
                        content: $"secret={_recaptchaOptions.SecretKey}&response={RecaptchaResponse}&remoteip={HttpContext.Connection.RemoteIpAddress}",
                        encoding: Encoding.UTF8,
                        mediaType: "application/x-www-form-urlencoded"
                        )
                    );

                response.EnsureSuccessStatusCode();
                var resultText = await response.Content.ReadAsStringAsync();

                var result = JsonConvert.DeserializeObject <dynamic>(resultText);
                if ((bool?)result?.success != true)
                {
                    throw new InvalidOperationException($"Validating g-recaptcha failed. Response: {resultText}");
                }
                if ((decimal)result.score < _recaptchaOptions.MinScore)
                {
                    return(PageWithError(nameof(RecaptchaResponse), string.Format(LanguageProvider.Errors[lang, "YOURE_A_BOT_FORMAT"], _config.GetValue <string>("AdminEmail").Replace("@", " at ").Replace(".", " dot "))));
                }
            }
            catch (Exception ex)
            {
                _utils.HandleErrorAsWarning(ex, "Failed to check captcha");
                return(PageWithError(nameof(RecaptchaResponse), LanguageProvider.Errors[lang, "AN_ERROR_OCCURRED_TRY_AGAIN"]));
            }

            var conn = _context.GetDbConnection();

            var checkBanlist = await conn.QueryAsync(
                @"SELECT @email LIKE LOWER(REPLACE(REPLACE(ban_email, '*', '%'), '?', '_')) AS Email,
                         @ip LIKE LOWER(REPLACE(REPLACE(ban_ip, '*', '%'), '?', '_')) AS IP
                    FROM phpbb_banlist
                   WHERE @email LIKE LOWER(REPLACE(REPLACE(ban_email, '*', '%'), '?', '_')) 
                      OR @ip LIKE LOWER(REPLACE(REPLACE(ban_ip, '*', '%'), '?', '_'))",
                new { email = Email !.ToLower(), ip = HttpContext.Connection.RemoteIpAddress?.ToString() ?? string.Empty }
Пример #2
0
        public async Task <IActionResult> OnPost()
        {
            var connection = _context.GetDbConnection();

            var user = await connection.QueryAsync <PhpbbUsers>("SELECT * FROM phpbb_users WHERE username_clean = @username", new { username = _utils.CleanString(UserName) });

            var lang = LanguageProvider.GetValidatedLanguage(null, Request);

            Mode = LoginMode.Normal;
            if (user.Count() != 1)
            {
                ModelState.AddModelError(nameof(LoginErrorMessage), LanguageProvider.Errors[lang, "WRONG_USER_PASS"]);
                return(Page());
            }

            var currentUser = user.First();

            if (currentUser.UserInactiveReason != UserInactiveReason.NotInactive || currentUser.UserInactiveTime != 0)
            {
                ModelState.AddModelError(nameof(LoginErrorMessage), LanguageProvider.Errors[lang, "INACTIVE_USER"]);
                return(Page());
            }

            if (currentUser.UserPassword != Crypter.Phpass.Crypt(Password !, currentUser.UserPassword))
            {
                ModelState.AddModelError(nameof(LoginErrorMessage), LanguageProvider.Errors[lang, "WRONG_USER_PASS"]);
                return(Page());
            }

            await HttpContext.SignInAsync(
                CookieAuthenticationDefaults.AuthenticationScheme,
                await _userService.DbUserToClaimsPrincipal(currentUser),
                new AuthenticationProperties
            {
                AllowRefresh = true,
                ExpiresUtc   = DateTimeOffset.Now.Add(_config.GetValue <TimeSpan?>("LoginSessionSlidingExpiration") ?? TimeSpan.FromDays(30)),
                IsPersistent = true,
            });

            var key = $"UserMustLogIn_{currentUser.UsernameClean}";

            if (await _cache.GetAsync <bool?>(key) ?? false)
            {
                _cache.Remove(key);
            }

            var returnUrl = HttpUtility.UrlDecode(ReturnUrl ?? "/");

            if (returnUrl.StartsWith("/login", StringComparison.InvariantCultureIgnoreCase) ||
                returnUrl.StartsWith("/logout", StringComparison.InvariantCultureIgnoreCase) ||
                returnUrl.StartsWith("/confirm", StringComparison.InvariantCultureIgnoreCase))
            {
                returnUrl = "/";
            }
            return(Redirect(returnUrl));
        }
Пример #3
0
        public async Task <(Guid CorrelationId, Dictionary <int, List <AttachmentDto> > Attachments)> CacheAttachmentsAndPrepareForDisplay(List <PhpbbAttachments> dbAttachments, string language, int postCount, bool isPreview)
        {
            var correlationId = Guid.NewGuid();
            var attachments   = new Dictionary <int, List <AttachmentDto> >(postCount);
            var ids           = new List <int>(dbAttachments.Count);

            foreach (var attach in dbAttachments)
            {
                var dto = new AttachmentDto(attach, isPreview, language, correlationId);
                if (!attachments.ContainsKey(attach.PostMsgId))
                {
                    attachments.Add(attach.PostMsgId, new List <AttachmentDto>(_maxAttachmentCount)
                    {
                        dto
                    });
                }
                else
                {
                    attachments[attach.PostMsgId].Add(dto);
                }
                if (attach.Mimetype.IsMimeTypeInline())
                {
                    _cache.Add(_utils.GetAttachmentCacheKey(attach.AttachId, correlationId), dto, TimeSpan.FromSeconds(60));
                    ids.Add(attach.AttachId);
                }
            }

            if (!isPreview && ids.Any())
            {
                try
                {
                    var conn = _context.GetDbConnection();
                    await conn.ExecuteAsync(
                        "UPDATE phpbb_attachments SET download_count = download_count + 1 WHERE attach_id IN @ids",
                        new { ids }
                        );
                }
                catch (Exception ex)
                {
                    _utils.HandleErrorAsWarning(ex, "Error updating attachment download count");
                }
            }

            return(correlationId, attachments);
        }
Пример #4
0
        public async Task <PhpbbUsers> GetAnonymousDbUser()
        {
            if (_anonymousDbUser != null)
            {
                return(_anonymousDbUser);
            }
            var connection = _context.GetDbConnection();

            _anonymousDbUser = await connection.QuerySingleOrDefaultAsync <PhpbbUsers>("SELECT * FROM phpbb_users WHERE user_id = @userId", new { userId = Constants.ANONYMOUS_USER_ID });

            return(_anonymousDbUser);
        }
Пример #5
0
        public async Task <(string Message, bool?IsSuccess)> ChangeTopicType(int topicId, TopicType topicType, OperationLogDto logDto)
        {
            try
            {
                var conn = _context.GetDbConnection();
                var rows = await conn.ExecuteAsync("UPDATE phpbb_topics SET topic_type = @topicType WHERE topic_id = @topicId", new { topicType, topicId });

                if (rows == 1)
                {
                    await _operationLogService.LogModeratorTopicAction((ModeratorTopicActions)logDto.Action !, logDto.UserId, topicId);

                    return(LanguageProvider.Moderator[GetLanguage(), "TOPIC_CHANGED_SUCCESSFULLY"], true);
                }
                else
                {
                    return(string.Format(LanguageProvider.Moderator[GetLanguage(), "TOPIC_DOESNT_EXIST_FORMAT"], topicId), false);
                }
            }
            catch (Exception ex)
            {
                var id = Utils.HandleError(ex);
                return(string.Format(LanguageProvider.Errors[GetLanguage(), "AN_ERROR_OCCURRED_TRY_AGAIN_ID_FORMAT"], id), false);
            }
        }
Пример #6
0
        public async Task <int> GetFirstUnreadPost(int forumId, int topicId)
        {
            if (GetCurrentUser().IsAnonymous)
            {
                return(0);
            }
            Tracking?item  = null;
            var      found = (await GetForumTree(false, true)).Tracking !.TryGetValue(forumId, out var tt) && tt.TryGetValue(new Tracking {
                TopicId = topicId
            }, out item);

            if (!found)
            {
                return(0);
            }

            var connection = Context.GetDbConnection();

            return(unchecked ((int)((await connection.QuerySingleOrDefaultAsync(
                                         "SELECT post_id, post_time FROM phpbb_posts WHERE post_id IN @postIds HAVING post_time = MIN(post_time)",
                                         new { postIds = item !.Posts?.DefaultIfEmpty() ?? new int[] { default } }
Пример #7
0
        public async Task <IActionResult> OnGet(int?f, int?t, int?p, int?start)
        {
            if (t.HasValue)
            {
                if (start.HasValue)
                {
                    var conn = _context.GetDbConnection();
                    var post = await conn.QueryFirstOrDefaultAsync <PhpbbPosts>(
                        "SELECT * FROM phpbb_posts WHERE topic_id = @topicId ORDER BY post_time LIMIT @skip, 1",
                        new
                    {
                        topicId = t.Value,
                        skip    = start >= 1 ? start - 1 : 0
                    }
                        );

                    if (post != null)
                    {
                        return(RedirectToPage("../ViewTopic", "ByPostId", new { post.PostId }));
                    }
                    else
                    {
                        return(RedirectToPage("../ViewTopic", new { TopicId = t.Value, PageNum = 1 }));
                    }
                }
                else
                {
                    return(RedirectToPage("../ViewTopic", new { TopicId = t.Value, PageNum = 1 }));
                }
            }
            else if (p.HasValue)
            {
                return(RedirectToPage("../ViewTopic", "ByPostId", new { PostId = p.Value }));
            }
            else
            {
                _utils.HandleErrorAsWarning(new Exception($"Bad request to legacy viewtopic.php route: {Request.QueryString.Value}"));
                return(RedirectToPage("../Index"));
            }
        }
Пример #8
0
        public async Task <Statistics> GetStatistics()
        => await _cache.GetOrAddAsync(
            CACHE_KEY,
            async() =>
        {
            var conn      = _dbContext.GetDbConnection();
            var timeTask  = conn.ExecuteScalarAsync <long>("SELECT min(post_time) FROM phpbb_posts");
            var userTask  = conn.ExecuteScalarAsync <int>("SELECT count(1) FROM phpbb_users");
            var postTask  = conn.ExecuteScalarAsync <int>("SELECT count(1) FROM phpbb_posts");
            var topicTask = conn.ExecuteScalarAsync <int>("SELECT count(1) FROM phpbb_topics");
            var forumTask = conn.ExecuteScalarAsync <int>("SELECT count(1) FROM phpbb_forums");
            await Task.WhenAll(timeTask, userTask, postTask, topicTask, forumTask);

            return(new Statistics
            {
                FirstMessageDate = (await timeTask) == 0 ? null : (await timeTask).ToUtcTime(),
                UserCount = await userTask,
                PostCount = await postTask,
                TopicCount = await topicTask,
                ForumCount = await forumTask
            });
        },
            DateTimeOffset.UtcNow.AddMinutes(RefreshIntervalMinutes)
            );
        public async Task InvokeAsync(HttpContext context, RequestDelegate next)
        {
            if (EXCLUDED_PAGES.Contains(context.Request.Path))
            {
                await next(context);

                return;
            }

            AuthenticatedUserExpanded?user;

            try
            {
                if (context.User?.Identity?.IsAuthenticated != true)
                {
                    user = _userService.ClaimsPrincipalToAuthenticatedUser(await SignInAnonymousUser(context));
                }
                else
                {
                    user = _userService.ClaimsPrincipalToAuthenticatedUser(context.User);
                }
                if (user is null)
                {
                    _logger.Warning("Failed to log in neither a proper user nor the anonymous idendity.");
                    await next(context);

                    return;
                }
            }
            catch (Exception ex)
            {
                _logger.Warning(ex, "Failed to parse claims");
                await next(context);

                return;
            }

            var(isAllowed, dbUser) = await GetDbUserOrAnonymousIfNotAllowed(user);

            if (!isAllowed)
            {
                await context.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);

                user = _userService.ClaimsPrincipalToAuthenticatedUser(await SignInAnonymousUser(context));
            }

            var permissionsTask = _userService.GetPermissions(user !.UserId);
            var tppTask         = GetTopicPostsPage(user.UserId);
            var foesTask        = _userService.GetFoes(user.UserId);
            await Task.WhenAll(permissionsTask, tppTask, foesTask);

            if (dbUser is null)
            {
                var anonymousClaimsPrincipal = await SignInAnonymousUser(context);

                user = _userService.ClaimsPrincipalToAuthenticatedUser(anonymousClaimsPrincipal);
            }

            var sessionTrackingTimeout = _config.GetValue <TimeSpan?>("UserActivityTrackingInterval") ?? TimeSpan.FromHours(1);

            if (dbUser is not null && !user !.IsAnonymous && (
                    await _cache.GetAsync <bool?>($"ReloadUser_{user.UsernameClean}") == true ||
                    DateTime.UtcNow.Subtract(dbUser.UserLastvisit.ToUtcTime()) > sessionTrackingTimeout))
            {
                var claimsPrincipal = await _userService.DbUserToClaimsPrincipal(dbUser);

                await Task.WhenAll(
                    SignIn(context, claimsPrincipal),
                    _context.GetDbConnection().ExecuteAsync(
                        "UPDATE phpbb_users SET user_lastvisit = @now WHERE user_id = @userId",
                        new { now = DateTime.UtcNow.ToUnixTimestamp(), user.UserId }
                        )
                    );

                user = _userService.ClaimsPrincipalToAuthenticatedUser(claimsPrincipal);
            }

            user !.AllPermissions  = await permissionsTask;
            user.TopicPostsPerPage = await tppTask;
            user.Foes = await foesTask;

            context.Items[nameof(AuthenticatedUserExpanded)] = user;

            if (user.IsAnonymous && context.Request.Headers.TryGetValue(HeaderNames.UserAgent, out var header) && (context.Session.GetInt32("SessionCounted") ?? 0) == 0)
            {
                try
                {
                    var userAgent = header.ToString();
                    var dd        = new DeviceDetector(userAgent);
                    dd.Parse();
                    var IsBot = dd.IsBot();
                    if (IsBot)
                    {
                        if (context.Connection.RemoteIpAddress is not null)
                        {
                            _sessionCounter.UpsertBot(context.Connection.RemoteIpAddress.ToString(), userAgent, sessionTrackingTimeout);
                        }
                    }
                    else
                    {
                        context.Session.SetInt32("SessionCounted", 1);
                        _sessionCounter.UpsertSession(context.Session.Id, sessionTrackingTimeout);
                    }
                }
                catch (Exception ex)
                {
                    _logger.Warning(ex, "Failed to detect anonymous session type.");
                }
            }

            await next(context);
        }
Пример #10
0
        public async Task <(string Message, bool?IsSuccess)> ManageUser(AdminUserActions?action, int?userId, PageContext pageContext, HttpContext httpContext, int adminUserId)
        {
            var lang = GetLanguage();

            if (userId == Constants.ANONYMOUS_USER_ID)
            {
                return(LanguageProvider.Admin[lang, "CANT_DELETE_ANONYMOUS_USER"], false);
            }

            var user = await _context.PhpbbUsers.FirstOrDefaultAsync(u => u.UserId == userId);

            if (user == null)
            {
                return(string.Format(LanguageProvider.Admin[lang, "USER_DOESNT_EXIST_FORMAT"], userId ?? 0), false);
            }

            void flagUserAsChanged()
            {
                var key = $"UserMustLogIn_{user.UsernameClean}";

                _cache.Add(key, true, _config.GetValue <TimeSpan?>("LoginSessionSlidingExpiration") ?? TimeSpan.FromDays(30));
            }

            async Task deleteUser()
            {
                _context.PhpbbAclUsers.RemoveRange(_context.PhpbbAclUsers.Where(u => u.UserId == userId));
                _context.PhpbbBanlist.RemoveRange(_context.PhpbbBanlist.Where(u => u.BanUserid == userId));
                _context.PhpbbBots.RemoveRange(_context.PhpbbBots.Where(u => u.UserId == userId));
                _context.PhpbbDrafts.RemoveRange(_context.PhpbbDrafts.Where(u => u.UserId == userId));
                (await _context.PhpbbForums.Where(f => f.ForumLastPosterId == userId).ToListAsync()).ForEach(f =>
                {
                    f.ForumLastPosterId     = 1;
                    f.ForumLastPosterColour = string.Empty;
                    f.ForumLastPosterName   = user.Username;
                });
                _context.PhpbbForumsTrack.RemoveRange(_context.PhpbbForumsTrack.Where(u => u.UserId == userId));
                _context.PhpbbForumsWatch.RemoveRange(_context.PhpbbForumsWatch.Where(u => u.UserId == userId));
                _context.PhpbbLog.RemoveRange(_context.PhpbbLog.Where(u => u.UserId == userId));
                await(_context.GetDbConnection()).ExecuteAsync("DELETE FROM phpbb_poll_votes WHERE vote_user_id = @userId", new { userId });
                _context.PhpbbPrivmsgsTo.RemoveRange(_context.PhpbbPrivmsgsTo.Where(u => u.UserId == userId));
                _context.PhpbbReports.RemoveRange(_context.PhpbbReports.Where(u => u.UserId == userId));
                (await _context.PhpbbTopics.Where(t => t.TopicLastPosterId == userId).ToListAsync()).ForEach(t =>
                {
                    t.TopicLastPosterId     = 1;
                    t.TopicLastPosterColour = string.Empty;
                    t.TopicLastPosterName   = user.Username;
                });
                (await _context.PhpbbTopics.Where(t => t.TopicFirstPosterName == user.Username).ToListAsync()).ForEach(t =>
                {
                    t.TopicFirstPostId       = 1;
                    t.TopicFirstPosterColour = string.Empty;
                });
                _context.PhpbbTopicsTrack.RemoveRange(_context.PhpbbTopicsTrack.Where(u => u.UserId == userId));
                _context.PhpbbTopicsWatch.RemoveRange(_context.PhpbbTopicsWatch.Where(u => u.UserId == userId));
                _context.PhpbbUserGroup.RemoveRange(_context.PhpbbUserGroup.Where(u => u.UserId == userId));
                _context.PhpbbUsers.RemoveRange(_context.PhpbbUsers.Where(u => u.UserId == userId));
                _context.PhpbbUserTopicPostNumber.RemoveRange(_context.PhpbbUserTopicPostNumber.Where(u => u.UserId == userId));
                _context.PhpbbZebra.RemoveRange(_context.PhpbbZebra.Where(u => u.UserId == userId));
                _context.PhpbbUsers.Remove(user);
            }

            try
            {
                string?message   = null;
                bool?  isSuccess = null;
                var    forumName = _config.GetValue <string>("ForumName");
                switch (action)
                {
                case AdminUserActions.Activate:
                {
                    await Utils.SendEmail(
                        to : user.UserEmail,
                        subject : string.Format(LanguageProvider.Email[user.UserLang, "ACCOUNT_ACTIVATED_NOTIFICATION_SUBJECT_FORMAT"], forumName),
                        body : await Utils.RenderRazorViewToString(
                            "_AccountActivatedNotification",
                            new AccountActivatedNotificationDto
                        {
                            Username = user.Username,
                            Language = user.UserLang
                        },
                            pageContext,
                            httpContext
                            ));

                    user.UserInactiveReason = UserInactiveReason.NotInactive;
                    user.UserInactiveTime   = 0L;
                    user.UserReminded       = 0;
                    user.UserRemindedTime   = 0L;
                    message   = string.Format(LanguageProvider.Admin[lang, "USER_ACTIVATED_FORMAT"], user.Username);
                    isSuccess = true;
                    break;
                }

                case AdminUserActions.Deactivate:
                {
                    user.UserInactiveReason = UserInactiveReason.InactivatedByAdmin;
                    user.UserInactiveTime   = DateTime.UtcNow.ToUnixTimestamp();
                    flagUserAsChanged();
                    message   = string.Format(LanguageProvider.Admin[lang, "USER_DEACTIVATED_FORMAT"], user.Username);
                    isSuccess = true;
                    break;
                }

                case AdminUserActions.Delete_KeepMessages:
                {
                    var posts = await(
                        from p in _context.PhpbbPosts
                        where p.PosterId == userId
                        select p
                        ).ToListAsync();

                    posts.ForEach(p =>
                        {
                            p.PostUsername = user.Username;
                            p.PosterId     = Constants.ANONYMOUS_USER_ID;
                        });

                    flagUserAsChanged();
                    await deleteUser();

                    message   = string.Format(LanguageProvider.Admin[lang, "USER_DELETED_POSTS_KEPT_FORMAT"], user.Username);
                    isSuccess = true;
                    break;
                }

                case AdminUserActions.Delete_DeleteMessages:
                {
                    var toDelete = await _context.PhpbbPosts.Where(p => p.PosterId == userId).ToListAsync();

                    _context.PhpbbPosts.RemoveRange(toDelete);
                    await _context.SaveChangesAsync();

                    toDelete.ForEach(async p => await _postService.CascadePostDelete(p, false, false));

                    flagUserAsChanged();
                    await deleteUser();

                    message   = string.Format(LanguageProvider.Admin[lang, "USER_DELETED_POSTS_DELETED_FORMAT"], user.Username);
                    isSuccess = true;
                    break;
                }

                case AdminUserActions.Remind:
                {
                    string          subject;
                    WelcomeEmailDto model;
                    if (user.UserInactiveReason == UserInactiveReason.NewlyRegisteredNotConfirmed)
                    {
                        subject = string.Format(LanguageProvider.Email[user.UserLang, "WELCOME_REMINDER_SUBJECT_FORMAT"], forumName);
                        model   = new WelcomeEmailDto
                        {
                            RegistrationCode       = user.UserActkey,
                            Subject                = subject,
                            UserName               = user.Username,
                            IsRegistrationReminder = true,
                            RegistrationDate       = user.UserRegdate.ToUtcTime(),
                            Language               = user.UserLang
                        };
                    }
                    else if (user.UserInactiveReason == UserInactiveReason.ChangedEmailNotConfirmed)
                    {
                        subject = string.Format(LanguageProvider.Email[user.UserLang, "EMAIL_CHANGED_REMINDER_SUBJECT_FORMAT"], forumName);
                        model   = new WelcomeEmailDto
                        {
                            RegistrationCode      = user.UserActkey,
                            Subject               = subject,
                            UserName              = user.Username,
                            IsEmailChangeReminder = true,
                            EmailChangeDate       = user.UserInactiveTime.ToUtcTime(),
                            Language              = user.UserLang
                        };
                    }
                    else
                    {
                        message   = string.Format(LanguageProvider.Admin[lang, "CANT_REMIND_INVALID_USER_STATE_FORMAT"], user.Username, LanguageProvider.Enums[lang, user.UserInactiveReason]);
                        isSuccess = false;
                        break;
                    }

                    await Utils.SendEmail(
                        to : user.UserEmail,
                        subject : subject,
                        body : await Utils.RenderRazorViewToString("_WelcomeEmailPartial", model, pageContext, httpContext));

                    user.UserReminded     = 1;
                    user.UserRemindedTime = DateTime.UtcNow.ToUnixTimestamp();
                    message   = string.Format(LanguageProvider.Admin[lang, "USER_REMINDED_FORMAT"], user.Username);
                    isSuccess = true;
                    break;
                }

                default: throw new ArgumentException($"Unknown action '{action}'.", nameof(action));
                }

                await _context.SaveChangesAsync();

                if (isSuccess ?? false)
                {
                    await _operationLogService.LogAdminUserAction(action.Value, adminUserId, user);
                }

                return(message, isSuccess);
            }
            catch (Exception ex)
            {
                var id = Utils.HandleError(ex);
                return(string.Format(LanguageProvider.Errors[lang, "AN_ERROR_OCCURRED_TRY_AGAIN_ID_FORMAT"], id), false);
            }
        }