راهاندازی جدول پایگاه داده مجوزها
محدود کردن API به طوری که دادههای فیلم فقط توسط کاربران فعال قابل دسترسی و ویرایش باشد، مفید است، اما گاهی اوقات ممکن است به سطح کنترل دقیقتری نیاز داشته باشید. به عنوان مثال، در مورد ما ممکن است راضی باشیم که کاربران «عادی» API ما بتوانند دادههای فیلم را بخوانند (به شرطی که فعال باشند)، اما میخواهیم دسترسی نوشتن را به زیرمجموعه کوچکی از کاربران مورد اعتماد محدود کنیم.
در این فصل، مفهوم مجوزها (permissions) را به برنامه خود معرفی میکنیم، به طوری که فقط کاربرانی که مجوز خاصی دارند بتوانند عملیات خاصی انجام دهند. در مورد ما، دو مجوز ایجاد خواهیم کرد: مجوز movies:read که به کاربر اجازه میدهد فیلمها را دریافت و فیلتر کند، و مجوز movies:write که به کاربران اجازه میدهد فیلمها را ایجاد، ویرایش و حذف کنند.
مجوزهای مورد نیاز با endpointهای API ما به شرح زیر هماهنگ هستند:
| متد | الگوی URL | مجوز مورد نیاز |
|---|---|---|
| GET | /v1/healthcheck | — |
| GET | /v1/movies | movies:read |
| POST | /v1/movies | movies:write |
| GET | /v1/movies/:id | movies:read |
| PATCH | /v1/movies/:id | movies:write |
| DELETE | /v1/movies/:id | movies:write |
| POST | /v1/users | — |
| PUT | /v1/users/activated | — |
| POST | /v1/tokens/authentication | — |
رابطه بین مجوزها و کاربران
رابطه بین مجوزها و کاربران نمونه عالی از یک رابطه چند به چند است. یک کاربر ممکن است مجوزهای متعددی داشته باشد و همان مجوز ممکن است به کاربران متعددی تعلق داشته باشد.
روش کلاسیک مدیریت رابطه چند به چند در یک پایگاه داده رابطهای مانند PostgreSQL، ایجاد یک جدول اتصال (joining table) بین دو موجودیت است. من به سرعت توضیح میدهم که این چگونه کار میکند.
فرض کنید دادههای کاربران خود را در یک جدول users ذخیره میکنیم که به این شکل است:
| id | … | |
|---|---|---|
| 1 | alice@example.com | … |
| 2 | bob@example.com | … |
و دادههای مجوزهای ما در یک جدول permissions به این شکل ذخیره میشوند:
| id | code |
|---|---|
| 1 | movies:read |
| 2 | movies:write |
سپس میتوانیم یک جدول اتصال به نام users_permissions ایجاد کنیم تا اطلاعات مربوط به کدام کاربران چه مجوزهایی دارند را ذخیره کند، مشابه این:
| user_id | permission_id |
|---|---|
| 1 | 1 |
| 2 | 1 |
| 2 | 2 |
در مثال بالا، کاربر alice@example.com (شناسه کاربر 1) فقط مجوز movies:read (شناسه مجوز 1) را دارد، در حالی که bob@example.com (شناسه کاربر 2) هر دو مجوز movies:read و movies:write را دارد.
درست مانند رابطه یک به چند که قبلاً در کتاب بررسی کردیم، ممکن است بخواهید این رابطه را از هر دو طرف در مدلهای پایگاه داده خود جستجو کنید. به عنوان مثال، در مدلهای پایگاه داده خود ممکن است بخواهید متدهای زیر را ایجاد کنید:
PermissionModel.GetAllForUser(user) → Retrieve all permissions for a user UserModel.GetAllForPermission(permission) → Retrieve all users with a specific permission
ایجاد SQL migrations
بیایید این را عملی کنیم و یک SQL migration ایجاد کنیم که جداول permissions و users_permissions جدیدی در پایگاه داده ما ایجاد میکند، بر اساس الگویی که در بالا توضیح دادیم.
دستور زیر را اجرا کنید تا فایلهای migration ایجاد شوند:
$ migrate create -seq -ext .sql -dir ./migrations add_permissions /home/alex/Projects/greenlight/migrations/000006_add_permissions.up.sql /home/alex/Projects/greenlight/migrations/000006_add_permissions.down.sql
سپس دستورات SQL زیر را به فایل migration ‘up’ اضافه کنید:
CREATE TABLE IF NOT EXISTS permissions ( id bigserial PRIMARY KEY, code text NOT NULL ); CREATE TABLE IF NOT EXISTS users_permissions ( user_id bigint NOT NULL REFERENCES users ON DELETE CASCADE, permission_id bigint NOT NULL REFERENCES permissions ON DELETE CASCADE, PRIMARY KEY (user_id, permission_id) ); -- Add the two permissions to the table. INSERT INTO permissions (code) VALUES ('movies:read'), ('movies:write');
چند نکته مهم در اینجا وجود دارد:
خط
PRIMARY KEY (user_id, permission_id)یک کلید اصلی ترکیبی روی جدولusers_permissionsما تنظیم میکند، جایی که کلید اصلی از هر دو ستونuser_idوpermission_idتشکیل شده است. تنظیم این به عنوان کلید اصلی در اصل به این معنی است که ترکیب کاربر/مجوز میتواند فقط یک بار در جدول ظاهر شود و نمیتواند تکرار شود.هنگام ایجاد جدول
users_permissionsاز نحوREFERENCES usersاستفاده میکنیم تا یک محدودیت foreign key در برابر کلید اصلی جدولusersایجاد کنیم، که تضمین میکند هر مقداری در ستونuser_idدارای مدخل متناظری در جدولusersما باشد. و به همین ترتیب، از نحوREFERENCES permissionsاستفاده میکنیم تا مطمئن شویم که ستونpermission_idدارای مدخل متناظری در جدولpermissionsباشد.
بیایید دستورات DROP TABLE لازم را نیز به فایل migration ‘down’ اضافه کنیم، به شکل زیر:
DROP TABLE IF EXISTS users_permissions; DROP TABLE IF EXISTS permissions;
حالا که این کار تمام شد، لطفاً migration را اجرا کنید:
$ migrate -path ./migrations -database $GREENLIGHT_DB_DSN up 6/u add_permissions (22.74009ms)