Kontrol Akses & Logika Bisnis
Peran, tingkatan, penilaian risiko, dan aturan kepatuhan.
Menelusuri Sistem Kontrol Akses
Modul ini menelusuri lapisan kontrol akses dan logika bisnis yang menentukan siapa yang dapat melihat apa dan bagaimana keputusan kepatuhan dihitung. Setiap langkah mengacu pada kode nyata di backend.
Hierarki Rantai Pasok
Rantai pasok EUDR dimodelkan sebagai hierarki yang ketat. Setiap anggota enum memiliki level numerik yang menentukan visibilitas.
class UserRole(str, Enum):
TRADER = "trader" # Level 4
REFINERY = "refinery" # Level 3
MILLS = "mills" # Level 2
ESTATE = "estate" # Level 1
ADMIN = "admin" # Level 99
can_view_role() cukup memeriksa: apakah level saya ≥ level target? Tidak perlu tabel izin per peran.Tingkatan Langganan
Akses fitur diatur oleh tingkatan langganan, bukan peran. Metode SubscriptionTier.get_features() mengembalikan dictionary kapabilitas untuk tingkatan pengguna. Ini berfungsi sebagai sistem feature flag.
class SubscriptionTier(str, Enum):
FREE = "free"
ENTERPRISE = "enterprise"
def get_features(self):
if self == SubscriptionTier.FREE:
return {
"radd_alerts": True,
"glad_alerts": False,
"max_plots": 5,
"enhanced_pdf": False,
"batch_processing": False,
"rate_limit": 60,
}
else: # ENTERPRISE
return {
"radd_alerts": True,
"glad_alerts": True,
"max_plots": 999999,
"enhanced_pdf": True,
"batch_processing": True,
"rate_limit": 300,
}
Klasifikasi Risiko Negara
Persyaratan uji tuntas berdasarkan Pasal 29 bergantung pada klasifikasi risiko negara sumber. Sistem memetakan kode negara ISO ke level risiko dengan penyesuaian skor yang sesuai.
def get_country_risk_level(country_code: str):
code = country_code.upper()
if code in high_risk: # BR, ID, CD...
return ('high', 20)
elif code in standard_risk:
return ('standard', 0)
elif code in low_risk:
return ('low', -20)
else:
return ('unknown', 10)
Formula Skor Risiko
Skor risiko adalah nilai komposit dari 0 hingga 100. Dihitung secara aditif dari faktor-faktor independen, kemudian dibatasi ke rentang yang valid. Berikut cara setiap komponen berkontribusi:
risk_score = 0
# 1. Penyesuaian risiko negara
risk_score += country_adjustment # -20 hingga +20
# 2. Faktor kehilangan hutan
if forest_loss > 5:
risk_score += 10
# 3. Penalti peringatan pasca-batas waktu
if any_alert_after_cutoff:
risk_score += 25
# 4. Faktor luas peringatan
area_ratio = alert_area / plot_area
risk_score += min(area_ratio * 5, 25)
# 5. Dasar ketidakpastian data
risk_score += 5
# Batasi ke rentang valid
risk_score = max(0, min(100, risk_score))
Keputusan Kepatuhan
Pemeriksaan ambang batas kepatuhan akhir menggunakan tiga pemicu independen. Jika salah satu di antaranya aktif, plot ditandai NON_COMPLIANT:
# Tiga pemicu independen
trigger_1 = forest_loss > 2.0 # %
trigger_2 = risk_score > 70
trigger_3 = has_post_cutoff_alert
if trigger_1 or trigger_2 or trigger_3:
status = ComplianceStatus.NON_COMPLIANT
elif borderline_conditions:
status = ComplianceStatus.NEEDS_REVIEW
else:
status = ComplianceStatus.COMPLIANT
Tugas Kontrol Akses
Cara memeriksa apakah pengguna dapat mengakses fitur
- Panggil
user.has_feature("glad_alerts")pada objek pengguna yang telah diautentikasi. Ini mendelegasikan ke tingkatan langganan pengguna. - Secara internal,
has_feature()pertama-tama memeriksa apakah peran pengguna adalahADMIN. Jika ya, selalu mengembalikanTrue— admin melewati semua gerbang fitur. - Untuk pengguna non-admin, metode ini memanggil
SubscriptionTier.get_features()dan mencari kunci fitur yang diminta dalam dictionary yang dikembalikan. - Gunakan ini di guard endpoint:
if not user.has_feature("batch_processing"): raise HTTPException(403). - Kunci fitur harus sama persis — kunci yang tersedia adalah:
radd_alerts,glad_alerts,max_plots,enhanced_pdf,batch_processing,rate_limit,alert_subscriptions.
Cara menambahkan tingkatan langganan baru
- Buka enum
SubscriptionTierdi models dan tambahkan anggota baru, misalnyaPROFESSIONAL = "professional". - Di metode
get_features(), tambahkan cabangelif self == SubscriptionTier.PROFESSIONALdengan dictionary fitur untuk tingkatan baru. - Definisikan semua kunci fitur dengan nilainya — tingkatan baru dapat menggabungkan kapabilitas antara FREE dan ENTERPRISE.
- Perbarui cabang default/fallback di
get_features()jika diperlukan untuk memastikan tingkatan yang tidak dikenal gagal dengan aman. - Tidak diperlukan migrasi database — kolom
subscription_tiermenyimpan nilai string. Cukup perbarui record pengguna ke nama tingkatan baru.
Cara mengubah bobot skor risiko
- Semua bobot skor risiko didefinisikan sebagai konstanta konfigurasi. Nilai kunci adalah:
COMPLIANCE_FOREST_LOSS_THRESHOLD(default: 2%),COMPLIANCE_RISK_SCORE_THRESHOLD(default: 70),RISK_COUNTRY_HIGH(default: +20),RISK_ALERT_POST_CUTOFF(default: +25). - Ubah konstanta yang relevan untuk menyesuaikan sensitivitas. Misalnya, menurunkan
COMPLIANCE_RISK_SCORE_THRESHOLDdari 70 ke 60 membuat sistem lebih agresif dalam menandai ketidakpatuhan. - Tidak diperlukan perubahan kode selain nilai konfigurasi — formula perhitungan risiko membaca dari konstanta ini saat runtime.
- Setelah mengubah ambang batas, jalankan ulang analisis yang perlu mencerminkan pengaturan baru. Hasil yang sudah tersimpan tidak akan diperbarui secara otomatis.
Cara menambahkan negara baru ke daftar risiko
- Buka
forest_analyzer_with_alerts.pydan temukan dictionaryEU_COUNTRY_RISK_CLASSIFICATION. - Temukan daftar level risiko yang sesuai:
high_risk,standard_risk, ataulow_risk. - Tambahkan kode ISO 3166-1 alpha-2 negara tersebut (dua huruf kapital, misalnya
"TZ"untuk Tanzania) ke daftar yang dipilih. - Perubahan langsung berlaku pada analisis berikutnya — tidak diperlukan migrasi database atau restart selain memuat ulang modul.
- Verifikasi dengan menjalankan analisis untuk plot di negara tersebut dan periksa bahwa penyesuaian risiko negara sesuai dengan nilai yang diharapkan dalam hasil.
Alasan Desain
Mengapa Ambang Batas yang Dapat Dikonfigurasi?
Pasal 29 EUDR tidak menentukan metodologi risiko yang pasti. Pasal ini menetapkan kerangka benchmarking tetapi menyerahkan implementasi spesifik kepada operator. Ini disengaja — operator yang berbeda memiliki selera risiko yang secara fundamental berbeda.
Pedagang kakao kecil yang bersumber dari tiga perkebunan di Afrika Barat memiliki kebutuhan sensitivitas yang sangat berbeda dibandingkan kilang minyak sawit multinasional yang memproses ribuan plot di Asia Tenggara. Pedagang kakao mungkin menginginkan ambang batas yang agresif (batas skor risiko lebih rendah, batas kehilangan hutan lebih ketat) karena setiap plot sangat penting bagi rantai pasok mereka. Kilang mungkin membutuhkan ambang batas standar untuk menghindari membanjiri tim review mereka dengan false positive. Dengan menyimpan semua bobot dalam konfigurasi, setiap deployment dapat disetel tanpa menyentuh satu baris pun kode analisis.
Pola Hierarki
Metode can_view_role() membandingkan level hierarki alih-alih memelihara matriks izin eksplisit. Pemeriksaannya sederhana: apakah level penampil lebih besar dari atau sama dengan level target?
Ini elegan karena menambahkan peran baru — misalnya, WAREHOUSE di level 1.5 — hanya berarti menetapkan nomor level. Anda tidak perlu memperbarui setiap pemeriksaan izin di seluruh codebase. Matematika hierarki menanganinya secara otomatis.
Bayangkan seperti pangkat militer. Seorang Kolonel secara otomatis melebihi semua Letnan, Kapten, dan Mayor tanpa memerlukan aturan khusus untuk setiap pasangan. Anda hanya perlu tahu di mana setiap pangkat berada dalam rantai komando. Prinsip yang sama berlaku di sini: Trader (level 4) secara otomatis melihat data Kilang (3), Pabrik (2), dan Perkebunan (1) tanpa aturan eksplisit per peran.
Feature Flag vs Aplikasi Terpisah
Alih-alih memelihara dua aplikasi terpisah (versi "gratis" dan versi "enterprise"), sistem menggunakan satu codebase dengan pemeriksaan feature flag di setiap batas. Setiap endpoint yang menyajikan konten terbatas tingkatan memanggil user.has_feature() sebelum menyertakan data premium dalam respons.
Ini berarti perbaikan bug diterapkan ke semua pengguna secara bersamaan. Patch keamanan tidak perlu di-backport antar codebase. Dan meningkatkan pengguna dari FREE ke ENTERPRISE hanyalah perubahan satu field di database — tanpa redeployment, tanpa migrasi, tanpa downtime. Panggilan API berikutnya dari pengguna langsung mencerminkan kapabilitas baru mereka.
Referensi Kontrol Akses
Hierarki Peran
| Peran | Level | Dapat Melihat |
|---|---|---|
ADMIN | 99 | Semua peran |
TRADER | 4 | trader + kilang + pabrik + perkebunan |
REFINERY | 3 | kilang + pabrik + perkebunan |
MILLS | 2 | pabrik + perkebunan |
ESTATE | 1 | perkebunan saja |
Fitur Langganan
| Fitur | Free | Enterprise |
|---|---|---|
| Peringatan RADD | Ya | Ya |
| Peringatan GLAD | Tidak | Ya |
| Maks plot | 5 | Tak terbatas |
| PDF yang ditingkatkan (satelit) | Tidak | Ya |
| Pemrosesan batch | Tidak | Ya |
| Batas kecepatan API | 60/jam | 300/jam |
| Langganan peringatan | Tidak | Ya |
Komponen Skor Risiko
| Faktor | Poin | Kondisi |
|---|---|---|
| Negara (tinggi) | +20 | BR, ID, CD, PE, CO, BO, VE, MY, PG, KH, LA, MM, GH, CI, NG, CM, AR, PY |
| Negara (standar) | 0 | EC, GU, HN, NI, CR, PA dan lainnya |
| Negara (rendah) | -20 | AS, CA, AU, NZ, negara UE |
| Negara (tidak diketahui) | +10 | Tidak ada dalam daftar klasifikasi mana pun |
| Peringatan pasca-batas waktu | +25 | Peringatan GLAD atau RADD setelah 31-12-2020 |
| Faktor luas peringatan | hingga +25 | (luas_peringatan / luas_plot) × 5, dibatasi pada 25 |
| Kehilangan hutan | +10 | Kehilangan > 5% kanopi plot |
| Ketidakpastian data | +5 | Selalu ditambahkan sebagai margin dasar |
Negara Berisiko Tinggi
| Kode | Negara |
|---|---|
BR | Brasil |
ID | Indonesia |
CD | DR Kongo |
PE | Peru |
CO | Kolombia |
BO | Bolivia |
VE | Venezuela |
MY | Malaysia |
PG | Papua Nugini |
KH | Kamboja |
LA | Laos |
MM | Myanmar |
GH | Ghana |
CI | Pantai Gading |
NG | Nigeria |
CM | Kamerun |
AR | Argentina |
PY | Paraguay |