Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file added .github/BD-1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added .github/BD-2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
273 changes: 273 additions & 0 deletions DDD.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,273 @@
# TBO - lab 1

## Wstęp (Opis Zadania)
Celem zadania jest zamodelowanie wybranego fragmentu bezpiecznej aplikacji bankowej zgodnie z zasadami
Domain-Driven Design (DDD). Projekt skupia się na podstawowej, ale krytycznej operacji, jaką jest realizacja
przelewu krajowego. Poniższy model przedstawia kluczowe byty, agregaty oraz obiekty wartości biorące udział
w tym procesie, wraz z definicją ich granic (Bounded Context) i fundamentalnych założeń bezpieczeństwa.


## Model Domeny

### Zidentyfikowane Konteksty Ograniczone (Bounded Contexts)

Dla wybranego fragmentu systemu bankowego zidentyfikowano dwa główne konteksty:

* Transakcje
* Proces inicjowania, walidacji i wykonywania przelewów pieniężnych.
* Zarządzanie operacją przelewu, który komunikuje się z kontem w celu obciążenia rachunku nadawcy.


## Model agregatów i danych

### Transakcje

![BD-1.png](.github/BD-1.png)


#### Klient
- **ID**
- typ: `UUID`
- wymagane: tak
- **imię**
- typ: `str`
- wymagane: tak
- długość: 2–50 znaków
- format: tylko litery (A–Z, a–z, łącznie z polskimi znakami)
- **nazwisko**
- typ: `str`
- wymagane: tak
- długość: 2–50 znaków
- format: tylko litery (A–Z, a–z, łącznie z polskimi znakami)
- **adres**
- typ: `Adres`
- wymagane: tak
- **nr_telefonu**
- typ: `NrTelefonu`
- wymagane: tak

---

#### Konto
- **ID**
- typ: `UUID`
- wymagane: tak
- **klient**
- typ: `Klient`
- wymagane: tak
- **saldo**
- typ: `numeric`
- wymagane: tak
- minimalna wartość: 0.00
- precyzja: 2 miejsca po przecinku
- **numer**
- typ: `NumerKonta`
- wymagane: tak

---

#### Przelew
- **ID**
- typ: `UUID`
- wymagane: tak
- **tytuł**
- typ: `str`
- wymagane: tak
- długość: 3–140 znaków
- **konto_źródłowe**
- typ: `Konto`
- wymagane: tak
- **kwota**
- typ: `KwotaPrzelewu`
- wymagane: tak
- **nazwa_odbiorcy**
- typ: `str`
- wymagane: tak
- długość: 2–100 znaków
- **numer_konta_odbiorcy**
- typ: `NumerKonta`
- wymagane: tak
- **adres_odbiorcy**
- typ: `str` (intencjonalnie `str`, a nie `Adres` - w typowych systemach bankowych jest to mało istotne i wpisywane ręcznie)
- wymagane: nie
- maks. długość: 200 znaków
- **data_zlecenia**
- typ: `datetime`
- wymagane: tak
- **data_realizacji**
- typ: `datetime`
- wymagane: nie
- warunek: >= `data_zlecenia`

---

#### Adres
- **ulica**
- typ: `str`
- wymagane: tak
- maks. długość: 100 znaków
- **numer_budynku**
- typ: `str`
- wymagane: tak
- maks. długość: 10 znaków
- **numer_mieszkania**
- typ: `str`
- wymagane: nie
- maks. długość: 10 znaków
- **miasto**
- typ: `str`
- wymagane: tak
- maks. długość: 50 znaków
- **kod_pocztowy**
- typ: `str`
- wymagane: tak
- format: np. `NN-NNN` - zależny od kraju
- **kraj**
- typ: `strEnum`
- wymagane: tak
- maks. długość: 50 znaków
- wybierany z odgórnie zdefiniowanej listy krajów

---

#### NrTelefonu
- **prefiks_kraju**
- typ: `strEnum`
- wymagane: tak
- format: `+NN` lub `+NNN`
- walidowany z predefiniowaną listą prefiksów dla wszystkich krajów
- **numer**
- typ: `str[numeric]`
- wymagane: tak
- długość: 6–12 cyfr

---

#### NumerKonta
- **numer**
- typ: `str`
- wymagane: tak
- długość: 20–34 znaki
- format: IBAN (opcjonalnie z odstępami)
- **kod_kraju**
- typ: `strEnum`
- wymagane: tak
- wybierany z odgórnie zdefiniowanej listy prefiksów krajów

---

#### KwotaPrzelewu
- **kwota**
- typ: `numeric`
- wymagane: tak
- minimalna wartość: 0.01
- precyzja: 2 miejsca po przecinku
- **waluta**
- typ: `strEnum`
- wymagane: tak
- wybierana z odgórnie zdefiniowanej listy walut


### Uwierzytelnianie do konta

![BD-2.png](.github/BD-2.png)


#### Użytkownik
- **ID**
- typ: `UUID`
- wymagane: tak
- **nazwa_użytkownika**
- typ: `str`
- wymagane: tak
- długość: 3–40 znaków
- format: litery, cyfry, podkreślenia; bez spacji
- **email**
- typ: `Email`
- wymagane: tak
- **hash_hasła**
- typ: `HashHasła`
- wymagane: tak
- **licznik_nieudanych_prob**
- typ: `LicznikProb`
- wymagane: tak
- **metoda_uwierzytelnienia**
- typ: `list[MetodaUwierzytelnienia]`
- wymagane: tak
- co najmniej 1 metoda przypisana do użytkownika

---

#### MetodaUwierzytelnienia
- **ID**
- typ: `UUID`
- wymagane: tak
- **typ_metody**
- typ: `strEnum`
- wymagane: tak
- dozwolone wartości: `[hasło, passkey, klucz sprzętowy, e-mail, biometria]`
- **nazwa**
- typ: `str`
- wymagane: tak
- długość: 3–50 znaków
- **data_dodania**
- typ: `datetime`
- wymagane: tak

---

#### Logowanie
- **użytkownik**
- typ: `Użytkownik`
- wymagane: tak
- **data_logowania**
- typ: `datetime`
- wymagane: tak
- **czy_udane**
- typ: `bool`
- wymagane: tak
- opis: `true` = logowanie poprawne, `false` = błędne hasło lub metoda
- **adres_IP**
- typ: `IP`
- wymagane: tak

---

#### Email
- **email**
- typ: `str`
- wymagane: tak
- maks. długość: 100 znaków
- format: poprawny adres e-mail `user@host.domain`

---

#### HashHasła
- **hash**
- typ: `str`
- wymagane: tak
- długość: 60–255 znaków

---

#### LicznikProb
- **licznik**
- typ: `int`
- wymagane: tak
- minimalna wartość: 0
- maksymalna wartość: 10
- resetowany po udanym logowaniu

---

#### IP
- **IP**
- typ: `str`
- wymagane: tak
- format: IPv4 lub IPv6
- przykłady: `192.168.0.1`, `2001:0db8:85a3::8a2e:0370:7334`



## Założenia

5 changes: 5 additions & 0 deletions Python/Flask_Book_Library/project/books/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,11 @@ class Book(db.Model):
status = db.Column(db.String(20), default='available')

def __init__(self, name, author, year_published, book_type, status='available'):
if not re.compile(r"^[A-Za-z0-9ąćęłńóśźżĄĆĘŁŃÓŚŹŻ\s\-–—:;,.!?'\"()\[\]/&„”«»]{1,255}$").match(name):
raise ValueError("Invalid book name.")
if not re.compile(r"^[A-Za-ząćęłńóśźżĄĆĘŁŃÓŚŹŻ\.\s\-']{2,100}$").match(author):
raise ValueError("Invalid author name.")

self.name = name
self.author = author
self.year_published = year_published
Expand Down
1 change: 1 addition & 0 deletions Python/Flask_Book_Library/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,3 +15,4 @@ SQLAlchemy==2.0.21
typing_extensions==4.8.0
Werkzeug==2.3.7
WTForms==3.0.1
pytest==8.4.2
Empty file.
73 changes: 73 additions & 0 deletions Python/Flask_Book_Library/tests/test_book.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import pytest
from project.books.models import Book

@pytest.mark.parametrize("valid_name", [
"Długi tytuł książki z podtytułem: Część 1",
"Rok 1984",
"Gwiezdne Wojny: Część V (Imperium kontratakuje)",
"Władca Pierścieni. Drużyna Pierścienia.",
"Odprawa posłów greckich",
"Jądro Ciemności—Historia Prawdziwa",
])
def test_book_name_valid(valid_name):
try:
Book(name=valid_name, author="W. Shakespeare", year_published=2000, book_type="Fiction")
except ValueError:
pytest.fail(f"Walidacja nie powinna odrzucić prawidłowego tytułu: {valid_name}")

@pytest.mark.parametrize("invalid_name", [
"",
"Tytuł z $ symbolem",
"Tytuł z % symbolem",
])
def test_book_name_invalid(invalid_name):
with pytest.raises(ValueError, match="Invalid book name."):
Book(name=invalid_name, author="W. Shakespeare", year_published=2000, book_type="Fiction")


@pytest.mark.parametrize("malicious_name", [
"<script>alert('XSS')</script>",
"\" onmouseover='alert(1)'",
"Book Name <img src=x onerror=alert(1)>",
"Tytuł z tagiem <h1>",
"' OR 1=1 --",
"SELECT * FROM books",
])
def test_book_name_security_rejected(malicious_name):
with pytest.raises(ValueError, match="Invalid book name."):
Book(name=malicious_name, author="Valid Author", year_published=2000, book_type="Fiction")


@pytest.mark.parametrize("valid_author", [
"Jan Kowalski",
"Maria Skłodowska-Curie",
"J. R. R. Tolkien",
"O'Malley",
"F. Scott Fitzgerald",
"Łukasz P.",
])
def test_author_name_valid(valid_author):
try:
Book(name="Valid Title", author=valid_author, year_published=2000, book_type="Fiction")
except ValueError:
pytest.fail(f"Walidacja nie powinna odrzucić prawidłowego autora: {valid_author}")

@pytest.mark.parametrize("invalid_author", [
"Autor z cyfrą 1",
"Autor z @ symbolem",
"A",
"",
])
def test_author_name_invalid(invalid_author):
with pytest.raises(ValueError, match="Invalid author name."):
Book(name="Valid Title", author=invalid_author, year_published=2000, book_type="Fiction")


@pytest.mark.parametrize("malicious_author", [
"<script>alert('XSS')</script>",
"Author Name <img src=x onerror=alert(1)>",
"' OR 1=1 --"
])
def test_author_name_security_rejected(malicious_author):
with pytest.raises(ValueError, match="Invalid author name."):
Book(name="Valid Title", author=malicious_author, year_published=2000, book_type="Fiction")