• בלוג
  • עמוד 66
  • [ריילס] היום למדתי: לא מעבירים מזהים למודל ביצירה או עדכון

[ריילס] היום למדתי: לא מעבירים מזהים למודל ביצירה או עדכון

11/02/2023

את הטיפ הבא מצאתי במאמר הזה של חברת Betterment ומיד התחברתי אז אני משתף גם פה.

נתחיל עם קוד ריילס הבא עבור controller:

class Documents::AttachmentsController < ApplicationController
    def create
        AttachmentLink.new(create_params.merge(document: document)).save!
    end

    private

    def create_params
        params.permit(:attachment_id, :caption)
    end

    def document
        current_user.documents.find(params[:document_id])
    end
end

הקוד מאפשר להצמיד Attachments למסמך דרך הפונקציה create. הפונקציה מקבלת מהדפדפן מזהה של "קובץ מצורף" ומזהה של "מסמך" ויוצרת AttachmentLink שזה אוביקט חיבור בין השניים.

קחו רגע לקרוא את הקוד ונסו לחשוב מה שבור בו.

1. הבעיה בקוד: מאיפה מגיע Attachment

ראיתם את זה? הפונקציה הפרטית document טוענת את ה Document מתוך כל המסמכים של המשתמש, וכך מוודאת שאנחנו מנסים להצמיד קובץ מצורף למסמך שלנו.

לעומתה מזהה הקובץ המצורף מועבר בתור id פנימה למודל. טעינת אוביקט ה Attachment מתבצעת אוטומטית בתוך הפונקציה new ולכן תוקף יכול להעביר כל מזהה שירצה וכך לצרף כל Attachment למסמך שלו.

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

ברגע שמבינים את זה התיקון וגם המניעה פשוטים מאוד. ככה זה נראה בקוד:

class Documents::AttachmentsController < ApplicationController
    def create
        AttachmentLink.new(attach_params).save!
    end

    private

    def create_params
        params.permit(:attachment_id, :caption)
    end

    def attach_params
      {
        document: document,
        attachment: attachment,
        caption: create_params[:caption]
      }
    end

    def attachment
        current_user.attachments.find(create_params[:attachment_id])
    end

    def document
        current_user.documents.find(params[:document_id])
    end
end

החברים ב Betterment גם יצרו כללי Rubocop שיעזרו לכם לוודא שאתם לא מעבירים מזהים של מודלים לתוך פונקציות העדכון והיצירה של ריילס. אפשר למצוא את הכללים בקישור הזה ואם אתם כותבים בריילס שווה לשלב אותם גם ביישומים שלכם: https://github.com/betterment/betterlint/