تابع current_user_can چطور کار میکند؟
تابع current_user_can برای بررسی دسترسی کاربر در وردپرس؛ معرفی capability، پارامترها، نکات امنیتی و کاربردهای حرفهای.
چرا بررسی دسترسی کاربر یک اصل امنیتی است؟
هر عملیاتی که در وردپرس انجام میشود — از انتشار یک نوشته تا حذف یک کاربر — باید بر اساس مجوزهای از پیش تعریفشده کنترل شود. اگر این کنترل وجود نداشته باشد، هر کاربری میتواند عملیاتی فراتر از نقش خود انجام دهد. این همان اصل کمترین دسترسی (Least Privilege) است که در امنیت اطلاعات بهعنوان یک قاعده بنیادی شناخته میشود. تابعcurrent_user_can() ابزار اصلی وردپرس برای پیادهسازی این اصل است. بدون آن، نه پنل مدیریت امن است، نه REST API و نه حتی یک شورتکد ساده. به همین دلیل، شناخت دقیق این تابع یکی از پیشنیازهای جدی برای هر توسعهدهنده حرفهای وردپرس محسوب میشود.
تابع current_user_can چیست؟
تابعcurrent_user_can() بررسی میکند که آیا کاربر جاری دارای یک capability مشخص است یا نه. این تابع در فایل wp-includes/capabilities.php تعریف شده و در سراسر هسته وردپرس بهعنوان ابزار اصلی کنترل دسترسی استفاده میشود.
خروجی این تابع یک مقدار بولی است: true اگر کاربر دارای آن capability باشد و false در غیر این صورت. نکته مهم این است که این تابع تنها کاربر جاری را بررسی میکند. برای بررسی کاربر خاص، باید از user_can() یا WP_User::has_cap() استفاده کرد.
در حالت پیشفرض، این تابع به کاربر وارد نشده مقدار false بازمیگرداند. بنابراین بررسی دسترسی معمولاً پس از بررسی وضعیت ورود با تابع is_user_logged_in انجام میشود.
مفهوم capability در وردپرس
Capability یک مجوز مشخص است که به یک یا چند نقش (Role) اختصاص داده میشود. برای نمونه،edit_posts به نقش Author داده میشود، manage_options مختص Administrator است و publish_posts برای Editor و بالاتر فعال است.
نقشهای پیشفرض وردپرس عبارتاند از: Subscriber، Contributor، Author، Editor و Administrator. هر نقش مجموعهای از capabilityها را در خود دارد. اما این ساختار ثابت نیست؛ توسعهدهندگان میتوانند نقش سفارشی و capability سفارشی بسازند.
نکته مهم این است که capability ممکن است به یک متابلیتی (Meta Capability) ارجاع دهد. متاکپابیلیتیها بهصورت پویا محاسبه میشوند و به یک شیء مشخص گره میخورند. مثال کلاسیک آن edit_post است که به یک نوشته خاص اشاره دارد و با آرگومان دوم به تابع پاس داده میشود.
امضای تابع و پارامترها
امضای این تابع بهشکل زیر است:function current_user_can( $capability, ...$args ) {
return user_can( wp_get_current_user(), $capability, ...$args );
}
پارامتر اول یک رشته است که capability مورد بررسی را مشخص میکند. پارامترهای بعدی (بهصورت Variadic) برای متاکپابیلیتیها به کار میروند؛ مثلاً current_user_can( 'edit_post', 42 ) بررسی میکند آیا کاربر جاری اجازه ویرایش نوشته با ID 42 را دارد.
اگر کاربر جاری وجود نداشته باشد یا نقش آن بهدرستی تنظیم نشده باشد، مقدار false بازمیگردد. توجه داشته باشید که این تابع همیشه خروجی بولی میدهد و هرگز از خود آرایه یا شیء بازنمیگرداند.
متابیلیتی (Meta Capability)
متابیلیتیها پویا هستند و در زمان اجرا محاسبه میشوند. برای مثال،edit_post یک متاکپابیلیتی است که به edit_posts در سطوح پایهتر گره میخورد. نقشها معمولاً capabilityهای پایه را دریافت میکنند و وردپرس در زمان بررسی، آنها را به متاکپابیلیتیهای مشخص نگاشت میکند.
این موضوع در پروژههای بزرگ اهمیت دارد: اگر بخواهید برای یک کاربر خاص اجازه ویرایش یک نوشته مشخص را بدهید، از متاکپابیلیتی edit_post با آرگومان ID استفاده میکنید، نه از یک capability عمومی. برای مطالعه دقیقتر روی پست تایپ سفارشی و تنظیم capability آن، میتوانید به راهنمای register_post_type مراجعه کنید.
کاربردهای عملی در افزونه و پنل
یک کاربرد رایج این تابع، نمایش یا مخفیکردن بخشهای پنل بر اساس دسترسی است. برای نمونه، منوی تنظیمات تنها برای کاربرانی کهmanage_options دارند قابل مشاهده میشود. صفحههای افزونه نیز باید هم در سطح نمایش منو و هم در سطح پردازش درخواست، این بررسی را انجام دهند.
نمونه ساده برای ساخت یک صفحه منو در پنل:
add_action( 'admin_menu', 'register_my_menu' );
function register_my_menu() {
add_menu_page(
'عنوان صفحه',
'منوی افزونه',
'manage_options',
'my-plugin-slug',
'render_my_page'
);
}
نکته کلیدی این است که پارامتر سوم add_menu_page همان capability است. اگر این مقدار را اشتباه بگذارید، کاربران غیرمجاز ممکن است به صفحه دسترسی پیدا کنند. برای مطالعه بیشتر در این زمینه به راهنمای add_menu_page مراجعه کنید.
کاربرد دیگر، در پردازش فرمهای پنل است. پیش از ذخیره هر داده، باید دو بررسی انجام شود: اول nonce، سپس capability. الگوی صحیح چنین است:
if ( ! current_user_can( 'manage_options' ) ) {
wp_die( esc_html__( 'دسترسی غیرمجاز', 'textdomain' ) );
}
check_admin_referer( 'my_action_nonce' );
تابع check_admin_referer در راهنمای check_admin_referer به تفصیل بررسی شده است.
ترکیب با REST API
در REST API، تابعcurrent_user_can بهعنوان permission_callback استفاده میشود. نمونه:
register_rest_route( 'myplugin/v1', '/settings', array(
'methods' => 'POST',
'callback' => 'my_settings_callback',
'permission_callback' => function() {
return current_user_can( 'manage_options' );
},
) );
نکته مهم این است که permission_callback نباید تنها به یک بررسی ساده اکتفا کند. در مسیرهای دارای نوشتن، ترکیب آن با nonce ضروری است. توضیحات تکمیلی در راهنمای register_rest_route آمده است.
نکات امنیتی و اشتباهات رایج
اشتباه اول، بررسی دسترسی پس از اجرای عملیات است. همیشه بررسی باید پیش از هر تغییر در پایگاه داده یا فایل انجام شود. اشتباه دوم، نبود nonce است. تابعcurrent_user_can تنها میگوید کاربر مجاز است، اما نمیگوید این درخواست از طرف خود کاربر ارسال شده است. برای تأیید اصالت درخواست، nonce لازم است. راهنمای مربوطه در wp_verify_nonce موجود است.
اشتباه سوم، نبود escape در خروجی است. تمام دادههای پویا که پس از بررسی دسترسی نمایش داده میشوند، باید از توابعی مانند esc_html() و esc_attr() عبور کنند. برای جزئیات بیشتر به راهنمای esc_html مراجعه کنید.
اشتباه چهارم، اتکای انحصاری به نقش بهجای capability است. مثلاً بررسی 'administrator' === $user->roles[0] اشتباه است؛ زیرا ممکن است نقش سفارشی یا چند نقشی وجود داشته باشد. همیشه از capability استفاده کنید.
اشتباه پنجم، نبود تست است. باید حالتهای مختلف کاربر را در واحدهای تست پوشش داد: کاربر مهمان، Subscriber، Editor و Administrator.
تحلیل فنی پیشرفته
در نگاه مهندسی، تابعcurrent_user_can یک نقطه کنترل دسترسی (Access Control Checkpoint) است که در چند لایه معماری وردپرس نقش ایفا میکند. لایه اول لایه هسته است؛ هر عملیات حساس در وردپرس پیش از اجرا این تابع را فراخوانی میکند. اگر افزونهای این الگو را رعایت نکند، در واقع یک در پشتی (Backdoor) ایجاد کرده است.
لایه دوم لایه فیلترپذیری است. تابع current_user_can از یک فیلتر به نام map_meta_cap استفاده میکند که امکان بازتعریف متاکپابیلیتیها را فراهم میسازد. این قابلیت در سناریوهای پیچیده — مانند عضویت، اشتراک، یا گردش کار سازمانی — بسیار ارزشمند است.
لایه سوم لایه کشینگ است. نتیجه این تابع در حافظه کش میشود؛ اما در کارهای دستهای (Batch) که کاربر جاری چندین بار تغییر میکند، باید کش را باطل کرد. تابع wp_set_current_user این کار را انجام میدهد، اما توجه به ترتیب فراخوانی اهمیت دارد.
لایه چهارم لایه تست است. تست واحد باید برای هر نقش و هر capability نوشته شود. ابزارهایی مانند WP_Mock یا Brain Monkey امکان شبیهسازی نقشها را فراهم میکنند.
لایه پنجم لایه Observability است. در پروژههای بزرگ، ثبت رخدادهای دسترسی ناموفق میتواند سیگنالهای اولیه نفوذ را آشکار کند. ابزارهایی مانند Audit Trail در ویکیپدیا مفهوم این ردیابی را توضیح میدهند.
در معماری Headless WordPress، این تابع در سمت بکاند اجرا میشود و فرانتاند تنها بر اساس پاسخ API تصمیم میگیرد. بنابراین پاسخهای permission_callback باید بسیار دقیق طراحی شوند. در REST API نیز، ترتیب بررسیها در پاسخهای ۴۰۱ و ۴۰۳ معنا دارد: ۴۰۱ یعنی احراز هویت نشده و ۴۰۳ یعنی احراز هویت شده اما بدون مجوز. تفکیک این دو وضعیت در تجربه کاربری و امنیت اهمیت زیادی دارد.
پرسشهای پرتکرار
تفاوتcurrent_user_can و user_can چیست؟ current_user_can کاربر جاری را بررسی میکند، در حالی که user_can کاربر مشخصی را با پاس دادن ID یا شیء کاربر بررسی میکند.
آیا استفاده از current_user_can در قالب مناسب است؟ بله، اما معمولاً بهتر است منطق دسترسی در افزونه یا در سطح کنترلر باشد تا در قالب. با این حال، استفاده از آن در قالب برای نمایش شرطی بخشها رایج است.
آیا این تابع برای بررسی دسترسی به یک نوشته خاص کاربرد دارد؟ بله، با پاس دادن edit_post و ID نوشته بهعنوان آرگومان دوم.
آیا current_user_can قابل بازنویسی است؟ این تابع Pluggable نیست، اما فیلترهای مرتبط مانند map_meta_cap و user_has_cap امکان تغییر رفتار را میدهند.
چه خطاهایی در استفاده از این تابع رایجاند؟ نبود بررسی پیش از اجرا، نبود nonce و اتکا به نقش بهجای capability از رایجترین آنها هستند.
نتیجه و مسیر ادامه
تابعcurrent_user_can ستون فقرات امنیت منطقی وردپرس است. استفاده درست از آن یعنی تفکیک احراز هویت از مجوزدهی، ترکیب با nonce، escape در خروجی و تست در همه نقشها. هرگز نباید تنها به یک شرط ساده اتکا کرد؛ چرا که مرز میان امنیت و نفوذ تنها به یک خط کد بستگی دارد.
اگر این تابع را در سناریویی خاص — مثلاً با نقشهای سفارشی یا در Multisite — به کار بردهاید و رفتار غیرمنتظره دیدهاید، تجربهتان میتواند راهنمای دیگران باشد. کدام بخش بیشترین زمان را گرفت؟ و راهحل چیست؟ دیدگاه خود را به اشتراک بگذارید.