پرش به مستندات
API

اسکوپ‌ها

اینکه یک کلید اجازهٔ چه کاری دارد.

واژگان

مجموعه‌ای بسته، به شکل 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 بگذارد. یک ارسال به هر سه نیاز دارد، و ردشدن تنها نخستینی را نام می‌برد که با آن روبه‌رو شده است.