IlmHamroh
Python kursi/Web backend FastAPI11/16-dars17 daqiqa
Mundarija (22)

20.11-dars: Ruxsatlar

20-QISM — WEB-BACKEND (FastAPI) · 11-dars


1. Kirish va motivatsiya

O'tgan darsda autentifikatsiyani o'rgandik — server foydalanuvchini tanidi (kim u?). Lekin tanish yetarli emas: tizimga kirgan har foydalanuvchi hamma narsani qila olmasligi kerak. Oddiy o'quvchi kurs o'chira olmasligi, boshqa odamning izohini tahrirlay olmasligi kerak. Bu — ruxsat (avtorizatsiya): foydalanuvchi nima qila oladi?

Autentifikatsiya va ruxsat — ikki alohida savol:

  • Autentifikatsiya 20.10-bob: "Kim sen?" — token bilan tanish.
  • Ruxsat (bu dars): "Sen buni qila olasanmi?" — rol va egalik bilan tekshirish.

Real vaziyat. Bir API autentifikatsiyani to'g'ri qildi (JWT bilan), lekin ruxsatni unutdi: kirgan har kim istalgan kursni o'chira olardi, boshqaning profilini tahrirlay olardi. Bir o'quvchi (yomon niyatli) barcha kurslarni o'chirdi. Jamoa ruxsat qatlamini qo'shdi: kurs o'chirish — faqat admin; profil tahrir — faqat egasi yoki admin. Depends bilan ruxsat tekshiruvi endpointlarga qo'shildi. Endi har amal to'g'ri tekshiriladi — tanish va ruxsat ajratildi.

Bu darsda ruxsatlarni — rol, egalik va Depends bilan ruxsat qatlamlarini o'rganamiz.

Bu darsda:

  • Autentifikatsiya vs ruxsat (401 vs 403)
  • Rol asosli ruxsat (admin, o'quvchi)
  • Ruxsat bog'liqligi (rol_talab)
  • Egalik tekshiruvi (resurs egasi)
  • Bog'liqlik fabrikasi (parametrli bog'liqlik)
  • dependencies=[] bilan himoya
  • Umumiy ruxsat naqshlari
  • Amaliy: to'liq ruxsat tizimi

ℹ Misollarda FastAPI TestClient bilan sinaladi.


2. Nazariya — chuqur tushuntirish

2.1. Autentifikatsiya vs ruxsat

Ikki alohida tushuncha, ikki status kod:

Savol Nima Status
Kim sen? Autentifikatsiya 401 (kirmagan)
Buni qila olasanmi? Ruxsat 403 (taqiq)

401 Unauthorized — kirmagan (token yo'q/yaroqsiz). 403 Forbidden — kirgan, lekin ruxsat yo'q (rol yetmaydi). Bu farq muhim: 401 — "kir"; 403 — "kirgansan, lekin bu senga emas".

2.2. Rol asosli ruxsat

Foydalanuvchida rol bo'ladi (admin, o'quvchi, muallif). Amal rolga bog'liq:

python
def joriy_foydalanuvchi(...) -> dict:
    return {"ism": "ali", "rol": "oquvchi"}

@app.delete("/kurslar/{kurs_id}")
def ochir(kurs_id: int, user: Annotated[dict, Depends(joriy_foydalanuvchi)]):
    if user["rol"] != "admin":
        raise HTTPException(status_code=403, detail="admin huquqi kerak")
    ...

Rol — foydalanuvchi huquq darajasi. Amal bajarishdan oldin rol tekshiriladi; yetmasa 403. Bu — eng keng tarqalgan ruxsat naqshi (RBAC — rolga asoslangan kirish nazorati).

2.3. Ruxsat bog'liqligi

Rol tekshiruvini bog'liqlik qilib takrorni yo'qotami 20.7-bob:

python
def faqat_admin(user: Annotated[dict, Depends(joriy_foydalanuvchi)]):
    if user["rol"] != "admin":
        raise HTTPException(status_code=403, detail="admin huquqi kerak")
    return user

@app.delete("/kurslar/{kurs_id}")
def ochir(kurs_id: int, user: Annotated[dict, Depends(faqat_admin)]):
    ...    # faqat admin keladi

faqat_admin — ruxsat bog'liqligi. U joriy_foydalanuvchiga tayanadi (ichma-ich, 20.7) va rolni tekshiradi. Endpoint faqat ruxsatli foydalanuvchini oladi.

2.4. Bog'liqlik fabrikasi

Turli rollar uchun parametrli bog'liqlik — fabrika (funksiya qaytaruvchi funksiya):

python
def rol_talab(*ruxsat_rollar: str):
    def tekshir(user: Annotated[dict, Depends(joriy_foydalanuvchi)]):
        if user["rol"] not in ruxsat_rollar:
            raise HTTPException(status_code=403, detail="ruxsat yo'q")
        return user
    return tekshir

@app.delete("/kurslar/{id}", dependencies=[Depends(rol_talab("admin"))])
def ochir(id: int): ...

@app.post("/kurslar", dependencies=[Depends(rol_talab("admin", "muallif"))])
def yarat(...): ...

Bog'liqlik fabrikasi — parametr olib, bog'liqlik qaytaradi. rol_talab("admin") — bir yoki bir necha rolga ruxsat. Bir fabrika, ko'p ruxsat qoidasi.

2.5. Egalik tekshiruvi

Ba'zi amal rolga emas, egalikka bog'liq (o'z profilini tahrir):

python
@app.delete("/postlar/{post_id}")
def ochir(post_id: int, user: Annotated[dict, Depends(joriy_foydalanuvchi)]):
    post = post_top(post_id)
    if post is None:
        raise HTTPException(404, "topilmadi")
    if post["muallif"] != user["ism"] and user["rol"] != "admin":
        raise HTTPException(403, "faqat muallif yoki admin")
    ...

Egalik — resurs egasiga (yoki adminga) ruxsat. Bu roldan farq qiladi: "o'z resursingni tahrirlay olasan, boshqanikini yo'q". Ko'pincha rol + egalik birga ishlatiladi.

2.6. Topilmadi va taqiq tartibi

Xavfsizlik uchun tekshiruv tartibi muhim:

Holat Status
Kirmagan 401
Resurs yo'q 404
Ruxsat yo'q 403

Ko'pincha: avval autentifikatsiya (401), keyin resurs bor-yo'qligi (404), keyin ruxsat (403). Ba'zan xavfsizlik uchun "yo'q" va "taqiq" ni birlashtirish mumkin (resurs borligini oshkor qilmaslik uchun 403 o'rniga 404).

2.7. dependencies=[] bilan himoya

Natija kerak bo'lmasa, ruxsatni dependencies bilan 20.7-bob:

python
@app.delete("/kurslar/{id}", dependencies=[Depends(rol_talab("admin"))])
def ochir(id: int):
    ...    # user obyekti kerak emas, faqat tekshiruv

dependencies=[...] — ruxsat tekshiruvi natijasi kerak bo'lmaganda (faqat "o'tkaz yoki taqiqla"). Endpoint tozaroq — ruxsat mantigi ajratilgan.

2.8. Ruxsat qatlamlari

Murakkab tizimda ruxsat qatlam-qatlam:

joriy_foydalanuvchi (401 — kirgan?)
    → faqat_admin (403 — admin?)
        → egalik (403 — egasi?)

Ruxsat qatlamlari ichma-ich bog'liqlik bilan quriladi 20.7-bob. Har qatlam bir shartni tekshiradi. Bu toza va sinaladigan — har qatlamni alohida test qilish mumkin.


3. Tez ma'lumotnoma

python
from fastapi import Depends, HTTPException
from typing import Annotated

# rol tekshiruvi:
def faqat_admin(user: Annotated[dict, Depends(joriy_foydalanuvchi)]):
    if user["rol"] != "admin":
        raise HTTPException(403, "admin huquqi kerak")
    return user

# fabrika (parametrli):
def rol_talab(*rollar: str):
    def tekshir(user: Annotated[dict, Depends(joriy_foydalanuvchi)]):
        if user["rol"] not in rollar:
            raise HTTPException(403, "ruxsat yo'q")
        return user
    return tekshir

# egalik:
if resurs["egasi"] != user["ism"] and user["rol"] != "admin":
    raise HTTPException(403, "ruxsat yo'q")

# ishlatish:
@app.delete("/x/{id}", dependencies=[Depends(rol_talab("admin"))])
def ochir(id: int): ...

Status kodlar

401 — kirmagan (autentifikatsiya)
403 — kirgan, lekin ruxsat yo'q (avtorizatsiya)
404 — resurs topilmadi

4. Batafsil misollar

Misollarda FastAPI TestClient bilan sinaladi.

Misol 1 — Autentifikatsiya vs ruxsat (401 vs 403)

python
"""401 (kirmagan) vs 403 (ruxsat yo'q) farqi; rol tekshiruvi; ochiq/himoyalangan/admin endpointlar."""

import warnings
warnings.filterwarnings("ignore")

from typing import Annotated

from fastapi import Depends, FastAPI, Header, HTTPException
from fastapi.testclient import TestClient

app = FastAPI()

FOYDALANUVCHILAR = {
    "tok-admin": {"ism": "admin", "rol": "admin"},
    "tok-ali": {"ism": "ali", "rol": "oquvchi"},
}


def joriy_foydalanuvchi(x_token: Annotated[str | None, Header()] = None):
    if x_token not in FOYDALANUVCHILAR:
        raise HTTPException(status_code=401, detail="tizimga kiring")
    return FOYDALANUVCHILAR[x_token]


@app.get("/ochiq")
def ochiq():
    return {"kirish": "hamma uchun"}


@app.get("/profil")
def profil(user: Annotated[dict, Depends(joriy_foydalanuvchi)]):
    return {"ism": user["ism"], "rol": user["rol"]}


@app.delete("/kurslar/{kurs_id}", status_code=204)
def ochir(kurs_id: int, user: Annotated[dict, Depends(joriy_foydalanuvchi)]):
    if user["rol"] != "admin":
        raise HTTPException(status_code=403, detail="admin huquqi kerak")


def main() -> None:
    client = TestClient(app)

    print("=== 1. Ochiq endpoint ===")
    print(f"  /ochiq → status {client.get('/ochiq').status_code}")

    print("\n=== 2. ⚠️ Kirmagan (401) ===")
    print(f"  /profil (tokensiz) → status {client.get('/profil').status_code} (autentifikatsiya)")

    print("\n=== 3. Kirgan, lekin ruxsat yo'q (403) ===")
    javob = client.delete("/kurslar/1", headers={"X-Token": "tok-ali"})
    print(f"  o'quvchi o'chirmoqchi → status {javob.status_code} (avtorizatsiya)")

    print("\n=== 4. Admin — ruxsat bor ===")
    javob = client.delete("/kurslar/1", headers={"X-Token": "tok-admin"})
    print(f"  admin o'chirdi → status {javob.status_code}")
    print("  ⭐ 401 — kirmagan; 403 — kirgan, lekin ruxsat yo'q")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Ochiq endpoint ===
  /ochiq → status 200

=== 2. ⚠️ Kirmagan (401) ===
  /profil (tokensiz) → status 401 (autentifikatsiya)

=== 3. Kirgan, lekin ruxsat yo'q (403) ===
  o'quvchi o'chirmoqchi → status 403 (avtorizatsiya)

=== 4. Admin — ruxsat bor ===
  admin o'chirdi → status 204
  ⭐ 401 — kirmagan; 403 — kirgan, lekin ruxsat yo'q

Nima ko'rsatdi: 2.1, 2.2-bo'limlar.

Misol 2 — Ruxsat bog'liqligi va fabrika

python
"""faqat_admin bog'liqligi; rol_talab fabrikasi (parametrli); bir yoki bir necha rol; dependencies=[] bilan himoya."""

import warnings
warnings.filterwarnings("ignore")

from typing import Annotated

from fastapi import Depends, FastAPI, Header, HTTPException
from fastapi.testclient import TestClient

app = FastAPI()

FOYDALANUVCHILAR = {
    "tok-admin": {"ism": "admin", "rol": "admin"},
    "tok-muallif": {"ism": "vali", "rol": "muallif"},
    "tok-oquvchi": {"ism": "ali", "rol": "oquvchi"},
}


def joriy_foydalanuvchi(x_token: Annotated[str | None, Header()] = None):
    if x_token not in FOYDALANUVCHILAR:
        raise HTTPException(status_code=401, detail="kiring")
    return FOYDALANUVCHILAR[x_token]


def rol_talab(*ruxsat_rollar: str):
    def tekshir(user: Annotated[dict, Depends(joriy_foydalanuvchi)]):
        if user["rol"] not in ruxsat_rollar:
            raise HTTPException(status_code=403, detail=f"kerakli rol: {ruxsat_rollar}")
        return user
    return tekshir


@app.delete("/kurslar/{kurs_id}", status_code=204, dependencies=[Depends(rol_talab("admin"))])
def ochir(kurs_id: int):
    return None


@app.post("/kurslar", status_code=201, dependencies=[Depends(rol_talab("admin", "muallif"))])
def yarat(nom: str):
    return {"nom": nom}


def main() -> None:
    client = TestClient(app)

    print("=== 1. O'chirish — faqat admin ===")
    print(f"  admin → status {client.delete('/kurslar/1', headers={'X-Token': 'tok-admin'}).status_code}")
    print(f"  muallif → status {client.delete('/kurslar/1', headers={'X-Token': 'tok-muallif'}).status_code}")

    print("\n=== 2. Yaratish — admin yoki muallif ===")
    print(f"  admin → status {client.post('/kurslar?nom=X', headers={'X-Token': 'tok-admin'}).status_code}")
    print(f"  muallif → status {client.post('/kurslar?nom=Y', headers={'X-Token': 'tok-muallif'}).status_code}")

    print("\n=== 3. ⚠️ O'quvchi — ruxsat yo'q ===")
    print(f"  o'quvchi yaratmoqchi → status {client.post('/kurslar?nom=Z', headers={'X-Token': 'tok-oquvchi'}).status_code}")

    print("\n=== 4. Fabrika kuchi ===")
    print("  rol_talab('admin') va rol_talab('admin', 'muallif') — bir fabrika")
    print("  ⭐ bog'liqlik fabrikasi — parametrli ruxsat qoidasi")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. O'chirish — faqat admin ===
  admin → status 204
  muallif → status 403

=== 2. Yaratish — admin yoki muallif ===
  admin → status 201
  muallif → status 201

=== 3. ⚠️ O'quvchi — ruxsat yo'q ===
  o'quvchi yaratmoqchi → status 403

=== 4. Fabrika kuchi ===
  rol_talab('admin') va rol_talab('admin', 'muallif') — bir fabrika
  ⭐ bog'liqlik fabrikasi — parametrli ruxsat qoidasi

Nima ko'rsatdi: 2.3, 2.4, 2.7-bo'limlar.

Misol 3 — Egalik tekshiruvi

python
"""egalik: resurs egasi yoki admin tahrirlay oladi; boshqaning resursi 403; topilmasa 404; rol + egalik birga."""

import warnings
warnings.filterwarnings("ignore")

from typing import Annotated

from fastapi import Depends, FastAPI, Header, HTTPException, status
from fastapi.testclient import TestClient

app = FastAPI()

FOYDALANUVCHILAR = {
    "tok-admin": {"ism": "admin", "rol": "admin"},
    "tok-ali": {"ism": "ali", "rol": "oquvchi"},
    "tok-vali": {"ism": "vali", "rol": "oquvchi"},
}

IZOHLAR = {
    1: {"id": 1, "muallif": "ali", "matn": "birinchi izoh"},
    2: {"id": 2, "muallif": "vali", "matn": "ikkinchi izoh"},
}


def joriy_foydalanuvchi(x_token: Annotated[str | None, Header()] = None):
    if x_token not in FOYDALANUVCHILAR:
        raise HTTPException(status_code=401, detail="kiring")
    return FOYDALANUVCHILAR[x_token]


@app.delete("/izohlar/{izoh_id}", status_code=status.HTTP_204_NO_CONTENT)
def ochir(izoh_id: int, user: Annotated[dict, Depends(joriy_foydalanuvchi)]):
    izoh = IZOHLAR.get(izoh_id)
    if izoh is None:
        raise HTTPException(status_code=404, detail="izoh topilmadi")
    if izoh["muallif"] != user["ism"] and user["rol"] != "admin":
        raise HTTPException(status_code=403, detail="faqat muallif yoki admin")
    del IZOHLAR[izoh_id]


def main() -> None:
    client = TestClient(app)

    print("=== 1. Muallif o'z izohini o'chiradi ===")
    javob = client.delete("/izohlar/1", headers={"X-Token": "tok-ali"})
    print(f"  ali → izoh 1 (o'ziniki) → status {javob.status_code}")

    print("\n=== 2. ⚠️ Boshqaning izohini o'chirish (403) ===")
    javob = client.delete("/izohlar/2", headers={"X-Token": "tok-ali"})
    print(f"  ali → izoh 2 (vali'niki) → status {javob.status_code}")

    print("\n=== 3. Admin har qanday izohni o'chiradi ===")
    javob = client.delete("/izohlar/2", headers={"X-Token": "tok-admin"})
    print(f"  admin → izoh 2 → status {javob.status_code}")

    print("\n=== 4. ⚠️ Yo'q izoh (404) ===")
    javob = client.delete("/izohlar/999", headers={"X-Token": "tok-ali"})
    print(f"  yo'q izoh → status {javob.status_code}")
    print("  ⭐ egalik — o'z resursing yoki admin; boshqaniki 403")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Muallif o'z izohini o'chiradi ===
  ali → izoh 1 (o'ziniki) → status 204

=== 2. ⚠️ Boshqaning izohini o'chirish (403) ===
  ali → izoh 2 (vali'niki) → status 403

=== 3. Admin har qanday izohni o'chiradi ===
  admin → izoh 2 → status 204

=== 4. ⚠️ Yo'q izoh (404) ===
  yo'q izoh → status 404
  ⭐ egalik — o'z resursing yoki admin; boshqaniki 403

Nima ko'rsatdi: 2.5, 2.6-bo'limlar.

Misol 4 — Amaliy: to'liq ruxsat tizimi

Rol va egalikni birlashtirgan to'liq ruxsat tizimi: o'quvchi ko'radi, muallif yaratadi va o'z resursini boshqaradi, admin hamma narsani. Har amal to'g'ri qatlamda tekshiriladi.

python
"""to'liq ruxsat: ko'rish (hamma kirgan), yaratish (muallif+admin), tahrir/o'chirish (egasi+admin); rol + egalik qatlamlari."""

import warnings
warnings.filterwarnings("ignore")

from typing import Annotated

from fastapi import Depends, FastAPI, Header, HTTPException, status
from fastapi.testclient import TestClient
from pydantic import BaseModel

app = FastAPI(title="Wisar Ruxsat API")

FOYDALANUVCHILAR = {
    "tok-admin": {"ism": "admin", "rol": "admin"},
    "tok-vali": {"ism": "vali", "rol": "muallif"},
    "tok-ali": {"ism": "ali", "rol": "oquvchi"},
}

MAQOLALAR: dict[int, dict] = {}
KEYINGI = 1


class MaqolaKirish(BaseModel):
    sarlavha: str


def joriy_foydalanuvchi(x_token: Annotated[str | None, Header()] = None):
    if x_token not in FOYDALANUVCHILAR:
        raise HTTPException(status_code=401, detail="tizimga kiring")
    return FOYDALANUVCHILAR[x_token]


def rol_talab(*rollar: str):
    def tekshir(user: Annotated[dict, Depends(joriy_foydalanuvchi)]):
        if user["rol"] not in rollar:
            raise HTTPException(status_code=403, detail="ruxsat yo'q")
        return user
    return tekshir


def _maqola_va_ruxsat(maqola_id: int, user: dict) -> dict:
    maqola = MAQOLALAR.get(maqola_id)
    if maqola is None:
        raise HTTPException(status_code=404, detail="maqola topilmadi")
    if maqola["muallif"] != user["ism"] and user["rol"] != "admin":
        raise HTTPException(status_code=403, detail="faqat muallif yoki admin")
    return maqola


@app.get("/maqolalar")
def royxat(user: Annotated[dict, Depends(joriy_foydalanuvchi)]):
    return list(MAQOLALAR.values())


@app.post("/maqolalar", status_code=201)
def yarat(kirish: MaqolaKirish, user: Annotated[dict, Depends(rol_talab("muallif", "admin"))]):
    global KEYINGI
    maqola = {"id": KEYINGI, "sarlavha": kirish.sarlavha, "muallif": user["ism"]}
    MAQOLALAR[KEYINGI] = maqola
    KEYINGI += 1
    return maqola


@app.delete("/maqolalar/{maqola_id}", status_code=status.HTTP_204_NO_CONTENT)
def ochir(maqola_id: int, user: Annotated[dict, Depends(joriy_foydalanuvchi)]):
    _maqola_va_ruxsat(maqola_id, user)
    del MAQOLALAR[maqola_id]


def main() -> None:
    client = TestClient(app)

    print("=== 1. Muallif maqola yaratadi ===")
    javob = client.post("/maqolalar", json={"sarlavha": "Python"}, headers={"X-Token": "tok-vali"})
    print(f"  vali (muallif) → status {javob.status_code}, {javob.json()}")

    print("\n=== 2. ⚠️ O'quvchi yarata olmaydi (403) ===")
    javob = client.post("/maqolalar", json={"sarlavha": "X"}, headers={"X-Token": "tok-ali"})
    print(f"  ali (o'quvchi) → status {javob.status_code}")

    print("\n=== 3. Ko'rish — har kirgan foydalanuvchi ===")
    print(f"  o'quvchi ro'yxat → {len(client.get('/maqolalar', headers={'X-Token': 'tok-ali'}).json())} maqola")

    print("\n=== 4. O'chirish — egasi yoki admin ===")
    print(f"  begona (ali) → status {client.delete('/maqolalar/1', headers={'X-Token': 'tok-ali'}).status_code}")
    print(f"  admin → status {client.delete('/maqolalar/1', headers={'X-Token': 'tok-admin'}).status_code}")
    print("  ⭐ ko'rish (kirgan), yaratish (rol), o'chirish (egalik) — qatlamlar")


if __name__ == "__main__":
    main()

Natijaning muhim qismi:

text
=== 1. Muallif maqola yaratadi ===
  vali (muallif) → status 201, {'id': 1, 'sarlavha': 'Python', 'muallif': 'vali'}

=== 2. ⚠️ O'quvchi yarata olmaydi (403) ===
  ali (o'quvchi) → status 403

=== 3. Ko'rish — har kirgan foydalanuvchi ===
  o'quvchi ro'yxat → 1 maqola

=== 4. O'chirish — egasi yoki admin ===
  begona (ali) → status 403
  admin → status 204
  ⭐ ko'rish (kirgan), yaratish (rol), o'chirish (egalik) — qatlamlar

Nima ko'rsatdi: 2.1–2.8-bo'limlar.


5. To'g'ri va noto'g'ri tushunishlar

Noto'g'ri fikr To'g'risi
"Autentifikatsiya yetarli" Ruxsat ham kerak
"401 va 403 bir xil" 401 kirmagan, 403 taqiq
"Kirgan — hamma narsa" Rol/egalik cheklaydi
"Rol tekshiruvi endpoint ichida" Bog'liqlik bilan (takrorsiz)
"Bir rolga bir bog'liqlik" Fabrika — parametrli
"Egalik = rol" Egalik — resurs egasiga
"Admin ham egalik shart" Admin ko'pincha barchaga
"Ruxsatni mijoz tekshiradi" Server tekshiradi (majburiy)

6. Keng tarqalgan xatolar va yechimlari

1. Ruxsatni unutish

python
@app.delete("/kurslar/{id}")
def ochir(id: int): ...                         # ⚠️ hamma o'chiradi
# ✅ rol/egalik tekshiruvi

2. 401 va 403 aralashtirish

python
raise HTTPException(401, "ruxsat yo'q")          # ⚠️ kirgan-ku
raise HTTPException(403, "ruxsat yo'q")          # ✅ taqiq

3. Rol tekshiruvini takrorlash

python
if user["rol"] != "admin": raise ...             # ⚠️ har endpointda
# ✅ Depends(rol_talab("admin"))

4. Egalikni tekshirmaslik

python
def ochir(id, user): del RESURS[id]              # ⚠️ boshqaniki ham
if resurs["egasi"] != user["ism"]: raise 403     # ✅

5. Ruxsatni mijozda tekshirish

python
# frontend: if (user.rol === 'admin') showButton  # ⚠️ yetarli emas
# ✅ server ham tekshirsin (majburiy)

6. Admin istisnosini unutish

python
if resurs["egasi"] != user["ism"]: raise 403      # ⚠️ admin ham taqiq
if resurs["egasi"] != user["ism"] and user["rol"] != "admin": raise 403   # ✅

7. Tekshiruv tartibi (404 vs 403)

python
# avval resurs bor-yo'qligi (404), keyin ruxsat (403)
if resurs is None: raise 404
if not ruxsat: raise 403                          # ✅

7. Integratsiya — bu bilim qayerda kerak bo'ladi

  • 20.7-dars (o'tilgan): bog'liqlik — ruxsat bog'liqligi, fabrika
  • 20.10-dars (o'tilgan): autentifikatsiya — joriy_foydalanuvchi
  • 20.12-dars: xatolar — 401/403 boshqaruvi
  • 21-qism (Django): ruxsat tizimi (permissions)
  • 20.15-dars: testlash — ruxsat holatlarini test

8. Eng yaxshi amaliyotlar

  1. Autentifikatsiya (401) va ruxsatni (403) ajrating.

  2. Rol tekshiruvini bog'liqlik qiling (rol_talab).

  3. Turli rollar uchun bog'liqlik fabrikasi.

  4. Egalik tekshiruvida admin istisnosini qo'shing.

  5. Ruxsatni har doim serverda tekshiring (mijoz — faqat UI).

  6. dependencies=[] — natija kerak bo'lmaganda.

  7. Tekshiruv tartibi: 401 → 404 → 403.

  8. Ruxsat qatlamlarini alohida test qiling.


9. Amaliy topshiriq

Vazifa 1: Bashorat qiling

python
1.  # autentifikatsiya vs ruxsat farqi?
2.  # 401 nima?
3.  # 403 nima?
4.  # rol nima?
5.  # rol_talab fabrikasi nima qaytaradi?
6.  # egalik tekshiruvi nima?
7.  # admin istisnosi nima?
8.  # ruxsatni kim tekshiradi?
9.  # dependencies=[] qachon?
10. # tekshiruv tartibi (401/404/403)?
11. # kirgan — hamma narsani qiladimi?
12. # rol tekshiruvini qayerda?
Javoblar
  1. Autentifikatsiya — kim; ruxsat — nima qila oladi
  2. Kirmagan (token yo'q/yaroqsiz)
  3. Kirgan, lekin ruxsat yo'q (taqiq)
  4. Foydalanuvchi huquq darajasi (admin, o'quvchi)
  5. Bog'liqlik (funksiya) qaytaradi
  6. Resurs egasiga (yoki admin) ruxsat
  7. Admin egalikdan qat'i nazar ruxsat
  8. Server (majburiy) — mijoz faqat UI
  9. Natija kerak bo'lmaganda (faqat tekshiruv)
  10. 401 (kirgan?) → 404 (bor?) → 403 (ruxsat?)
  11. Yo'q (rol/egalik cheklaydi)
  12. Bog'liqlikda (takrorsiz)

Vazifa 2: Xatolarni tuzating

python
1.  @app.delete("/kurslar/{id}")
    def ochir(id): ...              # ruxsat yo'q

2.  raise HTTPException(401, "taqiq")   # kirgan-ku

3.  if user["rol"] != "admin": raise ...  # har endpointda

4.  def ochir(id, user):
        del RESURS[id]              # egalik?

5.  if resurs["egasi"] != user["ism"]: raise 403   # admin?
Javoblar
python
1.  @app.delete("/kurslar/{id}", dependencies=[Depends(rol_talab("admin"))])

2.  raise HTTPException(403, "ruxsat yo'q")

3.  # Depends(rol_talab("admin")) — bog'liqlik

4.  if resurs["egasi"] != user["ism"] and user["rol"] != "admin":
        raise HTTPException(403, ...)

5.  if resurs["egasi"] != user["ism"] and user["rol"] != "admin":
        raise HTTPException(403, ...)

Vazifa 3: Rol tizimi

Uch rolli API:

  1. Rollar: admin, muharrir, o'quvchi
  2. rol_talab fabrikasi
  3. Har rol o'z amallariga
  4. TestClient bilan har rol test

Vazifa 4: Egalik va rol birga

Blog API:

  1. Maqola yaratish (muallif+)
  2. Tahrir/o'chirish (egasi yoki admin)
  3. Ko'rish (hamma kirgan)
  4. Har holatni test

Vazifa 5: Ruxsat matritsasi

Ruxsat jadvalini amalga oshiring:

  1. Amallar × rollar matritsasi
  2. Har katak — ruxsat bor/yo'q
  3. Markazlashgan ruxsat funksiyasi
  4. To'liq test

Vazifa 6: Xavfsizlik tartibi

Tekshiruv tartibini sinang:

  1. 401 → 404 → 403 tartibi
  2. Resurs borligini yashirishni (403 o'rniga 404)
  3. Har tartib holati
  4. Qaysi tartib qachon to'g'ri — hujjatlang

Vazifa 7: O'ylash

Ruxsat tekshiruvi har doim serverda bo'lishi kerak — mijozda (frontend) tekshiruv faqat qulaylik (tugmani yashirishi), xavfsizlik emas. Foydalanuvchi brauzer kodini chetlab o'tib, to'g'ridan API'ga so'rov yubora oladi. "Mijozga ishonma" (never trust the client) tamoyili nima uchun xavfsizlikning asosi, va bu tamoyilni buzgan tizim (faqat frontend'da tekshirgan) qanday hujumga ochiq bo'ladi?

Javob

Qisqa javob: Mijoz (brauzer, mobil ilova) — foydalanuvchi nazoratida. U kodni o'zgartira, so'rovni qo'lda yubora, tekshiruvni chetlab o'ta oladi. Shuning uchun frontend'dagi ruxsat tekshiruvi — faqat qulaylik (foydalanuvchiga keraksiz tugmani ko'rsatmaslik), xavfsizlik emas. Xavfsizlik serverda bo'lishi shart, chunki server foydalanuvchi nazoratida emas. "Mijozga ishonma" — har kirgan ma'lumot (so'rov, parametr, token) tekshirilishi kerak. Faqat frontend'da tekshirgan tizim — hujumchi API'ga to'g'ridan so'rov yuborib, barcha himoyani chetlab o'tadi.

1. Mijoz nazoratda emas

Mijoz (ishonsiz) Server (ishonchli)
Foydalanuvchi nazoratida Sizning nazoratingizda
Kod o'zgartiriladi O'zgartirib bo'lmaydi
So'rov soxtalashtiriladi So'rovni tekshiradi

2. Frontend tekshiruvi — qulaylik

Frontend'da if (rol === 'admin') showButton — foydalanuvchi uchun qulay (keraksiz tugma yashirinadi), lekin himoya emas: hujumchi tugmasiz ham API'ga so'rov yubora oladi.

3. Faqat frontend tekshirgan tizim — hujum

1. Frontend: admin tugmasini yashiradi (o'quvchiga)
2. Hujumchi: brauzer konsolidan to'g'ridan DELETE /kurslar/1 yuboradi
3. Server tekshirmaydi → o'chiriladi (himoya chetlab o'tildi)

4. "Mijozga ishonma" tamoyili

Ishonsiz manba Server tekshiruvi
So'rov tanasi Validatsiya (Pydantic)
Parametrlar Tekshiruv
Token Imzo/muddat
Ruxsat Rol/egalik

5. Muhandislik saboqlari

  1. Mijoz — foydalanuvchi nazoratida, ishonsiz
  2. Frontend tekshiruvi — qulaylik, xavfsizlik emas
  3. Xavfsizlik serverda (majburiy)
  4. Har kirgan ma'lumotni tekshir

6. Xulosa

  1. "Mijozga ishonma" — xavfsizlik asosi
  2. Frontend — UI qulayligi; server — himoya
  3. Faqat frontend tekshirgan — API to'g'ridan ochiq
  4. Ruxsat har doim serverda tekshirilsin

Nimani mustahkamlaydi: 2.1–2.8-bo'limlar.


Xulosa

Bu darsda ruxsatlarni — rol, egalik va ruxsat qatlamlarini o'rgandik.

Eng muhim uch fikr:

  1. Autentifikatsiya va ruxsat — ikki savol. Autentifikatsiya 20.10-bob "kim sen?" ni aniqlaydi (token bilan), ruxsat esa "sen buni qila olasanmi?" ni tekshiradi (rol/egalik bilan). Status kodlar farqli: 401 Unauthorized — kirmagan (token yo'q/yaroqsiz), 403 Forbidden — kirgan, lekin ruxsat yo'q (rol yetmaydi). Kirgan har foydalanuvchi hamma narsani qila olmasligi kerak — bu ruxsatning mohiyati.

  2. Rol va bog'liqlik fabrikasi. Rol — foydalanuvchi huquq darajasi (admin, muallif, o'quvchi). Rol tekshiruvi bog'liqlik qilinadi (faqat_admin) — takror yo'qoladi. Turli rollar uchun bog'liqlik fabrikasi (rol_talab("admin", "muallif")) — parametr olib bog'liqlik qaytaruvchi funksiya — bir fabrika, ko'p ruxsat qoidasi. dependencies=[...] natija kerak bo'lmaganda ishlatiladi.

  3. Egalik va "mijozga ishonma". Ba'zi amal rolga emas, egalikka bog'liq: o'z resursingni tahrirlay olasan, boshqanikini yo'q (resurs["egasi"] != user["ism"] and user["rol"] != "admin" — admin istisnosi bilan). Tekshiruv tartibi: 401 (kirgan?) → 404 (bor?) → 403 (ruxsat?). Eng muhimi — ruxsat har doim serverda tekshiriladi: mijoz (frontend) foydalanuvchi nazoratida, uni chetlab o'tish mumkin, shuning uchun frontend tekshiruvi faqat qulaylik, xavfsizlik serverda.

Keyingi darsda xatolarni boshqarish ni o'rganamiz: HTTPExceptionni chuqurroq, maxsus xato klasslari, global xato ishlovchilari (exception handlers) va foydalanuvchiga tushunarli, izchil xato javoblari.

Ulashish:Telegram'da

Izohlar (0)

Izoh yozish uchun kiring.

  • Hozircha izoh yo'q. Birinchi bo'ling!
20.11-dars: Ruxsatlar — IlmHamroh