مهاجرت از سرویس کاربران App Engine به پلتفرم Cloud Identity (ماژول 21)

۱. مرور کلی

مجموعه آزمایشگاه‌های کد Serverless Migration Station (آموزش‌های عملی و خودآموز) و ویدیوهای مرتبط با آن ، با هدف کمک به توسعه‌دهندگان Google Cloud serverless برای مدرن‌سازی برنامه‌هایشان، با راهنمایی آنها در طول یک یا چند مهاجرت، و در درجه اول دور شدن از سرویس‌های قدیمی، ارائه می‌شوند. انجام این کار، برنامه‌های شما را قابل حمل‌تر می‌کند و گزینه‌ها و انعطاف‌پذیری بیشتری به شما می‌دهد و شما را قادر می‌سازد تا با طیف وسیع‌تری از محصولات Cloud ادغام شده و به آنها دسترسی داشته باشید و به راحتی به نسخه‌های جدیدتر زبان ارتقا دهید. در حالی که در ابتدا بر روی اولین کاربران Cloud، در درجه اول توسعه‌دهندگان App Engine (محیط استاندارد)، تمرکز دارد، این مجموعه به اندازه کافی گسترده است که شامل سایر پلتفرم‌های serverless مانند Cloud Functions و Cloud Run یا در صورت لزوم، هر جای دیگری نیز می‌شود.

هدف از این آزمایشگاه کد، نشان دادن نحوه مهاجرت توسعه‌دهندگان App Engine با پایتون ۲ از API/سرویس کاربران App Engine به پلتفرم هویت ابری (GCIP) است. همچنین یک مهاجرت ضمنی از App Engine NDB به Cloud NDB برای دسترسی به Datastore (که عمدتاً در ماژول مهاجرت ۲ پوشش داده شده است) و همچنین ارتقاء به پایتون ۳ نیز وجود دارد.

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

یاد خواهید گرفت که چگونه

  • استفاده از سرویس App Engine Users را با Cloud Identity Platform جایگزین کنید.
  • جایگزینی استفاده از App Engine NDB با Cloud NDB (همچنین به ماژول ۲ مراجعه کنید)
  • راه‌اندازی ارائه‌دهندگان هویت احراز هویت مختلف با استفاده از Firebase Auth
  • برای دریافت اطلاعات IAM پروژه از API مدیریت منابع ابری استفاده کنید
  • برای دریافت اطلاعات کاربر از Firebase Admin SDK استفاده کنید
  • انتقال برنامه نمونه به پایتون ۳

آنچه نیاز دارید

نظرسنجی

چگونه از این آموزش استفاده خواهید کرد؟

فقط آن را بخوانید آن را بخوانید و تمرین‌ها را انجام دهید

تجربه خود را با پایتون چگونه ارزیابی می‌کنید؟

تازه کار متوسط ماهر

تجربه خود را در استفاده از خدمات ابری گوگل چگونه ارزیابی می‌کنید؟

تازه کار متوسط ماهر

۲. پیشینه

سرویس کاربران App Engine یک سیستم احراز هویت کاربر برای استفاده توسط برنامه‌های App Engine است. این سرویس، Google Sign-In را به عنوان ارائه‌دهنده هویت خود ارائه می‌دهد، لینک‌های ورود و خروج راحت را برای استفاده در برنامه‌ها فراهم می‌کند و از مفهوم کاربران مدیر و قابلیت‌های فقط مدیر پشتیبانی می‌کند. برای بهبود قابلیت حمل برنامه، Google Cloud توصیه می‌کند از سرویس‌های همراه قدیمی App Engine به سرویس‌های مستقل Cloud ، به عنوان مثال، از سرویس کاربران به Cloud Identity Platform و موارد دیگر، مهاجرت کنید.

پلتفرم هویت (Identity Platform) مبتنی بر احراز هویت فایربیس (Firebase Authentication ) است و تعدادی ویژگی سازمانی از جمله احراز هویت چند عاملی، پشتیبانی از OIDC و SAML SSO، چند مستاجری، SLA 99.95٪ و موارد دیگر را اضافه می‌کند. این تفاوت‌ها همچنین در صفحه مقایسه محصول پلتفرم هویت و احراز هویت فایربیس برجسته شده‌اند. هر دو محصول ویژگی‌های بسیار بیشتری نسبت به قابلیت‌های ارائه شده توسط سرویس کاربران دارند.

این آزمایشگاه کد ماژول ۲۱، تغییر احراز هویت کاربر برنامه از سرویس کاربران به ویژگی‌های پلتفرم هویت را نشان می‌دهد که بیشترین شباهت را به عملکرد نشان داده شده در ماژول ۲۰ دارد. ماژول ۲۱ همچنین شامل مهاجرت از App Engine NDB به Cloud NDB برای دسترسی به Datastore است که مهاجرت ماژول ۲ را تکرار می‌کند.

اگرچه کد ماژول ۲۰ به عنوان یک برنامه نمونه پایتون ۲ "تبلیغ" می‌شود، اما خود منبع با پایتون ۲ و ۳ سازگار است و حتی پس از مهاجرت به پلتفرم Identity (و Cloud NDB) در ماژول ۲۱ نیز به همین شکل باقی می‌ماند. امکان استفاده از سرویس Users در حین ارتقا به پایتون ۳ وجود دارد زیرا مهاجرت به پلتفرم Identity اختیاری است. برای یادگیری نحوه ادامه استفاده از سرویس‌های همراه در حین ارتقا به زمان‌های اجرای نسل دوم مانند پایتون ۳، به codelab و ویدیوی ماژول ۱۷ مراجعه کنید.

این آموزش شامل مراحل زیر است:

  1. راه‌اندازی/پیش‌پردازش
  2. پیکربندی را به‌روزرسانی کنید
  3. اصلاح کد برنامه

۳. تنظیمات/پیش‌پردازش

این بخش توضیح می‌دهد که چگونه:

  1. پروژه ابری خود را راه‌اندازی کنید
  2. دریافت برنامه نمونه پایه
  3. (دوباره)استقرار و اعتبارسنجی برنامه پایه
  4. فعال کردن سرویس‌ها/APIهای جدید Google Cloud

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

۱. پروژه راه‌اندازی

اگر آزمایشگاه کد ماژول ۲۰ را تکمیل کرده‌اید، از همان پروژه (و کد) دوباره استفاده کنید. روش دیگر، ایجاد یک پروژه کاملاً جدید یا استفاده مجدد از یک پروژه موجود دیگر است. مطمئن شوید که پروژه دارای یک حساب صورتحساب فعال و یک برنامه App Engine فعال است. شناسه پروژه خود را پیدا کنید و در طول این آزمایشگاه کد آن را دم دست داشته باشید و هر زمان که با متغیر PROJ_ID مواجه شدید، از آن استفاده کنید.

۲. نمونه برنامه پایه را دریافت کنید

یکی از پیش‌نیازها، یک برنامه‌ی ماژول ۲۰ App Engine است که کار کند، بنابراین یا codelab آن را تکمیل کنید (توصیه می‌شود؛ لینک بالا) یا کد ماژول ۲۰ را از مخزن کپی کنید. چه از کد خودتان استفاده کنید و چه از کد ما، اینجا جایی است که ما شروع می‌کنیم ("شروع"). این codelab شما را در طول مهاجرت راهنمایی می‌کند و با کدی که شبیه کد موجود در پوشه‌ی مخزن ماژول ۲۱ است ("پایان")، به پایان می‌رسد.

پوشه مخزن ماژول ۲۰ را کپی کنید. خروجی آن باید مانند خروجی زیر باشد و اگر codelab ماژول ۲۰ را انجام داده باشید، ممکن است یک پوشه lib نیز داشته باشد:

$ ls
README.md               appengine_config.py     templates
app.yaml                main.py                 requirements.txt

۳. (دوباره) استقرار و اعتبارسنجی برنامه پایه

برای استقرار برنامه ماژول 20 مراحل زیر را انجام دهید:

  1. اگر پوشه lib وجود دارد، آن را حذف کنید و pip install -t lib -r requirements.txt را برای پر کردن مجدد آن اجرا کنید. اگر هر دو نسخه پایتون ۲ و ۳ را نصب کرده‌اید، ممکن است لازم باشد pip2 استفاده کنید.
  2. مطمئن شوید که ابزار خط فرمان gcloud را نصب و راه‌اندازی اولیه کرده‌اید و نحوه‌ی استفاده از آن را بررسی کرده‌اید.
  3. اگر نمی‌خواهید با هر دستور gcloud که صادر می‌شود، PROJ_ID خود را وارد کنید، ابتدا پروژه Cloud را با gcloud config set project PROJ_ID تنظیم کنید.
  4. برنامه نمونه را با gcloud app deploy مستقر کنید
  5. تأیید کنید که برنامه طبق انتظار و بدون خطا اجرا می‌شود. اگر بخش کدنویسی ماژول ۲۰ را تکمیل کرده باشید، برنامه اطلاعات ورود کاربر (ایمیل کاربر، «نشان مدیر» احتمالی و دکمه ورود/خروج) را در بالا به همراه آخرین بازدیدها (در زیر نشان داده شده است) نمایش می‌دهد.

907e64c19ef964f8.png

ورود به سیستم به عنوان یک کاربر عادی باعث می‌شود آدرس ایمیل کاربر نمایش داده شود و دکمه "ورود" به دکمه "خروج" تغییر کند:

ad7b59916b69a035.png

ورود به سیستم به عنوان کاربر مدیر باعث می‌شود آدرس ایمیل کاربر به همراه عبارت "(admin)" در کنار آن نمایش داده شود:

867bcb3334149e4.png

۴. فعال کردن APIها/سرویس‌های جدید گوگل کلود

مقدمه

برنامه ماژول ۲۰ از App Engine NDB و APIهای کاربران استفاده می‌کند، سرویس‌های همراه که نیازی به تنظیمات اضافی ندارند، اما سرویس‌های ابری مستقل نیاز به تنظیمات اضافی دارند و برنامه به‌روزرسانی‌شده از هر دو پلتفرم هویت ابری و فروشگاه داده ابری (از طریق کتابخانه کلاینت Cloud NDB ) استفاده خواهد کرد. علاوه بر این، نیاز ما به تعیین کاربران ادمین App Engine نیز مستلزم استفاده از API مدیریت منابع ابری است.

هزینه

  • App Engine و Cloud Datastore سهمیه‌های سطح «همیشه رایگان» دارند و تا زمانی که زیر این محدودیت‌ها بمانید، نباید برای تکمیل این آموزش هزینه‌ای متحمل شوید. همچنین برای جزئیات بیشتر به صفحه قیمت‌گذاری App Engine و صفحه قیمت‌گذاری Cloud Datastore مراجعه کنید.
  • هزینه استفاده از پلتفرم هویت ابری (Cloud Identity Platform) بسته به تعداد کاربران فعال ماهانه (MAU) یا تأیید هویت محاسبه می‌شود؛ برای هر مدل استفاده، نسخه‌ای از آن با عنوان «رایگان» در دسترس است. برای جزئیات بیشتر به صفحه قیمت‌گذاری آن مراجعه کنید. علاوه بر این، در حالی که App Engine و Cloud Datastore نیاز به پرداخت هزینه دارند، استفاده از GCIP به خودی خود نیازی به فعال کردن پرداخت هزینه ندارد، مادامی که از سهمیه روزانه بدون ابزار آن تجاوز نکنید. بنابراین این مورد را برای پروژه‌های ابری که شامل APIها/سرویس‌های ابری مورد نیاز برای پرداخت هزینه نیستند، در نظر بگیرید.
  • طبق صفحه قیمت‌گذاری، استفاده از رابط برنامه‌نویسی کاربردی مدیریت منابع ابری (Cloud Resource Manager API) عمدتاً رایگان است.

کاربران می‌توانند بسته به ترجیح خود، APIهای ابری را از کنسول ابری یا از خط فرمان (از طریق دستور gcloud ، بخشی از Cloud SDK )، فعال کنند. بیایید با APIهای Cloud Datastore و Cloud Resource Manager شروع کنیم.

از کنسول ابری

به صفحه کتابخانه API Manager (برای پروژه صحیح) در Cloud Console بروید و با استفاده از نوار جستجو، یک API را جستجو کنید. c7a740304e9d35b.png

این APIها را فعال کنید:

دکمه‌ی فعال‌سازی (Enable) را برای هر API به صورت جداگانه پیدا کرده و روی آن کلیک کنید—ممکن است از شما اطلاعات صورتحساب خواسته شود. برای مثال، صفحه‌ی مربوط به API مدیریت منابع (Resource Manager API) در اینجا آمده است:

f7bd8f4c49d12e5.png

وقتی دکمه فعال شود (معمولاً پس از چند ثانیه) به مدیریت تغییر می‌کند:

8eca12d6cc7b45b0.png

Cloud Datastore را به همین روش فعال کنید:

83811599b110e46b.png

از خط فرمان

اگرچه فعال کردن APIها از طریق کنسول از نظر بصری آموزنده است، اما برخی خط فرمان را ترجیح می‌دهند. شما این امتیاز اضافی را دارید که می‌توانید هر تعداد API را به طور همزمان فعال کنید. این دستور را برای فعال کردن هر دو API مربوط به Cloud Datastore و Cloud Resource Manager صادر کنید و منتظر بمانید تا عملیات تکمیل شود، همانطور که در اینجا نشان داده شده است:

$ gcloud services enable cloudresourcemanager.googleapis.com datastore.googleapis.com
Operation "operations/acat.p2-aaa-bbb-ccc-ddd-eee-ffffff" finished successfully.

ممکن است از شما اطلاعات صورتحساب خواسته شود.

«URL»های هر API که در دستور بالا استفاده شده‌اند، نام سرویس API نامیده می‌شوند و می‌توانید آن‌ها را در پایین صفحه کتابخانه هر API پیدا کنید. اگر می‌خواهید APIهای ابری دیگری را برای برنامه‌های خود فعال کنید، می‌توانید نام سرویس‌های مربوطه را در صفحات API مربوطه‌شان پیدا کنید. این دستور تمام نام‌های سرویس APIهایی را که می‌توانید فعال کنید، فهرست می‌کند:

gcloud services list --available --filter="name:googleapis.com" .

چه در کنسول Cloud و چه در خط فرمان، پس از تکمیل مراحل بالا، نمونه ما اکنون می‌تواند به آن APIها دسترسی داشته باشد. مراحل بعدی فعال کردن پلتفرم هویت ابری و ایجاد تغییرات لازم در کد است.

فعال‌سازی و راه‌اندازی پلتفرم هویت ابری (فقط کنسول ابری)

پلتفرم هویت ابری (Cloud Identity Platform) یک سرویس Marketplace است زیرا به منبعی خارج از Google Cloud، مثلاً Firebase Authentication ، متصل می‌شود یا به آن وابسته است. در حال حاضر، فقط می‌توانید سرویس‌های Marketplace را از کنسول Cloud فعال کنید. مراحل زیر را دنبال کنید:

  1. به صفحه پلتفرم هویت ابری در بازار ابری بروید و روی دکمه فعال‌سازی کلیک کنید. در صورت درخواست، از احراز هویت فایربیس ارتقا دهید - انجام این کار ویژگی‌های اضافی، مانند مواردی که قبلاً در بخش پس‌زمینه توضیح داده شد، را فعال می‌کند. در اینجا صفحه بازار را مشاهده می‌کنید که دکمه فعال‌سازی را برجسته می‌کند: ۲۸۴۷۵f1c9b29de69.png
  2. پس از فعال شدن پلتفرم هویت، ممکن است به طور خودکار به صفحه ارائه دهندگان هویت هدایت شوید. در غیر این صورت، از این لینک مناسب برای رفتن به آنجا استفاده کنید. fc2d92d42a5d1dd7.png
  3. ارائه‌دهنده‌ی احراز هویت گوگل (Google Auth provider) را فعال کنید. اگر هیچ ارائه‌دهنده‌ای تنظیم نشده است، روی افزودن یک ارائه‌دهنده (Add a Provider) کلیک کنید و گوگل (Google) را انتخاب کنید. وقتی به این صفحه برگردید، ورودی گوگل باید فعال شده باشد. گوگل تنها ارائه‌دهنده‌ی احراز هویتی است که ما در این آموزش برای انعکاس سرویس کاربران موتور برنامه (App Engine Users) به عنوان یک سرویس ورود به سیستم گوگل (Google Sign-In) سبک وزن، از آن استفاده می‌کنیم. در برنامه‌های خودتان، می‌توانید ارائه‌دهندگان احراز هویت دیگری را فعال کنید.
  4. وقتی گوگل و سایر ارائه‌دهندگان احراز هویت مورد نظر را انتخاب و تنظیم کردید، روی جزئیات تنظیمات برنامه کلیک کنید و از پنجره‌ی محاوره‌ایِ تضمین، apiKey و authDomain را در شیء config در تب وب کپی کنید و هر دو را در جایی امن ذخیره کنید. چرا همه آن را کپی نمی‌کنید؟ قطعه کد موجود در این کادر محاوره‌ای، کدگذاری شده و تاریخ‌گذاری شده است، بنابراین فقط مهم‌ترین بخش‌ها را ذخیره کنید و از آنها در کد خود با استفاده‌ی همزمان‌تر از احراز هویت Firebase استفاده کنید. پس از کپی کردن مقادیر و ذخیره آنها در جایی امن، روی دکمه‌ی بستن کلیک کنید و تمام تنظیمات لازم را انجام دهید. bbb09dcd9be538e.png

۴. به‌روزرسانی پیکربندی

به‌روزرسانی‌ها در پیکربندی شامل تغییر فایل‌های پیکربندی مختلف و همچنین ایجاد معادل App Engine اما در اکوسیستم Cloud Identity Platform می‌شود.

appengine_config.py

  • اگر به پایتون ۳ ارتقا می‌دهید، appengine_config.py حذف کنید.
  • اگر قصد دارید به پلتفرم Identity مهاجرت کنید اما همچنان از پایتون ۲ استفاده کنید، این فایل را حذف نکنید. در عوض، بعداً در طول بک‌پورت پایتون ۲ آن را به‌روزرسانی خواهیم کرد.

الزامات.txt

فایل requirements.txt ماژول ۲۰ فقط Flask را فهرست کرده است. برای ماژول ۲۱، بسته‌های زیر را اضافه کنید:

محتوای requirements.txt اکنون باید به این شکل باشد:

flask
google-auth
google-cloud-ndb
google-cloud-resource-manager
firebase-admin

برنامه.yaml

  • ارتقا به پایتون ۳ به معنای ساده‌سازی فایل app.yaml است. همه چیز را به جز دستورالعمل زمان اجرا حذف کنید و آن را روی نسخه‌ای از پایتون ۳ که در حال حاضر پشتیبانی می‌شود تنظیم کنید. این مثال در حال حاضر از نسخه ۳.۱۰ استفاده می‌کند.
  • اگر همچنان از پایتون ۲ استفاده می‌کنید، فعلاً هیچ اقدامی انجام ندهید.

قبل از:

runtime: python27
threadsafe: yes
api_version: 1

handlers:
- url: /.*
  script: main.app

برنامه نمونه ماژول ۲۰ دارای کنترل‌کننده‌های فایل استاتیک نیست. اگر برنامه‌های شما این کار را می‌کنند، آنها را دست‌نخورده بگذارید. در صورت تمایل می‌توانید تمام کنترل‌کننده‌های اسکریپت خود را حذف کنید یا آنها را برای مرجع همان‌جا بگذارید، به شرطی که کنترل‌کننده‌های آنها را به auto تغییر دهید، همانطور که در راهنمای مهاجرت app.yaml توضیح داده شده است. با این تغییرات، app.yaml به‌روزرسانی‌شده برای پایتون ۳ به صورت زیر ساده می‌شود:

بعد از:

runtime: python310

سایر به‌روزرسانی‌های پیکربندی

چه بخواهید از پایتون ۲ استفاده کنید و چه بخواهید به پایتون ۳ منتقل شوید، اگر پوشه lib دارید، آن را حذف کنید.

۵. کد برنامه را تغییر دهید

این بخش شامل به‌روزرسانی‌هایی برای فایل اصلی برنامه، main.py ، است که استفاده از سرویس App Engine Users را با Cloud Identity Platform جایگزین می‌کند. پس از به‌روزرسانی برنامه اصلی، قالب وب، templates/index.html ، را به‌روزرسانی خواهید کرد.

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

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

  1. برای ایمپورت‌ها، App Engine NDB را با Cloud NDB جایگزین کنید.
  2. همراه با Cloud NDB، Cloud Resource Manager را نیز وارد کنید.
  3. پلتفرم Identity مبتنی بر Firebase Auth است، بنابراین Firebase Admin SDK را وارد کنید.
  4. APIهای ابری نیاز به استفاده از یک کلاینت API دارند، بنابراین آن را برای Cloud NDB درست زیر مقداردهی اولیه Flask آغاز کنید.

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

قبل از:

from flask import Flask, render_template, request
from google.appengine.api import users
from google.appengine.ext import ndb

app = Flask(__name__)

بعد از:

from flask import Flask, render_template, request
from google.auth import default
from google.cloud import ndb, resourcemanager
from firebase_admin import auth, initialize_app

# initialize Flask and Cloud NDB API client
app = Flask(__name__)
ds_client = ndb.Client()

پشتیبانی از کاربران مدیر موتور برنامه

دو مؤلفه برای اضافه کردن به برنامه وجود دارد که از شناسایی کاربران مدیر پشتیبانی می‌کند:

  • _get_gae_admins() — مجموعه‌ای از کاربران ادمین را جمع‌آوری می‌کند؛ یک بار فراخوانی و ذخیره می‌شود
  • is_admin() - بررسی می‌کند که آیا کاربر وارد شده، کاربر ادمین است یا خیر؛ در هر ورود کاربری فراخوانی می‌شود.

تابع کاربردی _get_gae_admins() ‎، رابط برنامه‌نویسی کاربردی (API) مدیریت منابع (Resource Manager) را فراخوانی می‌کند تا سیاست مجاز (allow-policy) فعلی Cloud IAM را دریافت کند. سیاست مجاز (allow-policy) تعریف و اعمال می‌کند که چه نقش‌هایی به کدام مدیران اصلی (کاربران انسانی، حساب‌های خدماتی و غیره) اعطا شود. این تنظیمات شامل موارد زیر است:

  • دریافت شناسه پروژه ابری ( PROJ_ID )
  • ایجاد یک کلاینت API مدیریت منابع ( rm_client )
  • ایجاد مجموعه‌ای (فقط خواندنی) از نقش‌های مدیر موتور برنامه ( _TARGETS )

مدیر منابع به شناسه پروژه ابری نیاز دارد، بنابراین google.auth.default() را وارد کنید و آن تابع را برای دریافت شناسه پروژه فراخوانی کنید. این فراخوانی شامل پارامتری است که شبیه یک URL است اما دامنه مجوز OAuth2 دارد. هنگام اجرای برنامه‌ها در فضای ابری، به عنوان مثال، در یک ماشین مجازی Compute Engine یا برنامه App Engine، یک حساب سرویس پیش‌فرض ارائه می‌شود که دارای امتیازات گسترده‌ای است. با توجه به بهترین شیوه حداقل امتیاز، توصیه می‌کنیم حساب‌های سرویس مدیریت‌شده توسط کاربر خود را ایجاد کنید.

برای فراخوانی‌های API، بهتر است دامنه برنامه‌های خود را به حداقل سطح مورد نیاز برای عملکرد صحیح کاهش دهید . فراخوانی API مدیریت منابع که ما انجام خواهیم داد، get_iam_policy() است که برای عملکرد به یکی از دامنه‌های زیر نیاز دارد :

  • https://www.googleapis.com/auth/cloud-platform
  • https://www.googleapis.com/auth/cloud-platform.read-only
  • https://www.googleapis.com/auth/cloudplatformprojects
  • https://www.googleapis.com/auth/cloudplatformprojects.readonly

برنامه نمونه فقط به دسترسی فقط خواندنی به allow-policy نیاز دارد. این دسترسی نه سیاست را تغییر می‌دهد و نه به کل پروژه دسترسی دارد. این بدان معناست که برنامه به هیچ یک از سه مجوز اول مورد نیاز نیاز ندارد. آخرین مورد، تمام چیزی است که مورد نیاز است و این همان چیزی است که ما برای برنامه نمونه پیاده‌سازی می‌کنیم.

بدنه اصلی تابع، یک مجموعه خالی از کاربران مدیر ( admins ) ایجاد می‌کند، allow_policy از طریق get_iam_policy() دریافت می‌کند و تمام متغیرهای آن را به طور خاص برای یافتن نقش‌های مدیر App Engine پیمایش می‌کند:

  • roles/viewer
  • roles/editor
  • roles/owner
  • roles/appengine.appAdmin

برای هر نقش هدف یافت شده، کاربرانی که به آن نقش تعلق دارند را جمع‌آوری کرده و آنها را به مجموعه کلی کاربران مدیر اضافه می‌کند. در نهایت، تمام کاربران مدیر یافت شده و ذخیره شده به عنوان یک ثابت ( _ADMINS ) را برای طول عمر این نمونه App Engine برمی‌گرداند. به زودی شاهد فراخوانی آن خواهیم بود.

تعریف تابع _get_gae_admins() زیر را درست زیر نمونه‌سازی کلاینت Cloud NDB API ( ds_client ) به main.py اضافه کنید:

def _get_gae_admins():
    'return set of App Engine admins'
    # setup constants for calling Cloud Resource Manager API
    _, PROJ_ID = default(  # Application Default Credentials and project ID
            ['https://www.googleapis.com/auth/cloudplatformprojects.readonly'])
    rm_client = resourcemanager.ProjectsClient()
    _TARGETS = frozenset((     # App Engine admin roles
            'roles/viewer',
            'roles/editor',
            'roles/owner',
            'roles/appengine.appAdmin',
    ))

    # collate users who are members of at least one GAE admin role (_TARGETS)
    admins = set()                      # set of all App Engine admins
    allow_policy = rm_client.get_iam_policy(resource='projects/%s' % PROJ_ID)
    for b in allow_policy.bindings:     # bindings in IAM allow-policy
        if b.role in _TARGETS:          # only look at GAE admin roles
            admins.update(user.split(':', 1).pop() for user in b.members)
    return admins

وقتی کاربران وارد برنامه می‌شوند، موارد زیر رخ می‌دهد:

  1. پس از ورود کاربر به Firebase، یک بررسی سریع از الگوی وب انجام می‌شود.
  2. وقتی وضعیت احراز هویت در قالب تغییر می‌کند، یک فراخوانی fetch() به سبک Ajax به /is_admin انجام می‌شود که مدیریت‌کننده آن تابع بعدی، is_admin() است.
  3. توکن شناسه فایربیس در بدنه POST به is_admin() ارسال می‌شود، که آن را از هدرها می‌گیرد و SDK مدیریت فایربیس را برای اعتبارسنجی آن فراخوانی می‌کند. اگر کاربر معتبر باشد، آدرس ایمیل او را استخراج کرده و بررسی می‌کند که آیا کاربر مدیر است یا خیر.
  4. سپس نتیجه بولی به عنوان یک نتیجه موفق ۲۰۰ به قالب برگردانده می‌شود.

is_admin() درست بعد از _get_gae_admins() به main.py اضافه کنید:

@app.route('/is_admin', methods=['POST'])
def is_admin():
    'check if user (via their Firebase ID token) is GAE admin (POST) handler'
    id_token = request.headers.get('Authorization')
    email = auth.verify_id_token(id_token).get('email')
    return {'admin': email in _ADMINS}, 200

تمام کدهای هر دو تابع برای تکرار عملکردهای موجود در سرویس Users، به ویژه تابع is_current_user_admin() آن، مورد نیاز هستند. این فراخوانی تابع در ماژول 20 تمام کارهای سنگین را انجام داد، برخلاف ماژول 21 که در آن یک راه حل جایگزین را پیاده‌سازی کردیم. خبر خوب این است که برنامه دیگر به یک سرویس App Engine وابسته نیست، به این معنی که می‌توانید برنامه‌های خود را به Cloud Run یا سایر سرویس‌ها منتقل کنید. علاوه بر این، می‌توانید تعریف "کاربر ادمین" را برای برنامه‌های خود نیز فقط با تغییر به نقش‌های مورد نظر در _TARGETS تغییر دهید، در حالی که سرویس Users برای نقش‌های ادمین App Engine به صورت hardcoded شده است.

مقداردهی اولیه Firebase Auth و ذخیره سازی کاربران ادمین App Engine

می‌توانستیم Firebase Auth را در بالا، نزدیک به همان نقطه‌ای که برنامه Flask مقداردهی اولیه شده و کلاینت Cloud NDB API ایجاد شده است، مقداردهی اولیه کنیم، اما تا زمانی که تمام کد مدیریت تعریف نشده بود، نیازی به این کار نبود، که اکنون در آن هستیم. به طور مشابه، اکنون که _get_gae_admins() تعریف شده است، آن را برای ذخیره لیست کاربران مدیر فراخوانی کنید.

این خطوط را درست زیر بدنه تابع is_admin() اضافه کنید:

# initialize Firebase and fetch set of App Engine admins
initialize_app()
_ADMINS = _get_gae_admins()

از به‌روزرسانی‌های مدل داده بازدید کنید

مدل داده Visit تغییر نمی‌کند. دسترسی به Datastore نیازمند استفاده صریح از مدیریت زمینه کلاینت Cloud NDB API، یعنی ds_client.context() است. در کد، این به این معنی است که شما فراخوانی‌های Datastore را در هر دو store_visit() و fetch_visits() درون پایتون with بلوک‌ها قرار می‌دهید. این به‌روزرسانی مشابه ماژول ۲ است. تغییرات را به شرح زیر اعمال کنید:

قبل از:

class Visit(ndb.Model):
    'Visit entity registers visitor IP address & timestamp'
    visitor   = ndb.StringProperty()
    timestamp = ndb.DateTimeProperty(auto_now_add=True)

def store_visit(remote_addr, user_agent):
    'create new Visit entity in Datastore'
    Visit(visitor='{}: {}'.format(remote_addr, user_agent)).put()

def fetch_visits(limit):
    'get most recent visits'
    return Visit.query().order(-Visit.timestamp).fetch(limit)

بعد از:

class Visit(ndb.Model):
    'Visit entity registers visitor IP address & timestamp'
    visitor   = ndb.StringProperty()
    timestamp = ndb.DateTimeProperty(auto_now_add=True)

def store_visit(remote_addr, user_agent):
    'create new Visit entity in Datastore'
    with ds_client.context():
        Visit(visitor='{}: {}'.format(remote_addr, user_agent)).put()

def fetch_visits(limit):
    'get most recent visits'
    with ds_client.context():
        return Visit.query().order(-Visit.timestamp).fetch(limit)

منطق ورود کاربر را به الگوی وب منتقل کنید

سرویس App Engine Users سمت سرور است در حالی که Firebase Auth و Cloud Identity Platform عمدتاً سمت کلاینت هستند. در نتیجه، بخش زیادی از کد مدیریت کاربر در برنامه ماژول 20 به قالب وب ماژول 21 منتقل می‌شود.

در main.py ، زمینه وب پنج قطعه داده ضروری را به الگو منتقل می‌کند، چهار مورد اول ذکر شده به مدیریت کاربر مرتبط هستند و بسته به اینکه کاربر وارد سیستم شده باشد یا خیر، متفاوت هستند:

  • who - ایمیل کاربر در صورت ورود به سیستم یا در غیر این صورت کاربر
  • admin - نشان (مدیر) اگر کاربر وارد شده مدیر باشد
  • sign - نمایش دکمه ورود یا خروج
  • link - لینک‌های ورود یا خروج هنگام کلیک روی دکمه
  • visits — آخرین بازدیدها

قبل از:

@app.route('/')
def root():
    'main application (GET) handler'
    store_visit(request.remote_addr, request.user_agent)
    visits = fetch_visits(10)

    # put together users context for web template
    user = users.get_current_user()
    context = {  # logged in
        'who':   user.nickname(),
        'admin': '(admin)' if users.is_current_user_admin() else '',
        'sign':  'Logout',
        'link':  '/_ah/logout?continue=%s://%s/' % (
                      request.environ['wsgi.url_scheme'],
                      request.environ['HTTP_HOST'],
                  ),  # alternative to users.create_logout_url()
    } if user else {  # not logged in
        'who':   'user',
        'admin': '',
        'sign':  'Login',
        'link':  users.create_login_url('/'),
    }

    # add visits to context and render template
    context['visits'] = visits  # display whether logged in or not
    return render_template('index.html', **context)

تمام مدیریت کاربران به قالب وب منتقل می‌شود، بنابراین فقط بازدیدها باقی می‌مانند و کنترل‌کننده اصلی به همان چیزی که در برنامه ماژول ۱ داشتیم، برمی‌گردد:

بعد از:

@app.route('/')
def root():
    'main application (GET) handler'
    store_visit(request.remote_addr, request.user_agent)
    visits = fetch_visits(10)
    return render_template('index.html', visits=visits)

قالب وب را به‌روزرسانی کنید

تمام به‌روزرسانی‌های بخش قبلی در قالب چگونه به نظر می‌رسند؟ عمدتاً انتقال مدیریت کاربر از برنامه به Firebase Auth که در قالب اجرا می‌شود و پورت بخشی از تمام آن کدی که به جاوا اسکریپت منتقل کردیم. شاهد کاهش قابل توجه main.py بودیم، بنابراین انتظار رشد مشابهی را در templates/index.html داشته باشید.

قبل از:

<!doctype html>
<html>
<head>
<title>VisitMe Example</title>
</head>
<body>
<p>
Welcome, {{ who }} <code>{{ admin }}</code>
<button id="logbtn">{{ sign }}</button>
</p><hr>

<h1>VisitMe example</h1>
<h3>Last 10 visits</h3>
<ul>
{% for visit in visits %}
    <li>{{ visit.timestamp.ctime() }} from {{ visit.visitor }}</li>
{% endfor %}
</ul>

<script>
document.getElementById("logbtn").onclick = () => {
    window.location.href = '{{ link }}';
};
</script>
</body>
</html>

کل قالب وب را با محتویات زیر جایگزین کنید:

بعد از:

<!doctype html>
<html>
<head>
<title>VisitMe Example</title>

<script type="module">
// import Firebase module attributes
import {
        initializeApp
} from "https://www.gstatic.com/firebasejs/9.10.0/firebase-app.js";
import {
        GoogleAuthProvider,
        getAuth,
        onAuthStateChanged,
        signInWithPopup,
        signOut
} from "https://www.gstatic.com/firebasejs/9.10.0/firebase-auth.js";

// Firebase config:
// 1a. Go to: console.cloud.google.com/customer-identity/providers
// 1b. May be prompted to enable GCIP and upgrade from Firebase
// 2. Click: "Application Setup Details" button
// 3. Copy: 'apiKey' and 'authDomain' from 'config' variable
var firebaseConfig = {
        apiKey: "YOUR_API_KEY",
        authDomain: "YOUR_AUTH_DOMAIN",
};

// initialize Firebase app & auth components
initializeApp(firebaseConfig);
var auth = getAuth();
var provider = new GoogleAuthProvider();
//provider.setCustomParameters({prompt: 'select_account'});

// define login and logout button functions
function login() {
    signInWithPopup(auth, provider);
};

function logout() {
    signOut(auth);
};

// check if admin & switch to logout button on login; reset everything on logout
onAuthStateChanged(auth, async (user) => {
    if (user && user != null) {
        var email = user.email;
        who.innerHTML = email;
        logbtn.onclick = logout;
        logbtn.innerHTML = "Logout";
        var idToken = await user.getIdToken();
        var rsp = await fetch("/is_admin", {
                method: "POST",
                headers: {Authorization: idToken}
        });
        var data = await rsp.json();
        if (data.admin) {
            admin.style.display = "inline";
        }
    } else {
        who.innerHTML = "user";
        admin.style.display = "none";
        logbtn.onclick = login;
        logbtn.innerHTML = "Login";
    }
});
</script>
</head>

<body>
<p>
Welcome, <span id="who"></span> <span id="admin"><code>(admin)</code></span>
<button id="logbtn"></button>
</p><hr>

<h1>VisitMe example</h1>
<h3>Last 10 visits</h3>
<ul>
{% for visit in visits %}
    <li>{{ visit.timestamp.ctime() }} from {{ visit.visitor }}</li>
{% endfor %}
</ul>

<script>
var who    = document.getElementById("who");
var admin  = document.getElementById("admin");
var logbtn = document.getElementById("logbtn");
</script>
</body>
</html>

اجزای زیادی در بدنه HTML وجود دارد، بنابراین بیایید آنها را به صورت جداگانه بررسی کنیم.

واردات فایربیس

در حالی که هنوز در هدر سند HTML هستید، پس از گذشتن از عنوان صفحه، اجزای Firebase مورد نیاز را وارد کنید. اجزای Firebase اکنون برای کارایی بیشتر به چندین ماژول تقسیم شده‌اند. کدی که Firebase را مقداردهی اولیه می‌کند از ماژول اصلی برنامه Firebase وارد می‌شود، در حالی که توابعی که احراز هویت Firebase، گوگل به عنوان ارائه دهنده احراز هویت، ورود و خروج و تغییر وضعیت احراز هویت "callback" را مدیریت می‌کنند، همگی از ماژول Firebase Auth وارد می‌شوند:

<!doctype html>
<html>
<head>
<title>VisitMe Example</title>

<script type="module">
// import Firebase module attributes
import {
        initializeApp
} from "https://www.gstatic.com/firebasejs/9.10.0/firebase-app.js";
import {
        GoogleAuthProvider,
        getAuth,
        onAuthStateChanged,
        signInWithPopup,
        signOut
} from "https://www.gstatic.com/firebasejs/9.10.0/firebase-auth.js";

پیکربندی فایربیس

پیش از این در بخش تنظیمات پلتفرم هویت در این آموزش، apiKey و authDomain را از کادر محاوره‌ای جزئیات تنظیمات برنامه ذخیره کردید. این مقادیر را در بخش بعدی به متغیر firebaseConfig اضافه کنید. لینکی به دستورالعمل‌های جزئیات بیشتر در نظرات ارائه شده است:

// Firebase config:
// 1a. Go to: console.cloud.google.com/customer-identity/providers
// 1b. May be prompted to enable GCIP and upgrade from Firebase
// 2. Click: "Application Setup Details" button
// 3. Copy: 'apiKey' and 'authDomain' from 'config' variable
var firebaseConfig = {
        apiKey: "YOUR_API_KEY",
        authDomain: "YOUR_AUTH_DOMAIN",
};

مقداردهی اولیه فایربیس

بخش بعدی، Firebase را با این اطلاعات پیکربندی مقداردهی اولیه می‌کند.

// initialize Firebase app & auth components
initializeApp(firebaseConfig);
var auth = getAuth();
var provider = new GoogleAuthProvider();
//provider.setCustomParameters({prompt: 'select_account'});

این قابلیت، امکان استفاده از گوگل به عنوان ارائه‌دهنده‌ی احراز هویت را فراهم می‌کند و گزینه‌ای برای نمایش انتخابگر حساب کاربری ارائه می‌دهد، حتی اگر فقط یک حساب کاربری گوگل در مرورگر شما ثبت شده باشد. به عبارت دیگر، وقتی چندین حساب کاربری دارید، همانطور که انتظار می‌رود، با این «انتخابگر حساب کاربری» مواجه خواهید شد: a38369389b7c4c7e.png با این حال، اگر فقط یک کاربر در جلسه وجود داشته باشد، فرآیند ورود به سیستم بدون هیچ گونه تعامل کاربر به طور خودکار تکمیل می‌شود. (پنجره بازشو ظاهر می‌شود و سپس ناپدید می‌شود.) می‌توانید با لغو کامنت کردن خط پارامتر سفارشی، کادر محاوره‌ای انتخاب حساب را مجبور کنید که برای یک کاربر نمایش داده شود (به جای اینکه بلافاصله وارد برنامه شود). در صورت فعال بودن، حتی ورود تک کاربره نیز کادر انتخاب حساب را نمایش می‌دهد: b75624cb68d94557.png

توابع ورود و خروج

خطوط بعدی کد، توابع مربوط به کلیک‌های دکمه ورود یا خروج را تشکیل می‌دهند:

// define login and logout button functions
function login() {
    signInWithPopup(auth, provider);
};

function logout() {
    signOut(auth);
};

اقدامات ورود و خروج

آخرین بخش اصلی در این بلوک <script> تابعی است که برای هر تغییر مجوز (ورود یا خروج) فراخوانی می‌شود.

// check if admin & switch to logout button on login; reset everything on logout
onAuthStateChanged(auth, async (user) => {
    if (user && user != null) {
        var email = user.email;
        who.innerHTML = email;
        logbtn.onclick = logout;
        logbtn.innerHTML = "Logout";
        var idToken = await user.getIdToken();
        var rsp = await fetch("/is_admin", {
                method: "POST",
                headers: {Authorization: idToken}
        });
        var data = await rsp.json();
        if (data.admin) {
            admin.style.display = "inline";
        }
    } else {
        who.innerHTML = "user";
        admin.style.display = "none";
        logbtn.onclick = login;
        logbtn.innerHTML = "Login";
    }
});
</script>
</head>

کد موجود در ماژول ۲۰ که تعیین می‌کند آیا یک زمینه قالب «کاربر وارد شده» در مقابل یک زمینه «کاربر خارج شده» ارسال شود، در اینجا منتقل می‌شود. شرط بالا در صورتی که کاربر با موفقیت وارد سیستم شود، نتیجه true می‌دهد و اقدامات زیر را آغاز می‌کند:

  1. آدرس ایمیل کاربر برای نمایش تنظیم شده است.
  2. دکمه ورود به سیستم به خروج تغییر می‌کند.
  3. یک فراخوانی به سبک آژاکس به /is_admin انجام می‌شود تا مشخص شود که آیا نشان کاربری مدیر (admin) نمایش داده شود یا خیر.

وقتی کاربر از سیستم خارج می‌شود، عبارت else برای تنظیم مجدد تمام اطلاعات کاربر اجرا می‌شود:

  1. نام کاربری روی کاربر تنظیم شده است
  2. هرگونه نشان مدیریتی حذف شد
  3. دکمه خروج دوباره به ورود تغییر کرد

متغیرهای الگو

پس از پایان بخش هدر، بدنه اصلی با متغیرهای قالب آغاز می‌شود که با عناصر HTML جایگزین می‌شوند که در صورت لزوم تغییر می‌کنند:

  1. نام کاربری نمایش داده شده
  2. (admin) نشان مدیر (در صورت وجود)
  3. دکمه ورود یا خروج
<body>
<p>
Welcome, <span id="who"></span> <span id="admin"><code>(admin)</code></span>
<button id="logbtn"></button>
</p><hr>

آخرین بازدیدها و متغیرهای عنصر HTML

کد مربوط به آخرین بازدیدها تغییر نمی‌کند، و بلوک <script> نهایی، متغیرهایی را برای عناصر HTML که برای ورود و خروج از سیستم تغییر می‌کنند، تنظیم می‌کند:

<h1>VisitMe example</h1>
<h3>Last 10 visits</h3>
<ul>
{% for visit in visits %}
    <li>{{ visit.timestamp.ctime() }} from {{ visit.visitor }}</li>
{% endfor %}
</ul>

<script>
var who    = document.getElementById("who");
var admin  = document.getElementById("admin");
var logbtn = document.getElementById("logbtn");
</script>
</body>
</html>

بدین ترتیب، تغییرات مورد نیاز در برنامه و قالب وب برای تغییر از App Engine NDB و Users APIs به Cloud NDB و Identity Platform و همچنین ارتقا به پایتون ۳ به پایان می‌رسد. به خاطر دریافت نمونه برنامه ماژول ۲۱ جدیدتان تبریک می‌گوییم! نسخه ما برای بررسی در پوشه مخزن ماژول ۲۱b موجود است.

بخش بعدی codelab اختیاری (*) است و فقط برای کاربرانی است که برنامه‌هایشان باید روی پایتون ۲ باقی بمانند و شما را در مراحل لازم برای رسیدن به یک برنامه پایتون ۲ ماژول ۲۱ کارآمد راهنمایی می‌کند.

۶. *پشتیبانی پایتون ۲

این بخش اختیاری برای توسعه‌دهندگانی است که مهاجرت به پلتفرم Identity را انجام می‌دهند اما باید به اجرای آن در زمان اجرای پایتون ۲ ادامه دهند. اگر این موضوع برای شما مهم نیست، از این بخش صرف نظر کنید.

برای ایجاد یک نسخه پایتون ۲ کارآمد از برنامه ماژول ۲۱، به موارد زیر نیاز دارید:

  1. الزامات زمان اجرا : فایل‌های پیکربندی که از پایتون ۲ پشتیبانی می‌کنند و تغییرات لازم در برنامه اصلی برای جلوگیری از ناسازگاری پایتون ۳
  2. تغییر جزئی کتابخانه: پایتون ۲ قبل از اضافه شدن برخی از ویژگی‌های مورد نیاز به کتابخانه کلاینت Resource Manager منسوخ شد. در نتیجه، شما به یک روش جایگزین برای دسترسی به آن قابلیت از دست رفته نیاز دارید.

بیایید اکنون این مراحل را انجام دهیم، با پیکربندی شروع کنیم.

بازیابی appengine_config.py

پیش از این در این آموزش، به شما توصیه شد که appengine_config.py حذف کنید، زیرا توسط زمان اجرای App Engine پایتون ۳ استفاده نمی‌شود. برای پایتون ۲، نه تنها باید آن را حفظ کرد، بلکه ماژول ۲۰ appengine_config.py نیز باید به‌روزرسانی شود تا از استفاده از کتابخانه‌های شخص ثالث داخلی ، یعنی grpcio و setuptools ، پشتیبانی کند. این بسته‌ها هر زمان که برنامه App Engine شما از کتابخانه‌های کلاینت Cloud مانند کتابخانه‌های Cloud NDB و Cloud Resource Manager استفاده می‌کند، مورد نیاز هستند.

شما آن بسته‌ها را به صورت لحظه‌ای به app.yaml اضافه خواهید کرد، اما برای اینکه برنامه شما به آنها دسترسی داشته باشد، باید تابع pkg_resources.working_set.add_entry() از setuptools فراخوانی شود. این امر به کتابخانه‌های شخص ثالث کپی شده (خود-بسته‌بندی شده یا فروشنده‌ای) که در پوشه lib نصب شده‌اند، اجازه می‌دهد تا با کتابخانه‌های داخلی ارتباط برقرار کنند.

برای اعمال این تغییرات، به‌روزرسانی‌های زیر را در فایل appengine_config.py خود پیاده‌سازی کنید:

قبل از:

from google.appengine.ext import vendor

# Set PATH to your libraries folder.
PATH = 'lib'
# Add libraries installed in the PATH folder.
vendor.add(PATH)

این کد به تنهایی برای پشتیبانی از استفاده از setuptools و grpcio کافی نیست. چند خط دیگر مورد نیاز است، بنابراین appengine_config.py را طوری به‌روزرسانی کنید که به شکل زیر باشد:

بعد از:

import pkg_resources
from google.appengine.ext import vendor

# Set PATH to your libraries folder.
PATH = 'lib'
# Add libraries installed in the PATH folder.
vendor.add(PATH)
# Add libraries to pkg_resources working set to find the distribution.
pkg_resources.working_set.add_entry(PATH)

جزئیات بیشتر در مورد تغییرات مورد نیاز برای پشتیبانی از کتابخانه‌های کلاینت ابری را می‌توانید در مستندات سرویس‌های همراه در حال انتقال بیابید.

برنامه.yaml

مشابه appengine_config.py ، فایل app.yaml باید به فایلی که از پایتون ۲ پشتیبانی می‌کند، برگردانده شود. بیایید با ماژول اصلی app.yaml یعنی ماژول ۲۰، شروع کنیم:

قبل از:

runtime: python27
threadsafe: yes
api_version: 1

handlers:
- url: /.*
  script: main.app

علاوه بر setuptools و grpcio همانطور که قبلاً ذکر شد، یک وابستگی (که صریحاً به مهاجرت پلتفرم Identity مربوط نمی‌شود) وجود دارد که نیاز به استفاده از کتابخانه کلاینت Cloud Storage دارد و به یک بسته شخص ثالث داخلی دیگر، ssl ، نیاز دارد. هر سه را در بخش libraries جدید اضافه کنید و "آخرین" نسخه‌های موجود از آن بسته‌ها را انتخاب کنید و به app.yaml :

بعد از:

runtime: python27
threadsafe: yes
api_version: 1

handlers:
- url: /.*
  script: main.app

libraries:
- name: grpcio
  version: latest
- name: setuptools
  version: latest
- name: ssl
  version: latest

الزامات.txt

برای ماژول ۲۱، ما Google Auth ، Cloud NDB ، Cloud Resource Manager و Firebase Admin SDK را به فایل requirements.txt پایتون ۳ اضافه کردیم. وضعیت برای پایتون ۲ پیچیده‌تر است:

  • رابط برنامه‌نویسی کاربردی مدیریت منابع (Resource Manager API) قابلیت allow-policy مورد نیاز برای برنامه نمونه را فراهم می‌کند. متأسفانه این پشتیبانی هنوز در نسخه نهایی پایتون ۲ کتابخانه کلاینت مدیریت منابع ابری (Cloud Resource Manager ) در دسترس نبود. (این قابلیت فقط در نسخه پایتون ۳ موجود است.)
  • در نتیجه، یک روش جایگزین برای دسترسی به این ویژگی از طریق API مورد نیاز است. راه حل، استفاده از کتابخانه کلاینت سطح پایین Google APIs برای ارتباط با API است. برای جابجایی به این کتابخانه کلاینت، google-cloud-resource-manager با بسته سطح پایین google-api-python-client جایگزین کنید.
  • از آنجا که پایتون ۲ دیگر پشتیبانی نمی‌شود ، نمودار وابستگی که از ماژول ۲۱ پشتیبانی می‌کند، نیاز به قفل کردن بسته‌های خاص به نسخه‌های خاص دارد. برخی از بسته‌ها حتی اگر در app.yaml پایتون ۳ مشخص نشده باشند، باید فراخوانی شوند.

قبل از:

flask

با شروع از فایل requirements.txt ماژول ۲۰، آن را برای یک برنامه ماژول ۲۱ که کار می‌کند، به صورت زیر به‌روزرسانی کنید:

بعد از:

grpcio==1.0.0
protobuf<3.18.0
six>=1.13.0
flask
google-gax<0.13.0
google-api-core==1.31.1
google-api-python-client<=1.11.0
google-auth<2.0dev
google-cloud-datastore==1.15.3
google-cloud-firestore==1.9.0
google-cloud-ndb
google-cloud-pubsub==1.7.0
firebase-admin

شماره بسته‌ها و نسخه‌ها با تغییر وابستگی‌ها در مخزن به‌روزرسانی می‌شوند، اما در زمان نوشتن این مطلب، app.yaml برای یک برنامه‌ی در حال اجرا کافی است.

سایر به‌روزرسانی‌های پیکربندی

اگر پوشه lib را از مراحل قبلی این codelab حذف نکرده‌اید، اکنون این کار را انجام دهید. با استفاده از requirements.txt که به تازگی به‌روزرسانی شده است، این دستور آشنا را برای نصب این الزامات در lib اجرا کنید:

pip install -t lib -r requirements.txt  # or pip2

اگر پایتون ۲ و ۳ را روی سیستم توسعه خود نصب کرده‌اید، ممکن است لازم باشد به جای pip از pip2 استفاده کنید.

اصلاح کد برنامه

خوشبختانه، بیشتر تغییرات مورد نیاز در فایل‌های پیکربندی هستند. تنها تغییر مورد نیاز در کد برنامه، یک به‌روزرسانی جزئی برای استفاده از کتابخانه کلاینت سطح پایین‌تر Google API به جای کتابخانه کلاینت Resource Manager برای دسترسی به API است. هیچ به‌روزرسانی در قالب وب templates/index.html لازم نیست.

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

کتابخانه کلاینت Resource Manager ( google.cloud.resourcemanager ) را با کتابخانه کلاینت Google APIs ( googleapiclient.discovery ) جایگزین کنید، همانطور که در زیر نشان داده شده است:

قبل از:

from flask import Flask, render_template, request
from google.auth import default
from google.cloud import ndb, resourcemanager
from firebase_admin import auth, initialize_app

بعد از:

from flask import Flask, render_template, request
from google.auth import default
from google.cloud import ndb
from googleapiclient import discovery
from firebase_admin import auth, initialize_app

پشتیبانی از کاربران مدیر موتور برنامه

برای پشتیبانی از استفاده از کتابخانه کلاینت سطح پایین‌تر، چند تغییر در _get_gae_admins() مورد نیاز است. بیایید ابتدا در مورد تغییرات بحث کنیم و سپس تمام کد را برای به‌روزرسانی در اختیار شما قرار دهیم.

کد پایتون ۲ نیاز به استفاده از اعتبارنامه‌ها و شناسه پروژه برگردانده شده از google.auth.default() دارد. اعتبارنامه‌ها در پایتون ۳ استفاده نمی‌شوند، بنابراین به یک متغیر ساختگی عمومی با زیرخط ( _ ) اختصاص داده شده‌اند. از آنجایی که برای نسخه پایتون ۲ مورد نیاز است، زیرخط را به CREDS تغییر دهید. همچنین، به جای ایجاد یک کلاینت API مدیریت منابع، یک نقطه پایانی سرویس API ایجاد خواهید کرد که از نظر مفهومی مشابه یک کلاینت API است، بنابراین ما همان نام متغیر ( rm_client ) را حفظ می‌کنیم. یک تفاوت این است که نمونه‌سازی یک نقطه پایانی سرویس نیاز به اعتبارنامه‌ها ( CREDS ) دارد.

این تغییرات در کد زیر منعکس شده است:

قبل از:

_, PROJ_ID = default(  # Application Default Credentials and project ID
        ['https://www.googleapis.com/auth/cloudplatformprojects.readonly'])
rm_client = resourcemanager.ProjectsClient()

بعد از:

CREDS, PROJ_ID = default(  # Application Default Credentials and project ID
        ['https://www.googleapis.com/auth/cloud-platform'])
rm_client = discovery.build('cloudresourcemanager', 'v1', credentials=CREDS)

تفاوت دیگر این است که کتابخانه کلاینت Resource Manager اشیاء allow-policy را که از نمادگذاری dotted-attribute استفاده می‌کنند، برمی‌گرداند در حالی که کتابخانه کلاینت سطح پایین‌تر، دیکشنری‌های پایتون را که در آنها از براکت‌های مربعی ( [ ] ) استفاده می‌شود، برمی‌گرداند، برای مثال، binding.role برای کتابخانه کلاینت Resource Manager در مقابل binding['role'] برای کتابخانه سطح پایین‌تر استفاده کنید. اولی همچنین از نام‌های "underscore_separated" در مقابل کتابخانه سطح پایین‌تر که نام‌های "CamelCased" را ترجیح می‌دهد، به علاوه روشی کمی متفاوت برای ارسال پارامترهای API استفاده می‌کند.

این تفاوت‌های کاربرد در زیر نشان داده شده است:

قبل از:

allow_policy = rm_client.get_iam_policy(resource='projects/%s' % PROJ_ID)
for b in allow_policy.bindings:     # bindings in IAM allow-policy
    if b.role in _TARGETS:          # only look at GAE admin roles
        admins.update(user.split(':', 1).pop() for user in b.members)

بعد از:

allow_policy = rm_client.projects().getIamPolicy(resource=PROJ_ID).execute()
for b in allow_policy['bindings']:  # bindings in IAM allow-policy
    if b['role'] in _TARGETS:       # only look at GAE admin roles
        admins.update(user.split(':', 1).pop() for user in b['members'])

Putting all these changes together, replace the Python 3 _get_gae_admins() with this equivalent Python 2 version:

def _get_gae_admins():
    'return set of App Engine admins'
    # setup constants for calling Cloud Resource Manager API
    CREDS, PROJ_ID = default(  # Application Default Credentials and project ID
            ['https://www.googleapis.com/auth/cloud-platform'])
    rm_client = discovery.build('cloudresourcemanager', 'v1', credentials=CREDS)
    _TARGETS = frozenset((     # App Engine admin roles
            'roles/viewer',
            'roles/editor',
            'roles/owner',
            'roles/appengine.appAdmin',
    ))

    # collate users who are members of at least one GAE admin role (_TARGETS)
    admins = set()                      # set of all App Engine admins
    allow_policy = rm_client.projects().getIamPolicy(resource=PROJ_ID).execute()
    for b in allow_policy['bindings']:  # bindings in IAM allow-policy
        if b['role'] in _TARGETS:       # only look at GAE admin roles
            admins.update(user.split(':', 1).pop() for user in b['members'])
    return admins

The is_admin() function doesn't require any updates because it relies on _get_gae_admins() which has already been updated.

This concludes the changes required to backport the Python 3 Module 21 app to Python 2. Congratulations for arriving at your updated Module 21 sample app! You'll find all the code in the Module 21a repo folder .

7. Summary/Cleanup

The last steps in the codelab are to ensure principals (users or service accounts) running this app have the proper permissions to do so, then deploy your app to confirm it works as intended and the changes are reflected in the output.

Ability to read IAM allow-policy

Earlier, we introduced you to the four roles required to be recognized as an App Engine admin user, but there is now a fifth to become familiar with:

  • roles/viewer
  • roles/editor
  • roles/owner
  • roles/appengine.appAdmin
  • roles/resourcemanager.projectIamAdmin (for principals accessing the IAM allow-policy)

The roles/resourcemanager.projectIamAdmin role enables principals to determine whether an end-user is a member of any of the App Engine admin roles. Without membership in roles/resourcemanager.projectIamAdmin , calls to the Cloud Resource Manager API to get the allow-policy will fail.

You do not need to take any explicit action here as your app will run under App Engine's default service account which is automatically granted membership in this role. Even if you use the default service account during the development phase, we strongly recommend creating and using a user-managed service account with the minimal permissions required for your app to function properly. To grant membership to such a service account, run the following command:

$ gcloud projects add-iam-policy-binding PROJ_ID --member="serviceAccount:USR_MGD_SVC_ACCT@PROJ_ID.iam.gserviceaccount.com" --role=roles/resourcemanager.projectIamAdmin

PROJ_ID is the Cloud project ID and USR_MGD_SVC_ACCT@PROJ_ID.iam.gserviceaccount.com is the user-managed service account you create for your app. This command outputs the updated IAM policy for your project where you can confirm the service account has membership in roles/resourcemanager.projectIamAdmin . For more information, see the reference documentation . To repeat, you don't need to issue that command in this codelab, but save this as a reference for modernizing your own apps.

Deploy and verify application

Upload your app to the cloud with the standard gcloud app deploy command. Once deployed, you should see functionality almost identical to the Module 20 app except that you've successfully replaced the App Engine Users service with the Cloud Identity Platform (and Firebase Auth) for user management:

3a83ae745121d70.png

One difference you'll notice compared to Module 20 is that clicking on the Login results in a popup instead of a redirect, captured in some of the screenshots below. Like Module 20 however, the behavior differs slightly depending on how many Google accounts have been registered with the browser.

If there are no users registered with the browser or a single user who hasn't signed in yet, a generic Google Sign-in popup comes up:

8437f5f3d489a942.png

If a single user is registered with your browser but signs-in elsewhere, no dialog appears (or it pops up and closes immediately), and the app goes into a signed-in state (displays the user email and Logout button).

Some developers may want to provide an account-picker, even for a single user:

b75624cb68d94557.png

To implement this, uncomment the provider.setCustomParameters({prompt: 'select_account'}); line in the web template as described earlier.

If there are multiple users, the account-picker dialog pops up (see below). If not signed-in yet, the user will be prompted. If already signed-in, the popup disappears, and the app goes into a signed-in state.

c454455b6020d5e4.png

The signed-in state of Module 21 looks identical to Module 20's user interface:

49ebe4dcc1eff11f.png

The same is true for when an admin user has signed-in:

44302f35b39856eb.png

Unlike Module 21, Module 20 always accesses the logic for the web template content from the app (server-side code). A flaw of the Module 20 is that one visit is registered when the end-user hits the app the first time, and another one is registered when a user signs-in.

For Module 21, the login logic takes place in just the web template (client-side code). There is no required server-side trip to determine what content to display. The only call made to the server is the check for admin users after an end-user signs-in. This means that logins and logouts don't register additional visits, so the most recent visits list stays constant for user management actions. Notice the screenshots above display the same set of four visits across multiple user logins.

The Module 20 screenshots demonstrate the "double-visit bug" at the beginning of this codelab. Separate visits logs are displayed for each sign-in or sign-out action. Check the timestamps of the most recent visit for each screenshot displaying the chronological ordering.

تمیز کردن

عمومی

If you are done for now, we recommend you disable your App Engine app to avoid incurring billing. However if you wish to test or experiment some more, the App Engine platform has a free quota , and so as long as you don't exceed that usage tier, you shouldn't be charged. That's for compute, but there may also be charges for relevant App Engine services, so check its pricing page for more information. If this migration involves other Cloud services, those are billed separately. In either case, if applicable, see the "Specific to this codelab" section below.

For full disclosure, deploying to a Google Cloud serverless compute platform like App Engine incurs minor build and storage costs . Cloud Build has its own free quota as does Cloud Storage . Storage of that image uses up some of that quota. However, you might live in a region that does not have such a free tier, so be aware of your storage usage to minimize potential costs. Specific Cloud Storage "folders" you should review include:

  • console.cloud.google.com/storage/browser/LOC.artifacts.PROJECT_ID.appspot.com/containers/images
  • console.cloud.google.com/storage/browser/staging.PROJECT_ID.appspot.com
  • The storage links above depend on your PROJECT_ID and LOC ation, for example, " us " if your app is hosted in the USA.

On the other hand, if you're not going to continue with this application or other related migration codelabs and want to delete everything completely, shut down your project .

Specific to this codelab

The services listed below are unique to this codelab. Refer to each product's documentation for more information:

مراحل بعدی

Beyond this tutorial, other migration modules that focus on moving away from the legacy bundled services to consider include:

  • Module 2 : migrate from App Engine ndb to Cloud NDB
  • Modules 7-9 : migrate from App Engine Task Queue (push tasks) to Cloud Tasks
  • Modules 12-13 : migrate from App Engine Memcache to Cloud Memorystore
  • Modules 15-16 : migrate from App Engine Blobstore to Cloud Storage
  • Modules 18-19 : migrate from App Engine Task Queue (pull tasks) to Cloud Pub/Sub

App Engine is no longer the only serverless platform in Google Cloud. If you have a small App Engine app or one that has limited functionality and wish to turn it into a standalone microservice, or you want to break-up a monolithic app into multiple reusable components, these are good reasons to consider moving to Cloud Functions . If containerization has become part of your application development workflow, particularly if it consists of a CI/CD (continuous integration/continuous delivery or deployment) pipeline, consider migrating to Cloud Run . These scenarios are covered by the following modules:

  • Migrate from App Engine to Cloud Functions: see Module 11
  • Migrate from App Engine to Cloud Run: see Module 4 to containerize your app with Docker, or Module 5 to do it without containers, Docker knowledge, or Dockerfile s

Switching to another serverless platform is optional, and we recommend considering the best options for your apps and use cases before making any changes.

Regardless of which migration module you consider next, all Serverless Migration Station content (codelabs, videos, source code [when available]) can be accessed at its open source repo . The repo's README also provides guidance on which migrations to consider and any relevant "order" of Migration Modules.

8. Additional resources

Listed below are additional resources for developers exploring this or related migration modules further. Below, you can provide feedback on this content, find links to the code, and various pieces of documentation you may find useful.

Codelabs issues/feedback

If you find any issues with this codelab, please search for your issue first before filing. Links to search and create new issues:

Migration resources

Links to the repo folders for Module 20 (START) and Module 21 (FINISH) can be found in the table below.

Codelab

Python 2

Python 3

Module 20

code

(n/a)

Module 21 (this codelab)

code

code

Online references

Below are resources relevant for this tutorial:

Cloud Identity Platform and Cloud Marketplace

Cloud Resource Manager, Cloud IAM, Firebase Admin SDK

App Engine Users, App Engine NDB, Cloud NDB, Cloud Datastore

Other Migration Module references

App Engine migration

App Engine platform

Cloud SDK

Other Cloud information

ویدیوها

مجوز

This work is licensed under a Creative Commons Attribution 2.0 Generic License.