اسکوپها
اینکه یک کلید اجازهٔ چه کاری دارد.
واژگان
مجموعهای بسته، به شکل resource:action. آنقدر کوچک که بتوان به یک آدم در فهرستی از تیکها نشانش داد، و آنقدر پایدار که دسترسی ذخیرهشده یک سال بعد هم همان معنا را بدهد. همین واژگان است که نقش فضای کاری با آن نوشته میشود و ابزارهای MCP را دروازهبانی میکند، پس کلاینت فقطخواندنی حتی نمیتواند ابزار ارسال را ببیند. یک الفبا، سه سطح.
| اسکوپ | چه میدهد |
|---|---|
| emails:send | ارسال ایمیل |
| emails:read | خواندن پیامهای ارسالشده و وضعیت تحویلشان |
| drafts:read | خواندن پیشنویسها |
| drafts:write | ساخت و ویرایش پیشنویسها |
| threads:read | خواندن رشتهها و پیامها |
| threads:write | برچسبزدن، خواندهکردن و بایگانی رشتهها |
| labels:read | خواندن برچسبها |
| labels:write | ساخت و ویرایش برچسبها |
| contacts:read | خواندن مخاطبان |
| contacts:write | افزودن، ویرایش و حذف مخاطبان |
| audiences:read | خواندن مخاطبان هدف و اینکه چه کسانی در آنها هستند |
| audiences:write | ساخت و ویرایش مخاطبان هدف، و تغییر اینکه چه کسانی در آنها هستند |
| calendar:read | خواندن رویدادها و دعوتهای تقویم |
| calendar:write | ساخت، تغییر و پاسخ به رویدادهای تقویم |
| templates:read | خواندن قالبهای ایمیل و پیشنمایش آنها |
| templates:write | ساخت و ویرایش قالبهای ایمیل و ارسال با آنها |
| domains:read | خواندن دامنهها و وضعیت DNS آنها |
| domains:write | تأیید و پیکربندی دامنهها |
| webhooks:read | خواندن اندپوینتهای وبهوک و تحویلها |
| webhooks:write | ساخت، ویرایش و آزمون وبهوکها |
| rules:read | خواندن قانونهای نامه و آزمون آنها |
| rules:write | ساخت، ویرایش و تغییر ترتیب قانونهای نامه |
| connections:read | خواندن اینکه کدام صندوقهای پستی متصلاند |
| members:read | دیدن اینکه چه کسانی در فضای کاریاند و چه دارند |
| members:write | افزودن و حذف افراد، و تغییر آنچه میتوانند به آن دسترسی داشته باشند |
| roles:read | خواندن نقشهایی که این فضای کاری تعریف میکند |
| roles:write | ساخت، ویرایش و حذف نقشها |
| settings:read | خواندن تنظیمات صندوق پستی، از جمله امضا |
| settings:write | تغییر تنظیمات صندوق پستی و امضا |
| keys:write | جایگزینی راز خودش بدون اینکه کسی کنسول را باز کند |
کلیدی که بدون فهرست اسکوپ سنجیده ساخته شود emails:send میگیرد و نه چیز دیگر. پیشفرض امن برای یک اعتبارنامه، باریکترین چیزی است که آن را بهکار میآورد.
سقف یک کلید نقشی است که پشت آن است
یک کلید میتواند در برابر یک نقش صادر شود، و نقش سقف است نه یک اعطای دوم. آنچه کلید واقعاً میتواند بکند اشتراک اسکوپهای خودش با مجوزهای آن نقش است (key.scopes ∩ role.permissions)، که یک بار در مرز و در هر درخواست، پیش از رسیدن به هر اندپوینتی، محاسبه میشود. هیچچیز پاییندستی نمیداند نقشها وجود دارند: اسکوپی که نقش نداشته باشد بهسادگی در فهرستی که بررسیهای اسکوپ میخوانند نیست.
پس این دو فهرست با هم خوانده میشوند و هیچکدام بهتنهایی برنده نیست. کلیدی که emails:send دارد زیر نقشی که آن را ندارد نمیتواند ارسال کند؛ نقشی که emails:send دارد به کلیدی که هرگز آن را نخواسته چیزی نمیدهد. تیکزدن یک اسکوپ درخواست اختیار است، و نقش تصمیم میگیرد چه اندازه از آنچه خواستهاید به شما برسد.
کلیدی که هیچ نقشی ندارد سقفی ندارد، و بنابراین به گستردگی فضای کاریای است که برایش صادر شده. این همان چیزی است که هر کلید ساختهشده پیش از وجود نقشها با خود دارد و همان چیزی که مالک با دستنزدن به آن فیلد باز هم میگیرد، پس نقش null گستردهترین وضعیتی است که یک کلید میتواند داشته باشد، نه باریکترین. به همین دلیل هم حذف یک نقش شما را وادار میکند بگویید کلیدهایش کجا بروند: یتیمکردنشان بیصدا هر یک از آنها را ارتقا میداد.
اشتراک به ازای هر درخواست حل میشود نه اینکه هنگام صدور روی کلید مهر شود. همین باعث میشود تنگکردن یک نقش لغو دسترسی زندهای باشد که در فراخوانی بعدی فراخوانکننده و بدون نیاز به چرخاندن کلید اثر کند، و گستردهکردنش هم دقیقاً به همان شکل زنده است، که همان نیمهای است که بهیادسپردنش میارزد.
GET /ping و GET /keys/self مقدار scopes را کنار grantedScopes و roleId گزارش میکنند، بهخاطر یک شکست مشخص. scopes فهرست مؤثر است و تنها فهرستی که چیزی را مجاز میکند؛ grantedScopes آن چیزی است که کلید با آن صادر شده است. هر چه در دومی باشد و در اولی نباشد را نقش گرفته است، و همان تفاوت کل پاسخ به «کلید من emails:send دارد و insufficient_scope میگیرم» است. راهحل تغییر نقش است نه کلیدی دیگر.
curl "$OE/ping" -H "$AUTH" { "ok": true, "keyId": "4c1b257a66287fd113bd89d0", "mode": "live", "scopes": ["emails:read", "threads:read"], "roleId": "role_c40a95f21cc65d31c2a89e07", "grantedScopes": ["emails:send", "emails:read", "threads:read"], "workspaceId": "10417196-e324-4283-af98-66ec62167c47"}پنج مجوز هرگز نمیتوانند به یک کلید برسند: api-keys:read، api-keys:write، billing:read، billing:write و workspace:manage. اینها مجوزند اما اسکوپ نیستند، پس هیچ نقشی هر چه هم سخاوتمند باشد نمیتواند آنها را روی یک توکن بگذارد: ساختن کلیدی دیگر، تغییر کارهایی که کلیدی دیگر میتواند بکند، یا جابهجاکردن پلن کاری است که تنها شخصِ واردشده انجام میدهد. تنها کاری که یک کلید میتواند با خودش بکند جایگزینی راز خودش است، پشت اسکوپ keys:write. GET /roles/permissions آن پنجتا را با scope: false نشان میکند، و همین است که میگذارد یک کامپوننت هم ماتریس نقشها و هم فهرست تیکهای ساخت کلید را رندر کند.
roles:write عملاً کل واژگان است، و وانمود به خلافش مستندات خطرناکتری میبود. کلیدی که آن را دارد میتواند همان نقشی را که سقفش است PATCH کند و هر چیز دیگری را به خودش بدهد، و چون سقف در هر درخواست حل میشود، سقف گستردهتر در همان فراخوانی بعدی اعمال میشود. این حفرهای نیست که باید بسته شود، چون ویرایشگر نقشی که نتواند نقشها را ویرایش کند ویرایشگر نقش نیست. دلیلی است برای اینکه roles:write را روی کلیدی نگذارید که تنها قرار بوده فهرست اعضا را بخواند.
محدودهٔ ارسال
جدا از اسکوپها، یک کلید میتواند در آنچه با آن ارسال میکند تنگ شود. دو فهرست با خود دارد. domainAllowlist دامنههای کامل را نگه میدارد، و کلیدی که دامنهای دارد میتواند با هر نشانی روی آن ارسال کند، از جمله نشانیهایی که پس از کلید ساخته شدهاند. addressAllowlist تکنشانیها را نگه میدارد. هر دو را خالی بگذارید و کلید به گستردگی فضای کاری است، هرگز گستردهتر. GET /keys/self هر دو فهرست را نشان میدهد و GET /addresses گزارش میکند که یک کلید مشخص واقعاً از چه چیزی میتواند استفاده کند، که پاسخ یک from_address_forbidden توضیحناپذیر است.
همان مجموعه آنچه را کلید میخواند هم تنگ میکند. نامهٔ ارسالی، ردیابی و تقویم تنها برای نشانیهایی پاسخ میدهند که کلید میتواند با آنها ارسال کند، پس کلیدی که به یک دامنه محدود است نه از طرف دامنهای دیگر ارسال میکند و نه میخواند. یک دامنهٔ کامل همچنین به کلید اجازه میدهد میزبان ردیابی آن دامنه را تنظیم کند، کاری که کلید محدود به تکنشانی نمیتواند بکند.
پس سه تنگسازی داریم، و به جای آنکه یکدیگر را باطل کنند روی هم مینشینند: اسکوپهای روی کلید، مجوزهای نقشِ بالای آن، و دامنهها و نشانیهایی که میتواند در هدر From بگذارد. یک ارسال به هر سه نیاز دارد، و ردشدن تنها نخستینی را نام میبرد که با آن روبهرو شده است.