دعوت مشتری به یک پروژه، بدون دادن کل شرکت به او

درخواستی که اکثر ابزارها را خراب می‌کند

مشتری می‌خواهد پروژه‌اش را ببیند. راه ساده این است: «برایش یک حساب معمولی بساز» — و حالا او می‌تواند لیست تیم را مرور کند، هر پروژه‌ی مشتری دیگری را در نوار کناری ببیند، و اگر کسی یادش نرفته باشد قفلش کند، تب مالی را هم باز کند. اکثر سیستم‌های مجوز برای آدم‌های داخل شرکت ساخته شده‌اند، پس اولین حساب بیرونی همان چیزی را برملا می‌کند که کسی صراحتاً پنهانش نکرده بود.

«مهمان» در WKFGo واقعاً یعنی چه

مهمان یک ردیف واقعی User است — همان جدول، همان ورود — با یک فیلد تنظیم‌شده: IsGuest = true. این پرچم دو کار مستقل انجام می‌دهد، و فهمیدن اینکه چرا دوتاست اهمیت دارد.

اول، هنگام دعوت، مهمان همان مجوزهای دسترسی‌ویژگیِ هر همکار دیگری را در همان پروژه می‌گیرد — DASHBOARD_VIEW، TASK_VIEW، KNOWLEDGE_VIEW_OTHERS، DOCUMENTS_VIEW، DESCRIPTION_VIEW برای دسترسی نمایشی، به‌علاوه TASK_ADD و KNOWLEDGE_ADD اگر به‌عنوان ویرایشگر دعوت شده باشد. این همان سیستم مجوز معمولی است — چیز خاصی نیست، فقط محدود به یک projectId.

دوم — و این بخشی است که واقعاً برای امنیت اهمیت دارد — یک میان‌افزار محافظ روی هر درخواست اجرا می‌شود و isGuest را از زمینه‌ی احراز هویت چک می‌کند. اگر true بود، درخواست را برای فهرستی از پیشوندهای سطح‌سازمان کاملاً مسدود می‌کند، بدون توجه به اینکه جدول دسترسی‌ویژگی چه می‌گوید:

var guestDenyPrefixes = []string{
    "/api/teams", "/api/finances", "/api/saas", "/api/audit", "/api/usage",
    "/api/reports", "/api/project-roles", "/api/feature-accesses", "/api/organization",
    "/api/insight", "/api/portfolio", "/api/git", "/api/email", "/api/packages",
    "/api/goals", "/api/workload", "/api/roles", "/api/digest", "/api/ai/config",
    "/api/ai/usage", "/api/sprints", "/api/scrum",
}

چرا دو لایه به‌جای یکی

سؤال بدیهی این است: اگر مجوزهای دسترسی‌ویژگی از قبل محدودند، این فهرست مسدودی دوم برای چیست؟ چون دسترسی‌ویژگی یک سیستم مجوز مثبت است — چیزی را می‌دهد که صراحتاً پیکربندی کرده‌اید، و درستی‌اش وابسته به این است که هر مسیر به کلید ویژگیِ درست وصل باشد. اگر یک نقطه‌پایانی جدید بدون کلید ویژگیِ درست منتشر شود، یا ادمینی مجوزی که برای همکاران بوده را اشتباهی به کسی بدهد، هیچ‌چیز دیگری جلوی یک کاربر معمولی را نمی‌گیرد. مهمان، چون بیرونی است، به یک پشتیبان نیاز دارد که وابسته به درست‌پیکربندی‌بودن هر مسیر نباشد — فهرست مسدودی که فقط بر اساس «آیا این یک سطح سازمانی است» فعال می‌شود، مستقل از چیزی که جدول مجوزها می‌گوید.

این الگوی دفاع‌درعمق است: یک لایه می‌گوید مهمان به چه چیزی می‌تواند برسد، دیگری می‌گوید مهمان هرگز به چه چیزی نمی‌رسد، و دومی حتی اگر اولی اشتباه پیکربندی شده باشد برنده می‌شود.

تنها استثنای عمدی

مهمان‌ها از /api/feature-accesses — نقطه‌پایانی مدیریت — مسدودند، اما اجازه‌ی خواندن مجوزهای خودشان را از طریق /api/feature-accesses/check دارند. بدون این استثنا، رابط کاربری خود مهمان نمی‌توانست تشخیص دهد دکمه‌ی «افزودن تسک» را نشان بدهد یا نه، چون هیچ راهی برای پرسیدن «من اینجا چه می‌توانم بکنم؟» ندارد. این یک استثنای باریک، فقط-خودش، فقط-خواندنی است، نه یک سوراخ در فهرست مسدودی.

چیزی که مهمان‌ها هرگز نمی‌گیرند

هنوز جریان دعوت با لینک ایمیلی وجود ندارد — ادمین با ایمیل و پروژه دعوت می‌کند و پاسخ شامل یک رمز تصادفی است که باید از کانالی امن به مهمان برساند. این رمز هنگام ساخت حساب هش می‌شود، اما کد دعوت آن را «یک‌بارمصرف» نمی‌کند و اجبار خودکاری برای تعویض در اولین ورود هم در همین جریان وجود ندارد؛ بنابراین ادمین باید تحویل امن و تغییر رمز اولیه را بخشی از فرایند ورود مهمان بداند.

اشتباهات رایج

فرض کردن «مهمان» یعنی فقط‌خواندنی. نیست — هر دو سطح VIEWER و EDITOR سطح مهمان هستند؛ EDITOR می‌تواند تسک و ورودی دانش روی همان یک پروژه اضافه کند. فقط‌خواندنی انتخابی است که هنگام دعوت می‌کنید، نه ویژگی ذاتی پرچم مهمان.

فرض اینکه همان ایمیل را می‌توان دوباره برای پروژه‌ی دوم دعوت کرد. جریان فعلی هنگام دعوت یک کاربر تازه می‌سازد و ایمیل یا نام کاربری تکراری را در همان سازمان رد می‌کند. پس دعوت دوم برای همان ایمیل خودکار دسترسی پروژه‌ی قبلی را گسترش نمی‌دهد و حتی حساب دوم هم نمی‌سازد؛ دسترسی چندپروژه‌ای باید آگاهانه از مسیر مدیریت مجوزها طراحی شود، نه با تکرار دعوت.

فراموش کردن چک تداخل نام کاربری. نام کاربری مهمان ایمیلش است، و باید درون همان سازمان آزاد باشد — اگر همکاری قبلاً همان ایمیل را نام کاربری کرده باشد، دعوت به‌جای ادغام خاموش دو حساب، تمیز شکست می‌خورد.

سؤالات متداول

آیا مهمان می‌تواند مهمان‌های دیگر همان پروژه را ببیند؟

فقط چیزی که TASK_VIEW و بقیه درباره‌ی افراد مسئول تسک‌هایی که از قبل می‌تواند ببیند نشان می‌دهند — سطح جداگانه‌ای برای «فهرست همه‌ی مهمان‌ها» وجود ندارد، و فهرست /api/users برای مهمان‌ها کاملاً مسدود است (می‌توانند یک کاربر را با شناسه برای نام مسئول بگیرند، نه مرور فهرست کامل).

اگر ادمین بعداً دسترسی پروژه را لغو کند چه می‌شود؟

مجوزهای دسترسی‌ویژگی فقط ردیف‌اند — حذفشان کنید و مهمان در درخواست بعدی دیدش را از دست می‌دهد؛ میان‌افزار محافظ همچنان سطوح سازمانی را مسدود نگه می‌دارد.

آیا مهمان روی سقف صندلی‌های اشتراک حساب می‌شود؟

بله. شمارش فعلی صندلی‌های قابل‌صورتحساب همه‌ی کاربران سازمان به‌جز هویت‌های ایجنت را حساب می‌کند؛ مهمان چون یک کاربر عادی با IsGuest است، از سقف صندلی جدا نشده است. پیش از دعوت گسترده، محدودیت پلن را در صفحه‌ی صورتحساب بررسی کنید.

قبل از دعوت، این آزمایش سه‌دقیقه‌ای را انجام دهید

یک پروژه‌ی آزمایشی بسازید، یک مهمان VIEWER دعوت کنید و با همان حساب وارد شوید. سه چیز را کنترل کنید: فقط پروژه‌ی دعوت‌شده دیده شود، دکمه‌های ساخت تسک و دانش نمایش داده نشوند و مسیرهای سازمانی مثل گزارش، مالی و تیم پاسخ دسترسی‌نداشتن بدهند. برای آزمودن EDITOR یک حساب آزمایشی جدا دعوت کنید و فقط توانایی افزودن تسک و دانش در همان پروژه را بسنجید. این آزمایش کوتاه، تفاوت «نقش در رابط کاربری» با محدودیت واقعی سمت سرور را آشکار می‌کند.

برای همکاری چندماهه‌ای که مشتری باید نظر بدهد یا تسک بسازد، حساب مهمان انتخاب مناسبی است. اگر مخاطب فقط باید یک نمای لحظه‌ای و فقط‌خواندنی ببیند، لینک عمومی قابل‌لغو اصطکاک کمتری دارد. اگر همکار باید به چند پروژه، گزارش ظرفیت یا فرایندهای داخلی برسد، او دیگر مهمان نیست و بهتر است با نقش سازمانی و حداقل مجوز لازم عضو شود.

محدودیت فعلی: تحویل رمز موقت به مهمان خودکار نیست و ادمین باید آن را از یک کانال امن منتقل کند. همچنین دسترسی مهمان جایگزین بازبینی دوره‌ای مجوزها نیست؛ پایان قرارداد باید با لغو دسترسی و بررسی حساب همراه باشد.

جمع‌بندی

دسترسی بیرونی محدود یعنی «حساب بساز و امیدوار باش کسی روی لینک اشتباه کلیک نکند» نیست — دو لایه‌ی مستقل است که به هم اعتماد ندارند: مجوزهای صریح به‌ازای هر پروژه برای چیزی که مهمان باید ببیند، و یک فهرست مسدودیِ کدگذاری‌شده برای چیزی که مهمان هرگز نباید ببیند، صرف‌نظر از اینکه لایه‌ی اول چه می‌گوید.