Ada beberapa pendekatan yang berbeda untuk mengelola hak akses pengguna, dan masing-masing memiliki sisi positif dan negatif sendiri. Misalnya, menggunakan bit masking sangat efisien tetapi juga membatasi Anda dengan izin  32 atau 64 (jumlah bit dalam 32 atau 64 bit integer). Pendekatan lain adalah dengan menggunakan Access Control List (ACL), namun Anda hanya bisa memberikan izin tersebut pada objek bukan pada operasi tertentu.

Pada artikel ini saya akan membahas pendekatan favorit pribadi saya: akses kontrol berbasis peran (RBAC). RBAC adalah model di mana peran diciptakan untuk berbagai fungsi pekerjaan, dan izin untuk melakukan operasi tertentu kemudian diikat dengan peran. Seorang pengguna dapat diberikan satu atau beberapa peran yang membatasi akses mereka ke sistem hak akses dimana mereka telah diizinkan.

Kelemahan menggunakan RBAC adalah bahwa jika tidak dikelola dengan baik, peran dan hak akses dapat dengan mudah menjadi kekacauan. Dalam lingkungan bisnis yang berubah dengan cepat, itu bisa menjadi pekerjaan sendiri untuk melacak menempatkan peran yang tepat untuk karyawan baru dan menghapus mereka pada waktu yang tepat dari mantan karyawan atau mereka yang berganti posisi. Selain itu, mengidentifikasi peran baru untuk tugas pekerjaan yang unik dan merevisi atau menghapus peran tertentu memerlukan tinjauan rutin. Kegagalan  mengelola peran dapat membuka pintu ke banyak risiko keamanan.

Saya akan mulai dengan membahas tabel database yang diperlukan, maka saya akan membuat dua file kelas: (Role.php) yang akan melakukan beberapa tugas khusus untuk peran, dan (PrivilegedUser.php) yang akan memperluas kelas pengguna yang ada. Akhirnya saya akan berjalan melalui beberapa contoh bagaimana Anda dapat mengintegrasikan kodenya ke dalam aplikasi Anda. manajemen peran  dan manajemen pengguna berjalan beriringan, sehingga dalam artikel ini saya akan berasumsi bahwa Anda sudah memiliki beberapa jenis sistem otentikasi pengguna.

Database

Anda perlu empat tabel untuk menyimpan informasi peran dan izin : tabel roles menyimpan ID peran dan nama peran, tabel permissions menyimpan ID izin dan deskripsi, tabel role_perm menghubungkan izin mana saja yang dimasukkan ke peran tertentu, dan tabel  user_role menghubungkan peran mana saja yang ditugaskan ke seorang pengguna.

Menggunakan skema ini, Anda dapat memiliki jumlah yang tidak terbatas peran dan hak akses dan setiap pengguna dapat diberikan peran ganda.

Ini adalah pernyataan CREATE TABLE untuk databasenya:

CREATE TABLE roles (
  role_id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
  role_name VARCHAR(50) NOT NULL,

  PRIMARY KEY (role_id)
);

CREATE TABLE permissions (
  perm_id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
  perm_desc VARCHAR(50) NOT NULL,

  PRIMARY KEY (perm_id)
);

CREATE TABLE role_perm (
  role_id INTEGER UNSIGNED NOT NULL,
  perm_id INTEGER UNSIGNED NOT NULL,

  FOREIGN KEY (role_id) REFERENCES roles(role_id),
  FOREIGN KEY (perm_id) REFERENCES permissions(perm_id)
);

CREATE TABLE user_role (
  user_id INTEGER UNSIGNED NOT NULL,
  role_id INTEGER UNSIGNED NOT NULL,

  FOREIGN KEY (user_id) REFERENCES users(user_id),
  FOREIGN KEY (role_id) REFERENCES roles(role_id)
);

Catatan akhir tabel, user_role, referensi tabel pengguna yang belum saya definisikan di sini. Ini mengasumsikan bahwa user_id adalah kunci utama dari tabel pengguna Anda.

Anda tidak perlu membuat modifikasi pada tabel pengguna Anda untuk menyimpan informasi peran sebagai informasi yang disimpan secara terpisah dalam tabel baru ini. Bertentangan dengan beberapa sistem RBAC lain, pengguna di sini tidak diperlukan untuk memiliki peran secara default, melainkan, pengguna hanya tidak akan memiliki hak istimewa sampai peran telah secara khusus ditugaskan. Atau, akan ada kemungkinan di kelas PrivilegedUser untuk mendeteksi peran kosong dan merespon dengan peran unprivileged default ketika diperlukan, atau Anda bisa memilih untuk menulis naskah SQL singkat untuk menyalin ID pengguna dan menginisialisasi mereka dengan menetapkan peran unprivileged default.

Kelas Role

Fokus utama dari kelas Role adalah untuk mengembalikan objek peran yang diisi dengan masing-masing peran hak akses yang sesuai. Ini akan memungkinkan Anda untuk dengan mudah memeriksa apakah izin tersebut tersedia tanpa harus melakukan query SQL berlebihan dengan setiap permintaan.

Gunakan kode berikut untuk membuat Role.php:

<?php
class Role
{
    protected $permissions;

    protected function __construct() {
        $this->permissions = array();
    }

    // return a role object with associated permissions
    public static function getRolePerms($role_id) {
        $role = new Role();
        $sql = "SELECT t2.perm_desc FROM role_perm as t1
                JOIN permissions as t2 ON t1.perm_id = t2.perm_id
                WHERE t1.role_id = :role_id";
        $sth = $GLOBALS["DB"]->prepare($sql);
        $sth->execute(array(":role_id" => $role_id));

        while($row = $sth->fetch(PDO::FETCH_ASSOC)) {
            $role->permissions[$row["perm_desc"]] = true;
        }
        return $role;
    }

    // check if a permission is set
    public function hasPerm($permission) {
        return isset($this->permissions[$permission]);
    }
}

Metode getRolePerms() membuat objek Peran baru berdasarkan ID peran tertentu, dan kemudian menggunakan klausa JOIN untuk menggabungkan role_perm dan perm_desc tabel. Untuk setiap izin yang terkait dengan peran yang diberikan, deskripsi disimpan sebagai kunci dan nilainya diatur ke true. metode hasPerm ()  menerima keterangan izin dan mengembalikan nilai berdasarkan objek saat ini.

Kelas Privileged User

Dengan menciptakan kelas baru yang pemperluas/extend kelas pengguna yang ada, Anda dapat menggunakan kembali kode logika yang ada untuk mengelola pengguna dan kemudian menambahkan beberapa metode tambahan di atas yang diarahkan secara khusus supaya bekerja dengan hak istimewa.

Gunakan kode berikut untuk membuat file PrivilegedUser.php:

<?php
class PrivilegedUser extends User
{
    private $roles;

    public function __construct() {
        parent::__construct();
    }

    // override User method
    public static function getByUsername($username) {
        $sql = "SELECT * FROM users WHERE username = :username";
        $sth = $GLOBALS["DB"]->prepare($sql);
        $sth->execute(array(":username" => $username));
        $result = $sth->fetchAll();

        if (!empty($result)) {
            $privUser = new PrivilegedUser();
            $privUser->user_id = $result[0]["user_id"];
            $privUser->username = $username;
            $privUser->password = $result[0]["password"];
            $privUser->email_addr = $result[0]["email_addr"];
            $privUser->initRoles();
            return $privUser;
        } else {
            return false;
        }
    }

    // populate roles with their associated permissions
    protected function initRoles() {
        $this->roles = array();
        $sql = "SELECT t1.role_id, t2.role_name FROM user_role as t1
                JOIN roles as t2 ON t1.role_id = t2.role_id
                WHERE t1.user_id = :user_id";
        $sth = $GLOBALS["DB"]->prepare($sql);
        $sth->execute(array(":user_id" => $this->user_id));

        while($row = $sth->fetch(PDO::FETCH_ASSOC)) {
            $this->roles[$row["role_name"]] = Role::getRolePerms($row["role_id"]);
        }
    }

    // check if user has a specific privilege
    public function hasPrivilege($perm) {
        foreach ($this->roles as $role) {
            if ($role->hasPerm($perm)) {
                return true;
            }
        }
        return false;
    }
}

Metode pertama, getByUsername (), mengembalikan sebuah objek yang terisi dengan informasi tentang pengguna tertentu. Sebuah metode yang hampir sama dengan ini kemungkinan sudah ada di kelas pengguna Anda, tetapi Anda perlu menimpanya di sini sehingga metode-metode yang ada pada PrivilegedUser bisa dipanggil dengan objek yang sesuai. Jika Anda mencoba untuk memanggil metode dari PrivilegedUser pada objek pengguna, Anda akan mendapatkan kesalahan yang menyatakan bahwa metode ini tidak ada.

Metode kedua, initRoles (), menggunakan JOIN untuk menggabungkan user_role dan role  untuk mengumpulkan peran yang terkait dengan ID pengguna saat ini. Setiap peran kemudian diisi dengan izin yang sesuai dengan panggilan ke metode pada kelas Role yang dibuat sebelumnya, Role :: getRolePerms ().

Metode terakhir, hasPrivilege (), menerima penjelasan izin dan mengembalikan nilai true dari pengguna yang memiliki izin atau false jika sebaliknya.

Dengan dua kelas sebelumnya, memeriksa apakah pengguna memiliki hak istimewa tertentu menjadi sederhana seperti berikut:

<?php
require_once "Role.php";
require_once "PrivilegedUser.php";

// connect to database...
// ...

session_start();

if (isset($_SESSION["loggedin"])) {
    $u = PrivilegedUser::getByUsername($_SESSION["loggedin"]);
}

if ($u->hasPrivilege("thisPermission")) {
    // do something
}

Username berikut disimpan dalam session yang aktif dan objek PrivilegedUser baru dibuat untuk pengguna itu yang mana metode hasPrivilege() dapat dipanggil. Tergantung pada informasi dalam database Anda, keluaran objek Anda akan terlihat mirip dengan berikut ini:

object(PrivilegedUser)#3 (2) {

  ["roles":"PrivilegedUser":private]=>
  array(1) {
    ["Admin"]=>
    object(Role)#5 (1) {
      ["permissions":protected]=>
      array(4) {
        ["addUser"]=>bool(true)
        ["editUser"]=>bool(true)
        ["deleteUser"]=>bool(true)
        ["editRoles"]=>bool(true)
      }
    }
  }
  ["fields":"User":private]=>
  array(4) {
    ["user_id"]=>string(1) "2"
    ["username"]=>string(7) "mpsinas"
    ["password"]=>bool(false)
    ["email_addr"]=>string(0) ""
  }
}

Menjaga Semua Terorganisir

Salah satu dari banyak manfaat menggunakan pendekatan OOP dengan RBAC adalah bahwa hal itu memungkinkan Anda untuk memisahkan kode logika dan validasi dari objek tugas tertentu. Misalnya, Anda dapat menambahkan metode berikut pada kelas Role Anda untuk membantu mengelola peran operasi tertentu seperti memasukkan peran baru, menghapus peran dan seterusnya:

// insert a new role
public static function insertRole($role_name) {
    $sql = "INSERT INTO roles (role_name) VALUES (:role_name)";
    $sth = $GLOBALS["DB"]->prepare($sql);
    return $sth->execute(array(":role_name" => $role_name));
}

// insert array of roles for specified user id
public static function insertUserRoles($user_id, $roles) {
    $sql = "INSERT INTO user_role (user_id, role_id) VALUES (:user_id, :role_id)";
    $sth = $GLOBALS["DB"]->prepare($sql);
    $sth->bindParam(":user_id", $user_id, PDO::PARAM_STR);
    $sth->bindParam(":role_id", $role_id, PDO::PARAM_INT);
    foreach ($roles as $role_id) {
        $sth->execute();
    }
    return true;
}

// delete array of roles, and all associations
public static function deleteRoles($roles) {
    $sql = "DELETE t1, t2, t3 FROM roles as t1
            JOIN user_role as t2 on t1.role_id = t2.role_id
            JOIN role_perm as t3 on t1.role_id = t3.role_id
            WHERE t1.role_id = :role_id";
    $sth = $GLOBALS["DB"]->prepare($sql);
    $sth->bindParam(":role_id", $role_id, PDO::PARAM_INT);
    foreach ($roles as $role_id) {
        $sth->execute();
    }
    return true;
}

// delete ALL roles for specified user id
public static function deleteUserRoles($user_id) {
    $sql = "DELETE FROM user_role WHERE user_id = :user_id";
    $sth = $GLOBALS["DB"]->prepare($sql);
    return $sth->execute(array(":user_id" => $user_id));
}

Demikian juga, Anda bisa menambahkan pada kelas PrivilegedUser dengan metode yang serupa:

// check if a user has a specific role
public function hasRole($role_name) {
    return isset($this->roles[$role_name]);
}

// insert a new role permission association
public static function insertPerm($role_id, $perm_id) {
    $sql = "INSERT INTO role_perm (role_id, perm_id) VALUES (:role_id, :perm_id)";
    $sth = $GLOBALS["DB"]->prepare($sql);
    return $sth->execute(array(":role_id" => $role_id, ":perm_id" => $perm_id));
}

// delete ALL role permissions
public static function deletePerms() {
    $sql = "TRUNCATE role_perm";
    $sth = $GLOBALS["DB"]->prepare($sql);
    return $sth->execute();
}

Karena izin terikat langsung ke kode program yang mendasari logika aplikasi, izin baru harus dimasukkan secara manual atau dihapus dari database sesuai kebutuhan. Peran di sisi lain dapat dengan mudah dibuat, diubah atau dihapus melalui antarmuka administrasi.

Semakin banyak peran dan izin yang kamu miliki akan lebih sulit untuk dikelola, menjaga daftar tersebut seminimal mungkin adalah penting, tapi kadang-kadang hal yang sebaliknya tidak dapat dihindari. Saya hanya dapat menyarankan bahwa Anda menggunakan penilaian terbaik Anda dan mencoba untuk tidak terbawa arus.

Ringkasan

Anda sekarang memiliki pemahaman tentang akses kontrol  berbasis peran dan bagaimana menerapkan peran dan hak akses ke aplikasi yang sudah ada. Selain itu, Anda telah belajar beberapa tips untuk membantu mengelola peran Anda dan untuk menjaga hal-hal terorganisir dengan baik. Seperti biasa, saya mendorong Anda untuk bereksperimen dan bertanya jika Anda terjebak. Kami semua di sini untuk belajar dari satu sama lain dan saya senang untuk membantu ketika saya bisa!

translated from http://www.sitepoint.com/role-based-access-control-in-php/

Advertisements

About phpgeek programmer

pemimpi yang berharap menjadi the best programmer di zamannya

4 responses »

  1. Rufy says:

    Makasih pak, tulisan nya mantap,saya tunggu posting selanjutnya…

  2. Dedi Ananto says:

    OOP bikin saya pusing nih

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s