Add support page, admin features, and auth hardening.
Rename public affiliate route to /support with localized nav labels, and include affiliate/social/wallpaper admin, 2FA login, and related site updates. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
co-authored by
Cursor
parent
202dc16a52
commit
fc45c35dbe
+169
-14
@@ -1,7 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Web;
|
||||
using System.Web.Mvc;
|
||||
using System.Web.Security;
|
||||
using atakanozbancom.Models.classes;
|
||||
@@ -10,33 +8,190 @@ namespace atakanozbancom.Controllers
|
||||
{
|
||||
public class loginController : Controller
|
||||
{
|
||||
// GET: Login
|
||||
Context c = new Context();
|
||||
private readonly Context c = new Context();
|
||||
private const int MaxPasswordFailures = 8;
|
||||
private const int MaxTotpFailures = 5;
|
||||
private const int LockMinutes = 15;
|
||||
|
||||
public ActionResult Index()
|
||||
{
|
||||
return View();
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
public ActionResult Index(admin ad)
|
||||
[ValidateAntiForgeryToken]
|
||||
public ActionResult Index(admin ad)
|
||||
{
|
||||
var bilgiler = c.admins.FirstOrDefault(x => x.username == ad.username && x.password == ad.password);
|
||||
if (bilgiler != null)
|
||||
{
|
||||
FormsAuthentication.SetAuthCookie(bilgiler.username, false);
|
||||
Session["username"] = bilgiler.username.ToString();
|
||||
return RedirectToAction("Index", "admin");
|
||||
}
|
||||
else
|
||||
if (ad == null || string.IsNullOrWhiteSpace(ad.username) || string.IsNullOrWhiteSpace(ad.password))
|
||||
{
|
||||
ViewBag.Error = "Username and password are required.";
|
||||
return View();
|
||||
}
|
||||
|
||||
var user = c.admins.FirstOrDefault(x => x.username == ad.username);
|
||||
if (user == null)
|
||||
{
|
||||
ViewBag.Error = "Invalid username or password.";
|
||||
return View();
|
||||
}
|
||||
|
||||
if (user.login_lock_until.HasValue && user.login_lock_until.Value > DateTime.UtcNow)
|
||||
{
|
||||
ViewBag.Error = "Account temporarily locked. Try again later.";
|
||||
return View();
|
||||
}
|
||||
|
||||
if (!PasswordHasher.Verify(ad.password, user.password))
|
||||
{
|
||||
user.login_failed_count++;
|
||||
if (user.login_failed_count >= MaxPasswordFailures)
|
||||
{
|
||||
user.login_lock_until = DateTime.UtcNow.AddMinutes(LockMinutes);
|
||||
user.login_failed_count = 0;
|
||||
}
|
||||
c.SaveChanges();
|
||||
ViewBag.Error = "Invalid username or password.";
|
||||
return View();
|
||||
}
|
||||
|
||||
user.login_failed_count = 0;
|
||||
user.login_lock_until = null;
|
||||
|
||||
if (!PasswordHasher.IsHashed(user.password))
|
||||
{
|
||||
user.password = PasswordHasher.Hash(ad.password);
|
||||
}
|
||||
|
||||
c.SaveChanges();
|
||||
ClearPending2Fa();
|
||||
|
||||
if (user.two_factor_enabled && !string.IsNullOrWhiteSpace(user.two_factor_secret))
|
||||
{
|
||||
if (user.totp_lock_until.HasValue && user.totp_lock_until.Value > DateTime.UtcNow)
|
||||
{
|
||||
ViewBag.Error = "Too many failed 2FA attempts. Try again later.";
|
||||
return View();
|
||||
}
|
||||
|
||||
Session["Pending2FaUserId"] = user.id;
|
||||
Session["Pending2FaUsername"] = user.username;
|
||||
Session["Pending2FaExpiresUtc"] = DateTime.UtcNow.AddMinutes(10);
|
||||
return RedirectToAction("Verify");
|
||||
}
|
||||
|
||||
CompleteLogin(user.username);
|
||||
return RedirectToAction("Index", "admin");
|
||||
}
|
||||
|
||||
public ActionResult Verify()
|
||||
{
|
||||
if (!HasValidPending2Fa())
|
||||
{
|
||||
ClearPending2Fa();
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
return View();
|
||||
}
|
||||
|
||||
[HttpPost]
|
||||
[ValidateAntiForgeryToken]
|
||||
public ActionResult Verify(string code)
|
||||
{
|
||||
if (!HasValidPending2Fa())
|
||||
{
|
||||
ClearPending2Fa();
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
var pendingId = Convert.ToInt32(Session["Pending2FaUserId"]);
|
||||
var pendingUsername = Session["Pending2FaUsername"].ToString();
|
||||
|
||||
var user = c.admins.FirstOrDefault(x => x.id == pendingId && x.username == pendingUsername);
|
||||
if (user == null || !user.two_factor_enabled || string.IsNullOrWhiteSpace(user.two_factor_secret))
|
||||
{
|
||||
ClearPending2Fa();
|
||||
return RedirectToAction("Index");
|
||||
}
|
||||
|
||||
if (user.totp_lock_until.HasValue && user.totp_lock_until.Value > DateTime.UtcNow)
|
||||
{
|
||||
ViewBag.Error = "Too many failed attempts. Try again later.";
|
||||
return View();
|
||||
}
|
||||
|
||||
var secret = ResolveTotpSecret(user);
|
||||
if (string.IsNullOrWhiteSpace(secret) || !TotpHelper.VerifyCode(secret, code))
|
||||
{
|
||||
user.totp_failed_count++;
|
||||
if (user.totp_failed_count >= MaxTotpFailures)
|
||||
{
|
||||
user.totp_lock_until = DateTime.UtcNow.AddMinutes(LockMinutes);
|
||||
user.totp_failed_count = 0;
|
||||
ViewBag.Error = "Too many failed attempts. Try again later.";
|
||||
}
|
||||
else
|
||||
{
|
||||
ViewBag.Error = "Invalid authentication code.";
|
||||
}
|
||||
c.SaveChanges();
|
||||
return View();
|
||||
}
|
||||
|
||||
user.totp_failed_count = 0;
|
||||
user.totp_lock_until = null;
|
||||
c.SaveChanges();
|
||||
|
||||
ClearPending2Fa();
|
||||
CompleteLogin(user.username);
|
||||
return RedirectToAction("Index", "admin");
|
||||
}
|
||||
|
||||
public ActionResult logout()
|
||||
{
|
||||
ClearPending2Fa();
|
||||
Session.Remove("username");
|
||||
Session.Abandon();
|
||||
FormsAuthentication.SignOut();
|
||||
return RedirectToAction("index", "login");
|
||||
}
|
||||
|
||||
private void CompleteLogin(string username)
|
||||
{
|
||||
FormsAuthentication.SetAuthCookie(username, false);
|
||||
Session["username"] = username;
|
||||
}
|
||||
|
||||
private bool HasValidPending2Fa()
|
||||
{
|
||||
if (Session["Pending2FaUserId"] == null || Session["Pending2FaUsername"] == null)
|
||||
return false;
|
||||
|
||||
var expires = Session["Pending2FaExpiresUtc"] as DateTime?;
|
||||
if (expires == null || expires.Value < DateTime.UtcNow)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static string ResolveTotpSecret(admin user)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(user.two_factor_secret))
|
||||
return null;
|
||||
|
||||
var unprotected = SecretProtector.Unprotect(user.two_factor_secret);
|
||||
if (!string.IsNullOrWhiteSpace(unprotected))
|
||||
return unprotected;
|
||||
|
||||
return user.two_factor_secret;
|
||||
}
|
||||
|
||||
private void ClearPending2Fa()
|
||||
{
|
||||
Session.Remove("Pending2FaUserId");
|
||||
Session.Remove("Pending2FaUsername");
|
||||
Session.Remove("Pending2FaSetupSecret");
|
||||
Session.Remove("Pending2FaExpiresUtc");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user