public class AksesKontrol {

    // Satu permintaan akses: pengguna, peran, dan sumber daya yang diminta.
    record Permintaan(String pengguna, String peran, String sumberDaya) { }

    public static void main(String[] args) {
        // Skenario 1: permintaan sah dari admin keuangan.
        Permintaan sah = new Permintaan("budi", "admin", "laporan-keuangan");

        // Skenario 2: permintaan melampaui kewenangan (least privilege).
        Permintaan melampaui = new Permintaan("siti", "keuangan", "rekam-medis");

        // Skenario 3: data permintaan rusak/tidak lengkap dari luar sistem.
        Permintaan rusak = null;

        System.out.println("=== Fail-closed vs fail-open pada tiga skenario ===");
        uji("Permintaan sah      ", sah);
        uji("Melampaui wewenang  ", melampaui);
        uji("Data permintaan rusak", rusak);
    }

    static void uji(String label, Permintaan p) {
        System.out.printf("%s -> fail-closed: %-9s | fail-open: %-9s%n",
                label, putuskanFailClosed(p), putuskanFailOpen(p));
    }

    // Fail-closed: akses hanya diberikan bila seluruh pemeriksaan berhasil.
    // Setiap kegagalan - data rusak, exception, kondisi tak terduga - berarti DITOLAK.
    static String putuskanFailClosed(Permintaan p) {
        try {
            if (p == null || p.peran() == null || p.sumberDaya() == null) {
                return "DITOLAK";
            }
            boolean peranCukup = p.peran().equals("admin")
                    || (p.peran().equals("keuangan") && p.sumberDaya().equals("laporan-keuangan"));
            return peranCukup ? "DIIZINKAN" : "DITOLAK";
        } catch (RuntimeException e) {
            return "DITOLAK"; // kegagalan apa pun menutup akses, bukan membukanya
        }
    }

    // Fail-open: KEPUTUSAN SALAH untuk pembelajaran.
    // Exception ditelan lalu akses justru diberikan.
    static String putuskanFailOpen(Permintaan p) {
        try {
            if (p == null || p.peran() == null || p.sumberDaya() == null) {
                return "DIIZINKAN"; // data rusak dianggap boleh lewat
            }
            boolean peranCukup = p.peran().equals("admin")
                    || (p.peran().equals("keuangan") && p.sumberDaya().equals("laporan-keuangan"));
            return peranCukup ? "DIIZINKAN" : "DITOLAK";
        } catch (RuntimeException e) {
            return "DIIZINKAN"; // kegagalan dibuka lebar
        }
    }
}
