• בלוג
  • עמוד 6
  • שלושה יתרונות של ניהול הרשאות מבוסס Policy

שלושה יתרונות של ניהול הרשאות מבוסס Policy

02/06/2024

ניהול הרשאות במערכת זה המנגנון שמאפשר לנו מרגע שמשתמש התחבר להבין מה מותר לאותו משתמש לעשות. באנגלית זה נקרא Authorization ובדרך כלל אנחנו מפרידים את מנגנון ניהול ההרשאות ממנגנון ההזדהות (Authentication). ההזדהות מאפשרת לי להגיד שמי שנכנס הרגע הוא משתמש X וניהול ההרשאות זה מה שמאפשר לי להגיד שלמשתמש X מותר להיכנס לדף מסוים.

במנגנון ניהול ההרשאות אנחנו מוצאים שתי גישות מרכזיות (עם ספריות קוד תואמות). הגישה הראשונה היא ניהול הרשאות מבוסס משתמש. פה יש לנו ב Rails את cancancan וב JavaScript/Typescript את casl. בגישה זאת אנחנו מתחילים את התוכנית בלקחת אוביקט משתמש ו"להדביק" לו הרשאות:

import { defineAbility } from '@casl/ability';

export default (user) => defineAbility((can) => {
  can('read', 'Article');

  if (user.isLoggedIn) {
    can('update', 'Article', { authorId: user.id });
    can('create', 'Comment');
    can('update', 'Comment', { authorId: user.id });
  }
});

הרבה פעמים אני מחלק את קטע הקוד הזה לפונקציות ותהיה לי פונקציה אחת שמטפלת במשתמש מסוג "גולש רגיל" פונקציה אחרת ל"גולש מחובר" פונקציה שלישית ל"מנוי" ורביעית ל"מנהל האתר". בכל פונקציה אני מדביק למשתמש את ההרשאות המתאימות.

פה באתר למשל אני משתמש בספריה כזאת בקוד ריילס ויש לי הרשאות כמו:

def guest_user(user)
  can :read, BlogPost do |post|
    post.published_at <= Time.now
  end

  can :read, Lesson, free: true
end

גישה כזאת עובדת מאוד טוב בתחילת הפרויקט או בפרויקטים קטנים, כל עוד כל ההרשאות נכנסות בקובץ אחד.

גישה שנייה לניהול הרשאות היא ניהול מבוסס מדיניות. בגישה זו אני מגדיר Policy לגישה ל"דבר" מסוים ובתוך המדיניות אני מגדיר מי רשאי לעשות מה עם אותו דבר. זאת הגישה שאנחנו פוגשים בספריות ריילס כמו pundit ו action_policy, פאנדיט גם זמינה ב JavaScript. הנה קטע מתוך התיעוד של action_policy כדי שנראה את ההבדל:

class PostPolicy < ApplicationPolicy
  # everyone can see any post
  def show?
    true
  end

  def update?
    # `user` is a performing subject,
    # `record` is a target object (post we want to update)
    user.admin? || (user.id == record.user_id)
  end
end

או ב JavaScript מתוך התיעוד של pundit:

import { Policy } from 'pundit'

export default class PostPolicy extends Policy {
  constructor(user, record) {
    super(user, record)
    this.setup.apply(this)
  }

  edit() {
    return this.user.id === this.record.userId
  }

  destroy() {
    return this.user.isAdmin
  }
}

שלושה יתרונות מהירים של מדיניות הרשאות מבוססת מודלים הם:

  1. כל ההרשאות של מודל מסוים מרוכזות במקום אחד.

  2. אפשר לשתף Policy בין כמה מודלים.

  3. ניהול שינויים - אם אני רוצה לקבוע ממחר שתוכן מסוג מסוים כבר לא חופשי בגישה הראשונה אני צריך לשנות בשני מקומות (גם בפונקציה שמגדירה הרשאות לאורחים וגם בפונקציה שמגדירה הרשאות למשתמשים רשומים). בגישה השנייה מספיק לעדכן את פונקציית הצפייה ב Policy שמתאים לדבר אותו אני מזיז.