Permishout: بازآفرینی توییتر با کنترل دسترسی با استفاده از Permit.io

این یک ارسال برای مجوز است. چالش مجوز: مجوزها دوباره تعریف شده اند
اگر می خواهید بلافاصله راه اندازی کنید. دستورالعمل ها را در https://github.com/ansellmaximilian/permishout دنبال کنید
آنچه من ساختم
برای این چالش ، من تصمیم گرفتم Twitter/X را با کنترل دسترسی کامل کنم. به آن Permishout گفته می شود. جایی که a Shout
مثل یک است Tweet
یا پست من فکر کردم که این امر برای نشان دادن مجوزها مناسب است.
در اینجا گردش کار برنامه وجود دارد:
صفحه اصلی
ورود به سیستم/ثبت نام
نمایه کامل
فریاد ایجاد
به پروفایل نگاه کنید
دنبال کردن/ناپدید شدن
پاسخ به و حذف فریادها
همانطور که می بینید برخی از فریادها متفاوت از سایرین هستند. برخی از آنها یک دکمه حذف دارند – به این دلیل که Permit.io مشخص کرده است که کاربر فعلی مجوز انجام این کار را دارد.
برخی از دکمه های پاسخ غیرفعال هستند و برخی دیگر نیستند. این همچنین مجوز است. در اینجا شرایطی وجود دارد که برای کاربر تنظیم شده است تا بتواند پاسخ دهد:
- اگر حالت پاسخ فریاد روی “همه” تنظیم شده است ، هر کسی می تواند پاسخ دهد
- اگر حالت پاسخ فریاد روی “فقط حساب های تأیید شده” تنظیم شده است ، فقط “سرپرستان” می توانند پاسخ دهند
- اگر حالت پاسخ فریاد به “افراد ذکر شده” تنظیم شده باشد ، فقط افراد ذکر شده قادر به پاسخگویی خواهند بود
- هرکسی که کاربر دنبال می کند می تواند پاسخ دهد
نسخه آزمایشی
https://www.youtube.com/watch؟v=cmgdf1q73yo
repo پروژه
repo را اینجا ببینید
سفر من
باید بگویم یادگیری همه مباحث لازم در مورد Permit.io کار ساده ای نبود. این قطعاً قوی و پر از ویژگی هایی است که از یک کتابخانه کنترل دسترسی انتظار دارید.
اما می گوید که یکی از ناامید کننده ترین اما جذاب ترین بخش یادگیری در مورد ادغام مجوز است. در قسمت جلوی با استفاده از permit-fe-sdk
بشر
من سرانجام آن را به کار خود کردم و قطعاً یکی از قسمت های مورد علاقه من در مجوز است. و من یک ارائه دهنده زمینه قابل استفاده مجدد سفارشی از این مورد برای استفاده برای پروژه های آینده با استفاده از Permit.io دریافت کردم. ارائه دهنده زمینه اجازه سفارشی
به طور کلی ، این اولین بار بود که من از یک سرویس کنترل دسترسی شخص ثالث و از آنچه من دیده ام بسیار قوی و کامل است.
در ذهن من آن را با سهولت کنترل/مجوز دسترسی در لاراول مقایسه می کنم. که عالی است! این یکی از اصلی ترین دلایلی است که من عاشق استفاده از لاراول هستم حتی اگر NextJS چارچوب مورد علاقه من باشد.
با استفاده از مجوز. برای مجوز
راهی که من قصد ساخت این بخش را دارم ، با طی کردن توسعه است که گویی من از ابتدا مجاز هستم. به شما اطلاع می دهد که هنگام توسعه با مجوز ، چه تصمیماتی را باید گرفته شود. به این ترتیب ، شما قادر خواهید بود هنگام تهیه برنامه های شخصی خود با Permit.io ، سفر خود را به تنهایی انجام دهید.
در اینجا دسته ای از مواردی وجود دارد که می خواهم آن را برجسته کنم:
- برنامه ریزی و تنظیم
- همگام سازی کاربران
- انجام چک در پس زمینه
- با استفاده از
permit-fe-sdk
برای نمایش آسان چیزها بر اساس کنترل دسترسی
برنامه ریزی و تنظیم
داستانهای کاربر
بنابراین ابتدا می خواهم داستانهای کاربر را که برای این برنامه می خواهم شناسایی کنم:
- احراز هویت کاربر
- کاربران معتبر دارند
profile
- همه کاربران معتبر قادر خواهند بود
create
هیچshout
و همچنین آن را مشاهده کنید - در
shouter
(پوستر/نویسنده) ازshout
قادر خواهد بودdelete
وتreply
به آن - کاربران با نقش
admin
قادر خواهد بودdelete
هیچshout
- کاربران معتبر می توانند یکدیگر را دنبال و یا از بین ببرند.
- در توییتر/X می توانید تنظیم کنید که کاربران قادر به پاسخگویی به توییت/پست شما هستند. من می خواهم این ویژگی را تقلید کنم ، زیرا بسیاری از قابلیت های موجود در مجوز را نشان می دهد. در اینجا نحوه برخورد توییتر/X آن را آورده است:
- همه: همه می توانند به یک پاسخ دهند
shout
- مردم ذکر کردند: فقط مردم
mentioned
در الفshout
قوطیreply
بشر - افرادی که دنبال می کنید: فقط افرادی که دنبال می کنید می توانند
reply
بشر با این حال برای نشان دادن “مشتقات نقش” ، من می خواهم آن را بسازم تا افرادی که دنبال می کنید همیشه به هر یک از شما پاسخ دهندshout
s. - فقط کاربران تأیید شده: برای مجاز ، این افراد با افراد همراه خواهند بود
admin
نقش ها
- همه: همه می توانند به یک پاسخ دهند
شناسایی مؤلفه ها
اکنون که داستانهای کاربر خود را داریم ، می توانیم مجوز را شروع کنیم. مؤلفه های مورد نیاز ما.
این چیزی است که من کردم:
- نقش ها:
-
admin
-
منابع:
-
profile
این منبع فقط یک نقطه لنگر بسیاری از کارکردهای من است که من قصد اجرای آن را دارم. قرار نیست هیچ ویژگی را در خود جای دهد ، اما به من کمک می کند تا پیروان/زیر و همچنین مشتقات نقش را شناسایی کنم. این بعداً توضیح داده خواهد شد. -
shout
-
نقش نمونه منابع:
فقط توضیحات کمی در مورد نقش های نمونه وجود دارد: مدیر می تواند نقش سطح بالایی باشد ، جایی که مجوزهای مربوط به آن برای هر نمونه از یک منبع اعمال می شود. بنابراین اگر یک مدیر داشته باشدdelete
دسترسی به ashout
، آن را در هر یک خواهد داشت.
با این حال ، یک نقش نمونه فقط در مورد یک نمونه واحد از یک نوع منبع اعمال می شود. در اینجا چگونه اجازه می دهد. resource:resource_key#role
، به معنای اقدامات موجود در اختیار صاحب نقش فقط برای منابع با کلید اعمال می شود resource_key
بشر
در اینجا نقشهایی که من شناسایی کرده ام آورده شده است:
-
profile#owner
فقط صاحب یک پروفایل. درست مانند توییتر قبل از اینکه صدای جیر جیر داشته باشید ، برخی از داده ها را تکمیل کرده اید.
profile#follower
profile#followed
-
shout#shouter
: قوطیreply
وتdelete
یک فریاد -
shout#replier
: قوطیreply
بهshout
این نقش مهمی خواهد بود. از آنجا که من می خواهم به افرادی که از آنها پیروی می کنید اجازه دهم این توانایی را داشته باشندreply
بشر من این را از این نقش استخراج خواهم کردprofile#followed
بشر shout#mentioned
تنظیم کردن
مجموعه ای از روش های تنظیم این کار وجود دارد. در README.md
در مورد repo من آن را کاملاً توضیح می دهد ، اما من در اینجا مختصر خواهم بود و فقط مواردی را توضیح می دهم که در ابتدا درک آن دشوار است. ما از این استفاده خواهیم کرد permit
شی زیر به یاد داشته باشید که از این در قسمت جلوی استفاده نکنید.
import { Permit } from "permitio";
const permit = new Permit({
token: process.env.PERMIT_SDK_KEY,
pdp: process.env.PERMIT_PDP_URL,
apiUrl: process.env.PERMIT_API_URL || "https://api.permit.io",
});
export default permit;
در اینجا آمده است که چگونه می توانید به صورت برنامه ای ایجاد کنید admin
نقش:
await permit.api.createRole({
key: "admin",
name: "Admin",
permissions: ["shout:delete"],
});
فقط یک یادداشت ، در اینجا نحو مجوزها وجود دارد: resource#action
بشر این اساساً می گوید ، “سلام ، نقش مدیر را ایجاد کنید ، که قادر به حذف فریادها خواهد بود. از آنجا که این یک نقش سطح بالا است ، در مورد همه موارد فریادها اعمال می شود.
در اینجا نحوه ایجاد یک shout
منبع ، همراه با نقش های موجود در دسترس خود:
await permit.api.createResource({
key: "shout",
name: "shout",
actions: {
delete: {},
reply: {},
},
// Resource roles are used to define the permissions for each role on the resource
roles: {
shouter: {
name: "Shouter",
permissions: ["delete", "reply"],
},
replier: {
name: "Replier",
permissions: ["read", "reply"],
},
mentioned: {
name: "Mentioned",
permissions: ["read", "reply"],
},
},
همانطور که می بینید من 3 نقش منحصر به فرد را در اینجا ایجاد کرده ام. حتی اگر آنها همان مجوزها را داشته باشند ، من تصمیم گرفتم که آنها را جدا کنم فقط در صورت نیاز به اضافه کردن اقدامات بیشتر. اساساً این کد می گوید “سلام ، من می خواهم منبعی به نام” فریاد “ایجاد کنم. این می تواند 2 عمل روی آن انجام شود: حذف و پاسخ دهید. یک کاربر می تواند یک جایگزین ، ذکر شده و یک شورت باشد. هر 3 نفر از آنها قادر به انجام تمام اقدامات موجود خواهند بود.
در اینجا نحوه ایجاد رابطه بین منابع آورده شده است. این بسیار مهم است مشتقات نقش بسیار قدرتمند هستند و روابط برای آن لازم است.
await permit.api.resourceRelations.create("shout", {
key: "parent",
name: "Parent",
subject_resource: "profile",
});
قطعه فوق اساساً می گوید ، “سلام ، رابطه ای برای فریاد ایجاد کنید. موضوع این رابطه مشخصات است. من می خواهم پروفایل ها والدین فریاد باشند.”
من در ابتدا از این موضوع گیج شدم ، بنابراین سعی می کنم فقط در مورد شما توضیح دهم. در اصل ، در ذهن خود تصور کنید که می خواهید چه ارتباطی بین دو منبع داشته باشید.
در اینجا ما داریم shout
وت profile
بشر ما می خواهیم این موارد را به گونه ای وصل کنیم که a profile
موارد زیادی از shout
بشر بیایید با “پروفایل والدین فریادها” برویم.
subject_resource
استname
ازfirst parameter of create
در بالا نحوه ترجمه آن به پارامترها است.
ناسازگار:
name
پارامتر می تواند هر چیزی باشد. حتی لازم نیست که واقعاً رابطه را توصیف کند (اما باید). بنابراین می توانستم قرار دهمowns
به عنوانname
پارامتر دقیقاً همان.
سرانجام ، ایجاد مشتقات منابع. من می خواهم هر کسی که از یک کاربر پیروی می کند بتواند به همه آنها پاسخ دهد shouts
بشر بنابراین ، بیایید مشتق شویم profile#followed
به shout#replier
بشر
ناسازگار: روابط برای روابط مشتق یکپارچه است. قانون فوق که می خواهم پیاده سازی کنم به این معنی است که کاربران دارای این کار هستند
shout#replier
نقش فقط در صورتی که داشته باشدprofile#followed
در یک پروفایل و اگر آن پروفایل والدین استshout
در سوال
در اینجا نحوه ایجاد آن مشتق در کد آمده است:
await permit.api.resourceRoles.update("shout", "replier", {
granted_to: {
users_with_role: [
{
linked_by_relation: "parent",
on_resource: "profile",
role: "followed",
},
],
},
});
بسیار ساده این اساساً می گوید ، “سلام ، این نقش فریاد#جایگزین را که قبلاً ایجاد کرده اید به روز کنید و این نقش را با نقش زیر در یک پروفایل به کاربران اعطا کنید.
همگام سازی کاربران
بدیهی است که اجازه می دهد. احراز هویت شما را مدیریت نمی کند. شما به شخص ثالث خود یا یک شخص خود نیاز خواهید داشت. مثل منشی با این حال ، این بدان معنی نیست که می توانید از مدیریت کاربران در مجوز غفلت کنید.
permit.io به طور خودکار کاربران خود را از سیستم تأیید اعتبار شما نمی شناسد. بیایید به عنوان مثال از Clerk استفاده کنیم زیرا این کتابخانه ای است که من در برنامه خود استفاده می کنم.
بعد از اینکه کاربر خود را در منشی ایجاد کردید. شما همچنین باید آنها را به صورت مجوز ایجاد کنید. شما می توانید این کار را در منطق ورود به سیستم/ثبت نام خود انجام دهید ، یا می توانید یک فرآیند جداگانه را به طور کامل ایجاد کنید. شما می دانید که چگونه در توییتر/X ، پس از ثبت نام با ایمیل یا حساب Google ، هنوز هم باید مواردی مانند نام کاربری ، نام و غیره را پر کنید؟ من قصد دارم از این فرصت استفاده کنم تا کاربران خود را همگام سازی کنم.
بنابراین ، اگر یک کاربر با استفاده از منشی وارد سیستم شده است ، و ورود به مجوز وجود ندارد ، من قصد دارم آنها را مجبور کنم مشخصات خود را تکمیل کنند.
میان افزار در NextJs
export default clerkMiddleware(async (auth, req) => {
const userId = (await auth()).userId;
const { origin, pathname } = req.nextUrl;
if (isProtectedRoute(req)) await auth.protect();
// get project_id and environment_id
const { project_id, environment_id } = await permitApi
.get("/v2/api-key/scope")
.then((res) => res.data);
// check if user's profile is complete (based on their existence in Permit)
let isProfileComplete = false;
try {
await permitApi.get(
`/v2/facts/${project_id}/${environment_id}/users/${userId}`
);
isProfileComplete = true;
} catch {
isProfileComplete = false;
}
// if it's a public route, the user is signed in and the profile is complete, redirect to home
if (isPublicRoute(req) && isProfileComplete)
return NextResponse.redirect(`${origin}/home`);
// if the user has not completed their profile and is trying to access a protected route, redirect to profile creation page
if (
!isProfileComplete &&
isProtectedRoute(req) &&
!pathname.startsWith("/profile/create")
) {
return NextResponse.redirect(`${origin}/profile/create`);
}
});
در اینجا قطعه ای از میان افزار من وجود دارد. این در بیشتر صفحات مورد نظر شما در Permishout اجرا می شود. اساساً ، آنچه انجام می دهد این است:
- با استفاده از شناسه کاربر در حال حاضر وارد سیستم شوید
(await auth()).userId
- اگر کاربر حتی به منشی ثبت نام یا وارد سیستم نشده باشد ، ما فقط می توانیم از مسیرهای محافظت شده خود محافظت کنیم
await auth.protect()
- اگر کاربر تأیید شده است (توسط منشی) ، ما بررسی می کنیم که آیا آنها دارای یک ورودی تطبیق در مجوز هستند. ما این کار را با استفاده از کد زیر بررسی می کنیم:
// note that `permitApi` is just an instance of `axios`
await permitApi.get(
`/v2/facts/${project_id}/${environment_id}/users/${userId}`
);
- اگر این تماس API موفق شود ، این بدان معنی است که کاربر مطابق است
user
در مجوز. ما خوب هستیم کاربر می تواند به هر مسیرهای محافظت شده و غیر عمومی دسترسی پیدا کند. - اگر تماس API انجام نشود ، این بدان معنی است که کاربر هیچ تطبیق ندارد
user
بشر permit.io در مورد این کاربر نمی داند. اکنون ما کاربر را وادار می کنیم به/profile/create
مسیر. جایی که من آنها را مجبور به ایجاد یک حساب می کنم.
اکنون پس از پر کردن اطلاعات ، عمدتاً نام کاربری ، نام ، کشور و سال متولد شده ، ما اداره می کنیم
همگام سازی کاربران
در پس زمینه یا یک کنترل کننده مسیر همانطور که از NextJS استفاده می کنیم.
const POST = async (request: NextRequest) => {
const { userId } = getAuth(request) || "";
const user = await clerkClient.users.getUser(userId || "");
const { username, name, yearBorn, country } = await request.json();
const { firstName, lastName } = splitName(name);
await clerkClient.users.updateUser(userId || "", {
firstName,
lastName,
});
const { key: createdUser } = await permit.api.syncUser({
key: userId || "",
first_name: firstName,
last_name: lastName,
email: user?.emailAddresses[0].emailAddress || "",
attributes: {
username,
yearBorn: parseInt(yearBorn),
country,
},
});
await permit.api.roleAssignments.assign({
user: createdUser,
role: "owner",
resource_instance: `profile:profile_${createdUser}`,
tenant: "default",
});
return NextResponse.json({
success: true,
});
};
من کمی توضیح می دهم آنچه در اینجا اتفاق می افتد:
- اول ، ما کاربر معتبر در حال حاضر را از Clerk دریافت می کنیم (یادآوری کنید که این یک کاربر از Permit.io نیست – این همان چیزی است که ما در اینجا هستیم!)
- سپس ما تمام ویژگی های اضافی را که می خواهیم در مجوز قرار دهیم دریافت می کنیم.
- سپس-و اجازه. این کار را برای ما بسیار آسان می کند-ما این کار را می کنیم
permit.api.syncUser
بشر به یاد داشته باشید که با کلید کاربر برای permit.io مطابقت داشته باشیدid
از کاربر منشی شما شما لزوماً مجبور نیستید با آن مطابقت داشته باشید ، اما اگر این کار را نکنید ، بی نهایت سخت تر خواهد بود. به عنوان مثال ، اگر تصمیم دارید فقط از 10 کاراکتر اول منشی خود استفاده کنیدid
، شما باید هر بار که به کاربر مجوز خود مراجعه کنید ، 10 مورد اول را بدست آورید.
همچنین می توانید اضافه کنید email
با first_name
وت last_name
بشر این همچنین فرصت شما برای افزودن ویژگی های اضافی خواهد بود.
توجه: من گفتم Permit.io این کار را بسیار آسان می کند زیرا لازم نیست بین یک کاربر موجود و یکی از آنها که ایجاد نشده است ، تمایز قائل شوید.
syncUser
رسیدگی خواهد کرد اگر وجود داشته باشد ، آن را بازنویسی می کند (آن را همگام سازی می کند) ، و اگر این کار را نکند ، آن را ایجاد می کند.
- حالا ، از آنجا که من
profile
منابع تنظیم شده من همچنین می خواهم هر کاربر را با یک کاربر مرتبط کنم. بنابراین این جایی است که این خط برای:
await permit.api.roleAssignments.assign({
user: createdUser,
role: "owner",
resource_instance: `profile:profile_${createdUser}`,
tenant: "default",
});
آنچه این کد دو برابر می کند. اول ، این یک نمونه منبع ایجاد می کند profile
با کلید profile_${created_user_key}
بشر نکته مهمی که باید بخاطر بسپارید این است resource_instance
باید یک رشته در قالب باشد resource:instance_key
بشر
ثانیا ، این کد همچنین نقش را به کاربر اختصاص می دهد createdUser
کلید و نقش را تعیین می کند owner
به آن ( profile
نمونه)
بنابراین ، اکنون ، این کاربر خاص به طور رسمی صاحب پروفایل خود و تمام کنترل دسترسی که با آن نقش نمونه در آن پروفایل خاص و همچنین هرگونه مشتق نقش همراه است ، است.
من فقط می خواستم این را ذکر کنم زیرا ایجاد تکلیف نقش و نمونه ایجاد شده در یک عملکرد واقعاً مرتب است!
انجام چک
نکته مهمی که باید به آن توجه داشته باشید این است که همیشه باید چک ها انجام شود ، همانطور که در شما نیاز به پرس و جو از آنها دارید. permit.io در مورد کد شما و آنچه شما انجام می دهید نمی داند ، آنها فقط نوشتن کد را برای بررسی آسان برای مجوزها انجام می دهند. در اینجا یک مثال بررسی شده است که من هر زمان که کاربر در حال حذف است ، انجام می دهم shout
بشر
const isUserAllowedToDelete = await permit.check(userId || "", "delete", {
type: "shout",
key: shout.key,
});
واقعاً ساده نیست؟ به جای بررسی چندین سناریو که در آن به کاربر اجازه داده می شود یک فریاد خاص را حذف کند ، ما فقط از مجوز استفاده می کنیم. در اینجا ، باز خواهد گشت true
اگر کاربر یک باشد admin
یا اگر کاربر باشد owner
از shout
بشر
این دو شرط ساده است. اما تصور کنید اگر چندین مورد منحصر به فرد دارید ، باید پیگیری کنید. تصور کنید که آیا کاربر می تواند یک مجری سطح پایین برای حذف باشد. شما باید آن چک را بنویسید و آنها را به تمام قسمت های کد خود که نیاز به چک دارند اضافه کنید.
به هر حال ، اگر isUserAllowedToDelete
است ، true
، ما با استفاده از این موارد ، نمونه را حذف می کنیم:
await permit.api.resourceInstances.delete(`shout:${shout.key}`);
آسان!
با استفاده از permit-fe-sdk
این قسمت مورد علاقه من است. صادقانه می فهمید که چگونه کل کار بسیار عالی است! در اصل ، این کتابخانه به شما کمک می کند تا در قسمت جلویی تعیین کنید که آیا کاربر مجاز به انجام کاری است یا خیر.
مفهوم مشابهی در دستورالعمل های تیغه لاراول وجود دارد:
@can('update', $post)
@endcan
هنگامی که permit.io با CASL کار می کند ، می توانید به همان عملکرد دست یابید. اما این بار این همه در جبهه اتفاق می افتد – با خیال راحت!
در اینجا مراحل است. درک و فهمیدن آنها برای من واقعاً سخت بود ، بنابراین امیدوارم بتوانم آن را به خوبی توضیح دهم:
- بسته های لازم را نصب کنید
npm install @casl/ability @casl/react permit-fe-sdk permitio
- یک کنترل کننده مسیر پست ایجاد کنید که در آن
permit-fe-sdk
خواستار بررسی مجوز می شود.
بر اساس تحقیقات خودم ، اگر می خواهید مجوزهای فله ای دریافت کنید ، می خواهید مسیرهای پست ایجاد کنید. به این معنی ، شما به طور هم زمان مجوزهای زیادی را بررسی می کنید.
اینجا مال من است (من روی مجوزهای فله ای تمرکز خواهم کرد):
export async function POST(req: NextRequest) {
try {
const { searchParams } = req.nextUrl;
const userId = searchParams.get("user");
const { resourcesAndActions } = await req.json();
if (!userId) {
return NextResponse.json(
{ error: "No userId provided." },
{ status: 400 }
);
}
const checkPermissions = async (resourceAndAction: {
resource: string;
action: string;
userAttributes?: PermishoutUserAttributes;
resourceAttributes?: Record<string, any>;
}) => {
const { resource, action, userAttributes, resourceAttributes } =
resourceAndAction;
const [resourceType, resourceKey] = resource.split(":");
return permit.check(
{
key: userId,
attributes: userAttributes,
},
action,
{
type: resourceType,
key: resourceKey,
attributes: resourceAttributes,
tenant: "default",
}
);
};
const permittedList = await Promise.all(
resourcesAndActions.map(checkPermissions)
);
return NextResponse.json({ permittedList }, { status: 200 });
} catch (error) {
console.error("Permission check error:", error);
return NextResponse.json(
{ error: "Internal Server Error" },
{ status: 500 }
);
}
}
به نظر من ذکر هدف نهایی این مسیر مفید است. این اساساً برای بازگشت یک شیء است permittedList
به عنوان مجموعه ای از بول ها. اکنون به راحتی می توانید لیستی از 100 را برگردانید true
S و شما می خواهید همه چک را پشت سر بگذارید. اما این چیزی نیست که ما در اینجا درست هستیم؟
کمی بعد به جبهه خواهیم رسید ، اما اساساً می خواهیم نقشه برداری کنیم resourcesAndActions
به نتایج اجازه. این متغیر مجموعه ای از منابع و اقدامات است که از جبهه پرس و جو شده است.
هر عنصر آرایه ای خواهد داشت resouce
با action
با userAttributes
وت resourceAttributes
بشر شما می خواهید به طور دقیق برای این مجوزها آزمایش کنید.
مهم: توجه داشته باشید که من کلید منبع و منبع را تقسیم می کنم؟ این امر به این دلیل است که شما آنها را از جلوی این قالب تهیه می کنید
resource:resource_key
بشر و شما آن را در این مسیر همانطور که هست دریافت می کنید ، بنابراین باید آن را به صورت دستی تقسیم کنید و سپس منبع را در آن قرار دهیدtype
و کلید درkey
بشر این برای مدتی من را گرفت.
خوب ، بر اساس تمام اقدامات و منابعی که انجام می دهیم permit.check
بنابراین آرایه بولی را به ترتیب صحیح بازگرداند.
مهم: به یاد داشته باشید که سفارش بسیار مهم است. شما فقط نمی توانید به طور تصادفی انجام دهید
permitionList.reverse()
بشر جبهه دیگر در تعیین مجوزها دیگر دقیق نخواهد بود.
خوب ، بنابراین ما آن لیست از Booleans را برمی گردانیم. حالا چی؟
- جلوی آن را تنظیم کنید تا بتوانید از آن مسیر استفاده کنید
const defaultActionResources: ActionResourceSchema[] = [
{
action: "delete",
resource: "shout",
},
];
export const AbilityContext = createContext<
| {
ability: Ability<AbilityTuple, MongoQuery>;
setActionResources: React.Dispatch<
React.SetStateAction<ActionResourceSchema[]>
>;
}
| undefined
>(undefined);
export const useAbility = () => {
const ability = useContext(AbilityContext);
if (!ability) {
throw new Error("useAbility must be used within an AbilityProvider");
}
return ability;
};
export const AbilityLoader = ({ children }: { children: ReactNode }) => {
const { isSignedIn, user } = useUser();
const [ability, setAbility] = useState<Ability<AbilityTuple, MongoQuery>>(
new Ability()
);
const [actionResources, setActionResources] = useState<
ActionResourceSchema[]
>(defaultActionResources);
useEffect(() => {
(async () => {
// reset it first
setAbility(new Ability());
if (isSignedIn) {
const permit = Permit({
loggedInUser: user.id,
backendUrl: "/api/permit/check",
});
permit?.reset();
const allActionResources = [
...actionResources,
...defaultActionResources,
];
await permit.loadLocalStateBulk(allActionResources);
const caslConfig = permitState.getCaslJson();
const caslAbility =
caslConfig && caslConfig.length
? new Ability(caslConfig)
: new Ability();
setAbility(caslAbility);
}
})();
}, [isSignedIn, user, actionResources]);
return (
<AbilityContext.Provider value={{ ability, setActionResources }}>
{children}
</AbilityContext.Provider>
);
};
اساساً ما می خواهیم یک Ability
شیء در کل برنامه. من استفاده می کنم Context.Provider
برای این
اقدامات و منابع از مسیر را به خاطر می آورید؟ این نقشه ها به طور مستقیم به موارد موجود در این ارائه دهنده زمینه می پردازد.
مهم: چک که شما قادر خواهید بود به طور مستقیم با آنچه تهیه می کنید مطابقت داشته باشد
await permit.loadLocalStateBulk(allActionResources)
بشر اگر آن را با یک جفت منبع و عمل تهیه کنید ، قادر به بررسی دقیق دیگران نخواهد بود.
من یک کشور محلی به نام ایجاد کرده ام actionResources
، که باعث می شود useEffect
برای به روزرسانی و بارگذاری دولت محلی با آخرین مجوز.
من مجوزهای پیش فرض دارم که همیشه می خواهم بررسی کنم: مجوزهای مدیر. به همین دلیل من منابع اقدام پیش فرض دارم.
مهم: حتماً آن را مجدداً تنظیم کنید. اگر فقط تماس بگیرم مشکل پیدا کردم
loadLocalStateBulk
بارها و بارها
همین است! فقط مطمئن شوید که ability
متغیر با جدیدترین مجوزها. اکنون می توانید استفاده کنید useAbility
برای به دست آوردن این توانایی و انجام چک ها.
- در واقع با استفاده از آن در جلوی ، بیایید روی مجوزهای فریاد تمرکز کنیم. مدیر همیشه قادر خواهد بود
delete
shout
بنابراین بیایید آن را در آرایه پیش فرض قرار دهیم.
اکنون ، فریادها نیاز به کنترل ریز و درشت دارند زیرا نقش ها و مجوزهای منحصر به فرد برای نمونه های منفرد وجود دارد shout
بشر
در اینجا نحوه برخورد با آن آمده است. به یاد داشته باشید که من در معرض دیدم setActionResources
در آن ارائه دهنده و این که اگر آن آرایه به روز شود ، مجوزهای محلی را به روز می کند. ما این همان کاری است که من می خواهم در آن انجام دهم home
صفحه
const { setActionResources } = useAbility();
useEffect(() => {
const shoutActions: ActionResourceSchema[] = shouts.flatMap((shout) =>
[
{ action: "reply", resource: `shout:${shout.key}` },
{ action: "delete", resource: `shout:${shout.key}` },
]);
setActionResources(shoutActions);
}, [shouts, setActionResources]);
این است با این کار تمام مجوزهای ما در جبهه به روز می شود تا بتوانیم از آنها استفاده کنیم. در هر shout
من می خواهم یک دکمه حذف برای کاربرانی که اجازه دارند داشته باشم. من می توانم از Can
مؤلفه CASL:
import { Can } from "@casl/react";
<Can I="delete" a={`shout:${shout.key}`} ability={ability}>
<Button
style={{ padding: 0 }}
variant="ghost"
className="flex items-center gap-1 text-red-400 hover:text-red-500 ml-auto"
onClick={(e) => {
e.stopPropagation();
if (setShoutToDeleteKey) setShoutToDeleteKey(shout.key);
}}
>
<Trash size={16} />
<span>Delete</span>
</Button>
</Can>
این است این تمام کاری است که من باید انجام دهم. فقط آن را با عمل و منبع و همچنین تأمین کنید ability
شیء که ذکر کردم
همچنین می توانید یک چک خطی استاندارد را مانند این انجام دهید:
import { permitState } from "permit-fe-sdk";
const canReply =
shout.replyMode === ShoutReplyType.EVERYONE ||
permitState?.check("reply", `shout:${shout.key}`, {});
پایان
با عرض پوزش برای مماس من بسیار هیجان زده شدم که این کار را به خودی خود انجام دهم ، به خصوص جلوی SDK. امیدوارم که آنها روی آن کار کنند زیرا من عاشق دستورالعمل های لاراول تیغه هستم. و این دقیقاً همان است React
!