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

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

نقش و دسترسی در وردپرس: مفاهیم پایه

وردپرس از ابتدا با سیستم نقش و دسترسی کار می‌کند و این ساختار، یکی از پایه‌ای‌ترین لایه‌های امنیتی آن است. سه مفهوم کلیدی که باید تفکیک شوند:

  • نقش (Role): مجموعه‌ای از دسترسی‌ها که به یک کاربر داده می‌شود. مثال: «مدیر»، «ویرایشگر»، «نویسنده»، «مشارکت‌کننده»، «مشترک».
  • دسترسی (Capability): یک مجوز مشخص برای انجام یک کار. مثال: edit_posts، publish_posts، manage_options.
  • کاربر (User): موجودیتی که یک یا چند نقش به آن اختصاص داده می‌شود.

رابطه این سه: نقش، مجموعه‌ای از دسترسی‌هاست؛ کاربر، صاحب یک یا چند نقش است. بررسی دسترسی کاربر، با تابع current_user_can انجام می‌شود — و این تابع، قلب تمام سیستم امنیت وردپرس است. راهنمای تفصیلی این مباحث در توابع نقش و دسترسی و PHP امن در وردپرس آمده است.

در امنیت، «حداقل دسترسی» یک شعار نیست؛ قوی‌ترین لایه دفاعی است که کمترین هزینه را دارد.

توابع اصلی بررسی دسترسی

پرتکرارترین توابع در کار با نقش و دسترسی:

current_user_can( "edit_posts" );
current_user_can( "manage_options" );
current_user_can( "edit_post", $post_id );
current_user_can( "edit_user", $user_id );

user_can( $user_id, "manage_options" );
user_can( $user_id, "edit_post", $post_id );

$user = wp_get_current_user();
$user->has_cap( "edit_posts" );
$user->roles;

نکته مهم: همیشه current_user_can را روی «دسترسی» (capability) صدا بزنید، نه روی نام نقش. این تفکیک، در پروژه‌های چندساله حیاتی است چون نقش‌ها ممکن است بازتعریف شوند ولی دسترسی‌ها ثابت می‌مانند. راهنمای تفصیلی در توابع داده‌های کاربر و PHP امن.

نقش‌های پیش‌فرض وردپرس

وردپرس پنج نقش پیش‌فرض دارد که در اکثر پروژه‌ها کافی نیستند:

نقشدسترسی‌های کلیدیکاربرد رایج
Administratorهمه دسترسی‌هامدیر کل سایت
Editorویرایش و انتشار همه نوشته‌هامدیر محتوا
Authorانتشار نوشته‌های خودشنویسنده فعال
Contributorنوشتن پیش‌نویس، بدون انتشارنویسنده مهمان
Subscriberفقط خواندن و پروفایلعضو / مشتری

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

ساخت نقش سفارشی

ساخت نقش سفارشی با تابع add_role انجام می‌شود. این کار، باید در زمان فعال‌سازی افزونه یا قالب انجام شود تا در هر بار لود، تکرار نشود:

function myplugin_add_custom_roles() {
    add_role( "sales_manager", "مدیر فروش", array(
        "read"                    => true,
        "edit_products"            => true,
        "edit_others_products"     => true,
        "publish_products"         => true,
        "read_private_products"    => true,
        "edit_shop_orders"         => true,
        "read_shop_orders"         => true,
        "manage_woocommerce"       => true,
    ) );
}
register_activation_hook( __FILE__, "myplugin_add_custom_roles" );

نکته مهم: نقش سفارشی، باید فقط در فعال‌سازی ساخته شود؛ نه در هر بار لود. اگر اشتباه کنید و در init بگذارید، در هر درخواست به دیتابیس نوشته می‌شود و کارایی سایت را می‌کشد. راهنمای hook فعال‌سازی در هوک‌های وردپرس و ساختار فایل‌های افزونه استاندارد.

افزودن دسترسی به نقش موجود

گاهی نیاز داریم دسترسی جدیدی به یک نقش موجود (مثلاً Editor) اضافه کنیم:

function myplugin_add_cap_to_editor() {
    $role = get_role( "editor" );
    if ( $role ) {
        $role->add_cap( "manage_my_custom_feature" );
    }
}
add_action( "admin_init", "myplugin_add_cap_to_editor" );

نکته: بهتر است این کار در فعال‌سازی افزونه انجام شود، نه در admin_init هر درخواست. اگر در admin_init بگذارید، در هر لود پیشخوان یک نوشتن روی دیتابیس دارید. برای بازبینی دوره‌ای هم، در نظر بگیرید که دسترسی اضافه‌شده در حذف افزونه، باقی می‌ماند و باید در uninstall.php پاک شود. راهنمای uninstall.php در ساختار فایل‌های افزونه.

حذف نقش و دسترسی

حذف نقش سفارشی، با تابع remove_role انجام می‌شود:

remove_role( "sales_manager" );

و حذف دسترسی از یک نقش:

$role = get_role( "editor" );
if ( $role ) {
    $role->remove_cap( "manage_my_custom_feature" );
}

نکته حیاتی: حذف نقش، کاربرانی که آن نقش را داشتند را بدون نقش نمی‌کند؛ فقط آن‌ها بدون نقش سفارشی می‌مانند و نقش‌های دیگرشان حفظ می‌شود. اگر کاربری فقط این نقش را داشت، باید دستی به نقش پیش‌فرض دیگری مثل «مشترک» منتقل شود. راهنمای مدیریت کاربران در افزونه‌های مدیریت کاربران.

بررسی دسترسی در قالب و افزونه

پرتکرارترین کاربرد current_user_can در نمایش شرطی عناصر:

if ( current_user_can( "edit_posts" ) ) {
    printf(
        "<a href="%s" class="edit-link">ویرایش</a>",
        esc_url( get_edit_post_link( get_the_ID() ) )
    );
}

یک اشتباه رایج: نمایش لینک ویرایش با edit_post_link بدون بررسی دسترسی. اگر کاربر مهمان، روی این لینک کلیک کند، به صفحه ورود هدایت می‌شود ولی خود لینک، یک سیگنال به مهاجم است که «اینجا یک نقطه قابل ویرایش وجود دارد». راهنمای امنیت قالب در PHP امن و امنیت وردپرس برای مبتدیان.

بررسی دسترسی در فرم و AJAX

در فرم‌ها و درخواست‌های AJAX، بررسی دسترسی حیاتی‌تر است:

function myplugin_ajax_save_order() {
    check_ajax_referer( "myplugin_nonce", "nonce" );
    
    if ( ! current_user_can( "edit_shop_orders" ) ) {
        wp_send_json_error( array( "message" => "دسترسی غیرمجاز" ), 403 );
    }
    
    // پردازش
    wp_send_json_success( $data );
}
add_action( "wp_ajax_myplugin_save_order", "myplugin_ajax_save_order" );

سه لایه امنیت: نانس، بررسی وضعیت ورود (با check_ajax_referer)، و بررسی دسترسی. راهنمای کامل AJAX در نانس وردپرس، پیاده‌سازی نانس در فرم‌ها، و PHP امن.

بررسی دسترسی در REST API

در REST API، بررسی دسترسی با permission_callback انجام می‌شود:

register_rest_route( "myplugin/v1", "/orders", array(
    "methods"             => "GET",
    "callback"            => "myplugin_get_orders",
    "permission_callback" => function() {
        return current_user_can( "edit_shop_orders" );
    },
) );

نکته: permission_callback خالی یا __return_true بدون دلیل، خطرناک است. هر endpoint باید دقیقاً بررسی کند که چه کسی می‌تواند به آن دسترسی داشته باشد. راهنمای کامل در ساخت API اختصاصی و REST API وردپرس.

مپ کردن نقش‌ها به دسترسی‌ها

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

function myplugin_register_caps() {
    $caps = array(
        "manage_myplugin"          => array( "administrator" ),
        "edit_myplugin_items"      => array( "administrator", "editor" ),
        "view_myplugin_reports"    => array( "administrator", "editor" ),
    );
    
    foreach ( $caps as $cap => $roles ) {
        foreach ( $roles as $role_name ) {
            $role = get_role( $role_name );
            if ( $role ) {
                $role->add_cap( $cap );
            }
        }
    }
}
register_activation_hook( __FILE__, "myplugin_register_caps" );

مزیت این الگو: اگر روزی مدیریت افزونه به نقش دیگری منتقل شد، فقط این تابع را تغییر می‌دهید، نه همه کدها. راهنمای تفصیلی در توابع نقش و دسترسی.

نقش‌های سفارشی و ووکامرس

در فروشگاه‌های ووکامرسی، نقش‌های ووکامرس هم به مجموعه اضافه می‌شود: shop_manager، customer. اگر فروشگاه دارید، بررسی دسترسی باید این نقش‌ها را هم در نظر بگیرد. راهنمای کامل در قالب مناسب ووکامرس، امنیت فروشگاه ووکامرس، و مدیریت سفارش‌های ووکامرس.

if ( current_user_can( "manage_woocommerce" ) ) {
    // پنل مدیریت فروشگاه
}

نقش‌های سفارشی و سایت آموزشی

در سایت‌های آموزشی، نقش‌های «مدرس»، «دستیار مدرس»، و «دانشجو» رایج است. این نقش‌ها باید با دقت طراحی شوند تا هم مرز دسترسی روشن باشد، هم با سیستم پرداخت یکپارچه باشد. راهنمای LMS در افزونه‌های سایت آموزشی و قالب آموزشی.

نقش‌های سفارشی در مالتی‌سایت

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

الگوهای پیشرفته: نقش موقت و نقش با زمان

گاهی نیاز داریم یک دسترسی موقت داشته باشیم. مثلاً کاربری که تا پایان ماه جاری می‌تواند به بخش خاصی دسترسی داشته باشد:

add_filter( "user_has_cap", function( $allcaps, $caps, $args, $user ) {
    if ( ! in_array( "access_premium", $caps, true ) ) {
        return $allcaps;
    }
    
    $expire = get_user_meta( $user->ID, "_premium_expire", true );
    if ( $expire && strtotime( $expire ) < current_time( "timestamp" ) ) {
        $allcaps["access_premium"] = false;
    }
    return $allcaps;
}, 10, 4 );

این الگو، در سیستم‌های عضویت و اشتراک کاربرد دارد. راهنمای کامل متادیتای کاربر در کار با User Meta و توابع متادیتا.

حداقل دسترسی: قاعده طلایی

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

  • پشتیبان مشتری، به manage_options نیاز ندارد.
  • نویسنده، به edit_others_posts نیاز ندارد.
  • اپراتور انبار، به manage_woocommerce کامل نیاز ندارد.
  • مدرس، به install_plugins نیاز ندارد.

هرچه سطح دسترسی کمتر باشد، آسیب‌پذیری کمتر. راهنمای کامل در امن‌سازی ورود ادمین و امنیت وردپرس برای مبتدیان.

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

  • استفاده از نقش به‌جای دسترسی در شرط: به‌جای in_array( "editor", $user->roles )، از current_user_can استفاده کنید. توابع نقش و دسترسی.
  • ساخت نقش در هر درخواست: افزونه‌ای که در init نقش می‌سازد، در هر بار لود، یک نوشتن دیتابیس دارد. راه‌حل: در فعال‌سازی. هوک‌های وردپرس.
  • نبود current_user_can در فرم‌های حساس: خطر دسترسی غیرمجاز. PHP امن.
  • نبود check_ajax_referer در AJAX: خطر CSRF. نانس وردپرس.
  • نبود permission_callback در REST API: افشای داده. API اختصاصی.
  • نبود پاک‌سازی نقش در uninstall: باقی‌ماندن دسترسی در دیتابیس. پاک‌سازی دیتابیس.
  • نادیده‌گرفتن نقش‌های ووکامرس یا LMS: تعارض با سیستم‌های موجود. افزونه‌های ووکامرس.
  • نبود تست با چند نقش: رفتار سایت با نقش‌های مختلف باید تست شود. تست و دیباگ.
  • نبود escape در نمایش فیلدهای کاربر: خطر XSS. پاک‌سازی داده‌ها.
  • نبود بازبینی دوره‌ای نقش‌ها: نقش‌های اضافه، در بلندمدت بدهی امنیتی می‌شوند. افزونه‌های مدیریت کاربران.

جمع‌بندی

توابع نقش و دسترسی وردپرس، چهار گروه اصلی دارند: بررسی دسترسی (current_user_can، user_can)، ساخت نقش (add_role)، افزودن دسترسی (add_cap)، و حذف (remove_role، remove_cap). سه اصل را در پایان تاکید می‌کنم: اول، همیشه روی دسترسی شرط بگذارید، نه روی نام نقش. دوم، نقش‌های سفارشی را در فعال‌سازی بسازید، نه در هر درخواست. سوم، قاعده «حداقل دسترسی» را در طراحی هر نقش جدی بگیرید.

اگر تجربه‌ای از یک باگ امنیتی یا مشکل دسترسی در پروژه‌تان دارید که با نقش و دسترسی حل شد، در دیدگاه‌ها بنویسید — همان گزارش‌های واقعی، این راهنما را برای توسعه‌دهنده بعدی دقیق‌تر می‌کند. 🔐