using SameBeat.Domain.Enumy; using SameBeat.Domain.Wyjatki; namespace SameBeat.Domain.Encje; /// /// Konto uzytkownika systemu -- widz, uczestnik na scenie, host imprezy albo moderator. /// public class Uzytkownik { public Guid Id { get; private set; } public string Nick { get; private set; } = string.Empty; /// /// Hash hasla (Microsoft.AspNetCore.Identity.PasswordHasher), nigdy jawne haslo. /// Puste tylko dla kont, ktore jeszcze nie maja ustawionego hasla logowania. /// public string? HasloHash { get; private set; } public RolaUzytkownika Rola { get; private set; } public DateTime UtworzonyUtc { get; private set; } public bool Zbanowany { get; private set; } /// /// Liczba kolejnych nieudanych prob logowania NA TO KONTO (nie na IP -- to osobna warstwa, /// patrz rate limiting /api/auth/login w Program.cs). Zerowana po kazdym udanym logowaniu. /// public int NieudaneProbyLogowania { get; private set; } /// Konto zablokowane do tego momentu (ETAP 7, ochrona przed brute force per-konto). Null = brak blokady. public DateTime? ZablokowanyDoUtc { get; private set; } // Prywatny konstruktor bez parametrow -- wymagany przez EF Core do materializacji encji z bazy. private Uzytkownik() { } private Uzytkownik(Guid id, string nick, RolaUzytkownika rola, DateTime utworzonyUtc) { Id = id; Nick = nick; Rola = rola; UtworzonyUtc = utworzonyUtc; Zbanowany = false; } /// /// Tworzy nowego uzytkownika. Walidacja nicku tutaj chroni przed powstaniem encji w niepoprawnym /// stanie niezaleznie od tego, ktory serwis aplikacji jej uzyje. /// public static Uzytkownik Utworz(string nick, RolaUzytkownika rola) { if (string.IsNullOrWhiteSpace(nick)) { throw new NieprawidlowyStanEncjiWyjatek("Nick uzytkownika nie moze byc pusty."); } return new Uzytkownik(Guid.NewGuid(), nick.Trim(), rola, DateTime.UtcNow); } public void UstawHasloHash(string hasloHash) { if (string.IsNullOrWhiteSpace(hasloHash)) { throw new NieprawidlowyStanEncjiWyjatek("Hash hasla nie moze byc pusty."); } HasloHash = hasloHash; } public void Zbanuj() => Zbanowany = true; public void OdbanujOsobe() => Zbanowany = false; /// Czy konto jest w tej chwili zablokowane po zbyt wielu nieudanych probach logowania. public bool CzyZablokowany(DateTime terazUtc) => ZablokowanyDoUtc is DateTime blokadaDoUtc && blokadaDoUtc > terazUtc; /// /// Odnotowuje nieudana probe logowania. Po osiagnieciu /// konto zostaje zablokowane na -- niezaleznie od tego, z jakiego /// adresu IP przychodza kolejne proby (to chroni PRZED atakiem rozproszonym na wiele IP, w /// odroznieniu od rate limitingu per-IP na endpoincie /api/auth/login). /// public void ZanotujNieudaneLogowanie(int maksymalnaLiczbaProb, TimeSpan czasBlokady) { NieudaneProbyLogowania++; if (NieudaneProbyLogowania >= maksymalnaLiczbaProb) { ZablokowanyDoUtc = DateTime.UtcNow.Add(czasBlokady); } } /// Udane logowanie zeruje licznik i zdejmuje ewentualna blokade. public void ZanotujUdaneLogowanie() { NieudaneProbyLogowania = 0; ZablokowanyDoUtc = null; } }