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