اعتبارسنجی در پایگاه‌داده - REST

نکته: در صورتی که با مفهوم REST و سرویس‌های سمت سرور و یا با دستور curl آشنایی ندارید، به آشنایی با REST مراجعه کنید.

پیش‌نیازها

  1. در صورتی که با سرویس پایگاه‌داده آشنایی ندارید، به معرفی سرویس پایگاه‌داده مراجعه کنید.
  2. در صورتی که هنوز در پنل توسعه‌دهنده خود تنظیمات لازم برای سرویس پایگاه‌داده خود را انجام نداده‌اید، به تنظیمات پنل مراجعه کنید.
  3. در صورتی که با سرویس کاربران بکتوری آشنایی ندارید و فرآیندهای امنیتی بکتوری را نمی‌شناسید، به معرفی سرویس کاربران مراجعه کنید.
  4. در صورتی که با کار با اشیاء پایگاه داده و جستارهای ساده آشنایی ندارید، به کار با اشیاء مراجعه کنید.

توجه: در همه درخواست‌ها مقدار هدر X-Backtory-Object-Storage-Id باید از صفحه پروژه، بخش کلیدها استخراج و دریافت شود.

به تدریج که پروژه شما به لحاظ حوزه کاری و تعداد کاربران توسعه پیدا می‌کند، طبیعی است که بخواهید بر دسترسی کاربران کنترل بیشتر و جامع‌تری داشته باشید. سرویس دیتابیس با استفاده از مفهومی به نام نقش این امکان را در اختیار شما می‌گذارد. استفاده از نقش روشی منطقی برای گروه‌بندی کاربرانی است که دسترسی یکسانی به داده‌ها دارند. در سرویس دیتابیس کاربران بر اساس نقشی که دارند اجازه دسترسی و تغییر داده‌ها را دارند. مثلا در یک اپ کاربرانی که نقش «خواننده» دارند، ممکن است فقط بتوانند داده‌ها را بخوانند ولی نتوانند داده‌ها را تغییر دهند و در عوض کاربرانی که نقش «نویسنده» دارند می‌توانند داده‌ها را نغییر دهند. یک نقش علاوه بر کاربر می‌تواند شامل تعدادی نقش نیز باشد. در حقیقت، هر اجازه‌ای که برای یک نقش داده می‌شود، به تمامی کاربران و نقش هایی که در آن است نیز داده می‌شود. به عنوان مثالی دیگر، در برنامه شما احتمالا کاربرانی هستند که به عنوان مدیر (moderator) در نظر گرفته شده‌اند و می‌توانند داده‌های دیگر کاربران را تغییر دهند یا حذف کنند. یا مثلا کاربرانی ادمین هستند که علاوه بر اختیارات مدیر می‌تواند تنظیمات کلی برنامه را نیز تغییر دهند. با اضافه کردن کاربران به نقش‌ها، می‌توانیم بدون اینکه نیاز داشته باشیم برای هر کاربر اختیارات دسترسی را جداگانه تعریف کنیم، تنها اختیارات را برای نقش تعریف کنیم.

برای این منظور در سرویس دیتابیس به صورت پیش فرض جدول Roles وجود دارد که در آن نقش‌ها تعریف می‌شوند. هر نقش یک سطر از این جدول است و تعدادی ستون به صورت پیش فرض دارد:

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

در بخش قبلی مثال‌هایی زده شد که نیاز به اعتبارسنجی را نشان می‌داد. به عنوان مثال، تمامی درخواست‌ها دارای هدر Authorization است که در آن یک توکن (که در هنگام لاگین دریافت شده است) قرار می‌گیرد. این توکن می‌تواند توکن master یا توکن کاربر عادی باشد. حال در این بخش به نگاه جامع و کاملی از مفهوم اعتبارسنجی و نحوه عملیاتی کردن آن می‌پردازیم.

همانطور که گفته شد اولین جدولی که در پایگاه داده وجود دارد، جدول نقش (Roles) است. برای ادامه به سراغ این جدول در پنل می رویم:

Empty roles collection

ساخت یک نقش

ساختن یک نقش جدید مانند ساختن یک سطر است با این تفاوت اساسی که مقداردهی ستون name آن اجباری است. نقش‌ها همچنین بایستی مقدار ستون ACL را طوری مشخص کنند که تا جایی که ممکن است محدود کننده باشد و اجازه تغییر در یک نقش به کاربر نادرست داده نشود.

برای ساخت یک نقش درخواستی از نوع POST ارسال می‌کنیم. یک مثال:

curl -X POST \
    --header 'Authorization: Bearer <user-access-token>' \
    --header 'X-Backtory-Object-Storage-Id: <object-storage-Id>' \
    --header "Content-Type: application/json" \	
    -d '{
            "name": "Moderators", 
            "ACL": { 
                "*": { "read": true } 
            } 
        }' \ 
    https://api.backtory.com/object-storage/roles

با این درخواست نقشی با نام “Moderators” ایجاد می‌شود. مقداری که برای ACL در نظر گرفته شده نشان می‌دهد که همه کاربران و زیرنقش‌های Moderators اجازه خواندن دارند.

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

curl -X POST \
    --header 'Authorization: Bearer <user-access-token>' \
    --header 'X-Backtory-Object-Storage-Id: <object-storage-Id>' \
    --header "Content-Type: application/json" \
    -d '{
            "name": "Moderators",
            "ACL": {
                "*": { "read": true }
            },
            "roles": {
                "__op": "AddRelation",
                "objects": [
                    {
                        "__type": "Pointer",
                        "className": "Role",
                        "_id": "165632938928343"
                    }
                ]
            },

            "users": {
                "__op": "AddRelation",
                "objects": [
                    {
                        "__type": "Pointer",
                        "className": "_User",
                        "_id": "165632938928345"
                    }
                ]
            }
        }' \
    https://api.backtory.com/object-storage/roles

زمانی که ساخت نقش موفقیت آمیز باشد، پاسخی با کد 201 بازگردانده می‌شود که هدری به نام Location دارد. در این هدر آدرس دسترسی به این نقش که جهت به‌روزرسانی، خواندن، و حذف استفاده می‌شود قرار دارد. در بدنه پاسخ یک شیء JSON قرار دارد که فیلدهای id_ و createdAt دارد:

{ 
    "createdAt": "2012-04-28T17:41:09.106Z", 
    "_id": "165632938928347" 
}

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

برای گرفتن لیست تمامی نقش‌ها می‌توان درخواست GET ای را به صورت زیر فرستاد:

curl -X GET \ 
    --header 'Authorization: Bearer <user-access-token>' \
    --header 'X-Backtory-Object-Storage-Id: <object-storage-Id>' \
    --header "Content-Type: application/json" \
    https://api.backtory.com/object-storage/roles

پاسخ یک آرایه از اشیای JSON است که شامل لیست تمامی نقش‌های موجود در پروژه است:

[
    { 
        "createdAt": "2012-04-28T17:41:09.106Z", 
        "_id": "165632938928347", 
        "updatedAt": "2012-04-28T17:41:09.106Z", 
        "ACL": { 
            "*": { "read": true }, 
            "role:Administrators": { "write": true } 
        }, 
        "name": "Moderators" 
    },
    { 
        "createdAt": "2012-04-28T17:41:09.106Z", 
        "_id": "165632456788347", 
        "updatedAt": "2014-04-28T17:41:09.106Z", 
        "ACL": { 
            "*": { "write": true } 
        }, 
        "name": "Writers" 
    }
]

به دست آوردن جزئیات یک نقش

می‌توانید جزئیات یک نقش را با ارسال یک درخواست GET به url ای به دست آورید که در پاسخ ساخت نقش در هدر Location آمده است:

curl -X GET \ 
    --header 'Authorization: Bearer <user-access-token>' \
    --header 'X-Backtory-Object-Storage-Id: <object-storage-Id>' \
    --header "Content-Type: application/json" \
    https://api.backtory.com/object-storage/roles/165632938928347

پاسخ یک شیء JSON است که شامل تمامی فیلد های نقش است:

{ 
    "createdAt": "2012-04-28T17:41:09.106Z", 
    "_id": "165632938928347", 
    "updatedAt": "2012-04-28T17:41:09.106Z", 
    "ACL": { 
        "*": { "read": true }, 
        "role:Administrators": { "write": true } 
    }, 
    "name": "Moderators" 
}

پاسخ دریافت شده نشان می‌دهد که کاربران دارای نقش Moderators همگی حق خواندن اطلاعات را دارند، اما تنها آن‌هایی که نقش Administrators دارند، می‌توانند داده‌ها را تغییر دهند.

به روز رسانی نقش‌ها

به‌روزرسانی یک نقش به صورت کلی مانند به‌روزرسانی هر سطر دیگری است، با این تفاوت که مقدار ستون name را نمی‌توان تغییر داد. اضافه کردن و حذف کاربران و نقش‌ها با استفاده از اپراتورهای AddRelation و RemoveRelation انجام می‌شود. برای مثال می‌توان دو کاربر را به نقش مدیران اضافه کرد:

curl -X PUT \
    --header 'Authorization: Bearer <user-access-token>' \
    --header 'X-Backtory-Object-Storage-Id: <object-storage-Id>' \
    --header "Content-Type: application/json" \
    -d '{
            "users": {
                "__op": "AddRelation",
                "objects": [
                    {
                        "__type": "Pointer",
                        "className": "_User",
                        "_id": "165632938928343"
                    },
                    {
                        "__type": "Pointer",
                        "className": "_User",
                        "_id": "165632938928345"
                    }
                ]
            }
        }' \
    https://api.backtory.com/object-storage/roles/165632938928347

به همین ترتیب می‌توان یک نقش فرزند را از زیرنقش‌های مدیر حذف کرد:

curl -X PUT \
    --header 'Authorization: Bearer <user-access-token>' \
    --header 'X-Backtory-Object-Storage-Id: <object-storage-Id>' \
    --header "Content-Type: application/json" \
    -d '{
            "roles": { 
                "__op": "RemoveRelation", 
                "objects": [ 
                    {
                        "__type": "Pointer",
                        "className": "Role",
                        "_id": "165632938928349"
                    }
                ] 
            }
        }' \
    https://api.backtory.com/object-storage/roles/165632938928347

حذف نقش‌ها

برای حذف یک نقش از دیتابیس، کافی است که یک درخواست DELETE ارسال شود. به عنوان مثال:

curl -X DELETE  \
    --header 'Authorization: Bearer <user-access-token>' \
    --header 'X-Backtory-Object-Storage-Id: <object-storage-Id>' \
    --header "Content-Type: application/json" \
    https://api.backtory.com/object-storage/roles/165632938928347

در تمامی عملیات‌های بر روی نقش‌ها ابتدا امکان انجام آن عملیات بوسیله کاربر فرستنده درخواست بررسی می‌شود. این بررسی از طریق ACL مربوط به نقش آن کاربر انجام می‌شود، مگر آن‌که توکن ارسالی توکن master باشد.

امنیت

زمانی که یک کاربر ساده به بکتوری دسترسی پیدا می‌کنید، دسترسی او می‌تواند بوسیله ACL محدود شود. شما می‌توانید مقادیر ACL را با استفاده از سرویس REST و دسترسی به کلید ACL یک نقش خوانده و تغییر دهید. علاوه بر اختیارات برای هر کاربر، میتوان اختیارات سطح نقش را برای شیء‌های بکتوری تعریف کرد. شما می‌توانید به جای اینکه id_ نقش یا کاربر را به عنوان کلید اختیاردهی در نظر بگیرید، از نام نقش به همراه پیش آیند :role به عنوان کلید دسترسی شیء استفاده کنید. می‌توان از اختیارات سطح نقش به همراه اختیارات سطح کاربر برای کنترل دسترسی‌های کاربران استفاده کرد.

به عنوان مثال برای اینکه یک شیء برای کاربران دارای نقش Members قابل خواندن باشد و برای سازنده شیء که id_ برابر با 8TOXdXf3tz دارد و هر کس دیگری در نقش Modereator قابل نوشتن باشد، ACL ای به شکل زیر باید تعریف شود:

{ 
    "8TOXdXf3tz": { "write": true }, 
    "role:Members": { "read": true }, 
    "role:Moderators": { "write": true }
}

در این مثال فرض شده است که نقش Moderators فرزند نقش Members است و بنابراین دیگر نیازی به تعریف اختیارات read برای کاربر یا نقش Moderators نیست چرا که آنها از قبل اختیارات داده شده به نقش Members را گرفته‌اند.

سلسله مراتب نقش‌ها

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

این نوع از ارتباطات معمولا در app‌هایی با محتوای کاربران مانند فروم‌ها معنی دارد. زیرمجموعه کوچکی از کاربران، Admin‌ها هستند که بالاترین سطح دسترسی به تنظیمات app را مانند ساخت فروم‌های تازه، تنظیمات پیام‌های سراسری و غیره دارند. گروه دیگری از کاربران moderatorها هستند که مسئول این هستند که محتوای تولید شده بوسیله کاربران درست باشد. هر کاربری با اختیارات ادمین اختیارات مدیر را نیز دارد. برای ایجاد چنین رابطه ای، نقش ادمین فرزند نقش مدیر میشود، با اضافه کردن نقش ادمین به رابطه roles در شیء مدیر. مانند آنچه در ادامه می آید:

curl -X PUT \
    --header 'Authorization: Bearer <user-access-token>' \
    --header 'X-Backtory-Object-Storage-Id: <object-storage-Id>' \
    --header "Content-Type: application/json" \ 
    -d '{
            "roles": 
            { 
                "__op": "AddRelation", 
                "objects": [ 
                    {
                        "__type":"Pointer",
                        "className": "_Role",
                        "_id": "<AdministratorsRoleObjectId>"
                    }
                ] 
            }
        }' \ 
    https://api.backtory.com/object-storage/roles/<ModeratorsRoleObjectId>