سلام دوست عزیز😃👋

به مسابقه «هم‌کد ۵ - Front-end» خوش آمدی!

هرگونه ارتباط با سایر شرکت‌کنندگان و یا استفاده از ابزارهای تولید کد، مثل chatGPT و... در مسابقات کوئرا ممنوع است و بعد از شناسایی از لیست شرکت‌کنندگان مسابقه حذف می‌شوید. سوالات و مشکلات خودتان را می‌توانید از طریق قسمت «سوال بپرسید» با ما در میان بگذارید.

سوال اول صرفاً برای آموزش کار با سیستم داوری است و هیچ تاثیری در نتیجه‌ی مسابقه ندارد.

در صورت امکان انتخاب زبان، حل سوال با TypeScript برای استخدام اولویت بالاتری دارد.

موفق باشید و بهتون خوش بگذره 😉✌

اتاق‌های خالی از اعتماد


ظاهر برنامه

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

ساختار فایل‌های پروژه

شما تنها مجاز به انجام تغییرات در فایل‌های علامت زده می‌باشید

Zero-Trust-Rooms
├── index.html
├── package.json
├── postcss.config.js
├── server
│   ├── index.js
│   └── package.json
├── src
│   ├── encryption.ts
│   ├── main.ts
│   ├── memory.ts
│   ├── room.ts
│   ├── socket.ts
│   ├── style.css
│   └── vite-env.d.ts
├── tailwind.config.js
└── tsconfig.json

3 directories, 14 files                                                                            
Plain text
راه اندازی پروژه و سرور
  • فایل‌های پروژه اولیه را دانلود و از حالت فشرده خارج نمایید
  • سپس دستور زیر را برای نصب پکیج‌ها و وابستگی‌ها در پوشه پروژه درون ترمینال خود وارد نمایید: ‍‍
    cd initial && npm i
    
    Bash
- در نهایت برای راه اندازی سرور سوال دستور زیر را وارد نمایید:

```bash
cd server && npm i
node index.js
Plain text
نکات

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

منطق و پیاده سازی الگوریتم‌ها🔗

  • پیاده سازی مربوطه توابع و کلاس‌ها که امضای آن‌ها درون هر فایل به صورت مجزا قرار داده شده است.
  • شما موظف هستید که تابع را بر حسب خواسته آن بخش تکمیل نمایید.
  • رعایت کردن تایپ‌ها الزامی است!
  • تمامی توابع، متغیر‌ها و کلاس‌هایی که export شده‌اند و یا دارای متدهای عمومی (public) می‌باشند، توسط سیستم داوری بررسی و نمره دهی می‌شوند.

پیاده سازی رابط کاربری🔗

  • ساختاری کلی رابط کاربری از قبل با استفاده از کتابخانه tailwind پیاده شده است.
  • برای بعضی از بخش‌ها (مانند نمایش پیام) یک ساختار کلی برای شما تدارک دیده شده است و می‌توانید از آن استفاده نمایید.
  • در بخش‌هایی که شروط خطا ذکر شده است، باید پیغام خطا درون div#logs نمایش داده شوند.
  • پیاده سازی این بخش اجباری بوده و توسط سیستم داوری خودکار نمره دهی‌ می‌شود.

کتابخانه‌های مورد استفاده و مجاز در این تمرین🔗

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

مقدمه🔗

ماجرا از این قرار است که مهیار نه تنها به هیچ‌کس، حتی خودش اعتماد ندارد بلکه به نرم‌افزارها نیز اعتماد ندارد. به همین منظور، قصد دارد یک پیام‌رسان بسیار امن با رمزگذاری سرتاسری (End To End Encryption یه به اختصار E2EE) راه‌اندازی کند. در این پیامرسان پیام‌ها به صورت کاملا رمزگذاری شده به ازای هر فرد بین کاربران ارسال می‌شود.

از دیگر قابلیت‌های این پیامرسان امکان ایجاد گروه است! حتی چت‌های شخصی در قالب یک گروه صورت می‌گیرند، در این پیامرسان از گروه‌ها به عنوان اتاق (Room) یاد ‌می‌شود.

همچنین این پیامرسان هیچ اطلاعاتی را نه در سمت سرور و نه در سمت کاربر نگه داری نمی‌کند فلذا بدین معنا است که پس از بسته شدن مرورگر توسط کاربر تمامی اطلاعات فراموش‌شده و پس از بازگشت به برنامه باید اطلاعات مورد نیاز مجددا توسط کاربر وارد شوند.

معماری  برنامه

حافظه موقت🔗

به منظور مدیریت متغیرها به شیوه صحیح و با رعایت قید ذکر شده نیاز به پیاده سازی یک حافظه متغیر بسیار ساده “Key-Value Pair” وجود دارد که باید آن را درون فایل memory.ts تکمیل نمایید، این حافظه چیزی جز یکobjectساده نیست که چهار تابع برای مدیریت حافظه مذکور را دار می‌باشد:

  • تابع getMemory‍ که از حافظه موقت یک رونوشت (Copy) را برمیگرداند.
  • تابع memoryForget که با دریافت نام کلید آن را از حافظه موقت حذف می‌کند.
  • تابعmemoryRecall که با دریافت نام کلید مقدار تعیین شده برای آن کلید را بر می‌گرداند، در صورتی که کلید مذکور وجود نداشت باید مقدارundefined برگردانده شود.
  • تابع memoryRemember که با دریافت یک کلید و مقدار که می‌تواند یک متغیر باشد یا یک تابع که به عنوان ورودی مقدار متغیر قبلی را دریافت کرده و مقدار جدیدی را برمی‌گرداند، درون حافظه مقدار کلید را تنظیم و یا باز تنظیم کند.

اطلاعات کاربر🔗

  • برای این که کاربر بتواند از این برنامه طراحی شده استفاده کند باید اطلاعات زیر را درون مرورگر در قالب یک فایل .json با کلید‌های زیر بارگذاری نماید:
  1. نام کاربری که درون فیلدnameوارد می‌شود و از جنس رشته می‌باشد و باید حداقل ۳ کارکتر باشد در صورتی که طول رشته کمتر از ۳ بود باید ارور با پیغام زیر برگردانده شود:
"username" must be at least 3 characters
Plain text
  1. کلید عمومی (Public Key) که درون فیلد public در قالب یک رشته می‌باشد.
  2. کلید شخصی (Private Key) که درون فیلد‍private در قالب یک رشته می‌باشد.
  • در صورتی که کاربر هر یک از فیلدهای زیر را وارد نکرده بود باید اروری با پیغام زیر برگردانده شود:
You did not provide "<FIELD_NAME>"
Plain text
  • همچنین در مورد کلید‌های شخصی و عمومی این رشته کدگذاری شده باید موارد زیر بررسی و صحت سنجی شوند:

  • آیا کلید‌های ارسالی از نوع RSA256 می‌باشد؟ در غیر این صورت اروری با پیغام زیر برگردانده شود

Keys are not RSA256
Plain text
  • آیا کلیدها با یکدیگر همخوانی دارند؟ در صورت کلیدها با یکدیگر همخوانی نداشتند ارور با پیغام زیر برگردانده شود:
Invalid Key Pairs 
Plain text

مثال ورودی🔗

-برای مثال فایلی با محتوای زیر تمامی شرایط فوق را دارا می‌باشد:

{
  "name": "Mahyar",
  "public": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A0UaGNOz5e6QmGl63GgP\nWHw8xFE+r2MYVeGVGP2OD9juhIXg3ePOvSw8sakGprESFz7shkNCeAQ8q+affSeA\n8+u83Gn+twjnxij99nQ+fgdpHZN4WYzruJ2N76kxUYm5oMfYjU+Ao\nfxxl4E+q3UXZAQ/hnq/nNLAylnsxs175eaH0wFf9q80smAcCUaE/0CzRTDaw4bHi\nkFiUcpnVWFM7TiRHFWmNXIF2srAqA+jEPRQ+yjtCuDA9NdmRnAdFLItQI9UdnnV7\nj96hqWXcAgQDlr4NTyX1aNjaKFzuRz/w6F0P03UOVHP6okUGp6vjmsv/3sY6AydE\nTwIDAQAB",
  "private": "MIIEowIBAAKCAQEA70UaGNOz5e6QmGl63GgPWHw8xFE+r2MYVeGVGP2OD9juhIXg\n3ePOvSw8sakGprESFz7shkNCeAQ8q+affSeA8+u83Gn+twjnxij99nQ+fgdpHZN4\nWYzruJ2NpWw0Zcm4wdZ76kxUYm5oMfYjU+Aofxxl4E+q3UXZAQ/hnq/nNLAylnsx\ns175eaH0wFf9q80smAcCUaE/0CzRTDaw4bHikFiUcpnVWFM7TiRHFWmNXIF2srAq\nA+jEPRQ+yjtCuDA9NdmRnAdFLItQI9UdnnV7j96hqWXcAgQDlr4NTyX1aNjaKFzu\nRz/w6F0P03UOVHP6okUGp6vjmsv/3sY6AydETwIDAQABAoIBAE0H1zYE+hbdbinT\nU52f1sjq/nlJmr/GTHBhtqmb/990PJ3nqBRnoghfNI8R1rVVrnhTZrx27PBMlOfW\n0KPvUuG0lFH1we5VbKDsU47Dr9bNNfcyof1alA0UlCScqGXYwqWNEsnElyo1V1A2\naPs44HDDyLyliWHBefKQlVeMVgLs4f2QkidpUD5djPx38ObKknmP\nnSm2uVedP+KxC/uD7AwwWIKAw/XrIppWHZE1a+//NwsJOnZ2UwWp4QM41K/RZzgA\nwyMQ3Tl9SDCfNmm4Gs0OmF73V959B7yC4k3iTUJpRnru6wJVrOEIj2LcSUwPbI0c\nxSK7NZECgYEA+S/D4bQ9Afu8PGCide1F5wvtJy852pTsCnUnZLyA59KY9U\nbTvY468OH7le9zdDS93KC0QOEMESw75KJ+Ntd9BH5eW4PP8/7fvGIjAWPest0SFq\nzuHznchRYhs09/0BFGbGg2/FxeRUYazQ9sPy94+E9VRGlA14cph5DAkCgYEA9c/s\nTG7mJnuFP+KPJ76llpA68HhtBSmCYJLyr/jhqTOPIMQ+qQlENm2tc/zkMF43ciE6\n2wbXLteq/FKcGIbK/vXiCka1RCm9bbZ/be4IV9prdvm3MuTKUKjJQOdg2adwlM0Q\nRm+jvyr65GZ6U/IEgN519+cNfEE/ICwX3ikYk5cCgYEAw3j2vg03dmDVLr/Xihhl\n8MxtlUZoRyirR/O7pq12FDf8BnlnMbzfy7iQ0Tq3Z6dwmZleTxsyrVe7eOUcJTUW\n0ueTLdPTMsLbne3VZoiEyrjSGcxs6oq+UH/rLTDAtqcFDCwKNsjQnLaHfE866kw4\nt/rKk+yfrhtoKifO/oYhHpECgYB4YUYovFrL18wO2EYhQbVYQfQLVf6V+5kU8IIX\nebwRn+VlN5Y0NAyK1zWNyvh5U3q10t4+61+wWgArp804GbMKH6J+Q9h7tDFHrItF\na7/8PkRItMxqCS+nRrGgrm94yA5TYQWqaPv5H/RUrHcV6z0XW1rMeAPbqRxgBhf+\nPYQCqwKBgAfn3oxLjx0mSjtr2iDIaMHoZQq5Dl97ac3+nMpqEUJVS/F6bHRE4eTz\ni/NrMSJzwuMOrMFVBKpnDc9STsRuv+oGvoxeXA4taGlJVj5lAa83b0PEKCQV9Uzn\nk4K/8DCEKqLQPtXQI0Xdszx344RLINELoVm3p3jSFEyaOTK40uvp"
}
JSON

پیاده سازی🔗

برای تکمیل این قسمت شما باید موارد زیر را تکمیل نمایید

فایل encrypt.ts🔗

در این فایل شما باید isValidDataاز کلاس EncryptionService را تکمیل نمایید بدین نحو که با دریافت کردن اطلاعات کاربر در قالب یک Object ، خطاها را در صورت وجود در قالب یک آرایه شامل رشته‌ها برگرداند و درصورتی که هیچ مشکلی در داده ورودی نبود آرایه خالی برگرداند.

فایل main.ts🔗

در این فایل شما باید قابلیتی را پیاده سازی کنید در هنگامی که کاربر فایل جدید را در #file-upload بارگذاری کرد آن را صحت سنجی کرده و آن را درون حافظه با کلید credentials ذخیره نماید. و در صورت بروز خطا، خطاها را به کاربر نمایش دهد. همچنین در صورتی که فایل دارای مشکل بود باید پیغام زیر به کاربر نمایش داده شود:

File is Not Valid
Plain text

اتاق‌هایی برای گفت‌و‌گو🔗

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

ایجاد و عضویت🔗

همان‌طور که ذکر شد، کاربر برای ایجاد و یا عضویت در اتاق باید نام اتاق (roomName) به همراه گذرواژه (password) و کلید عمومی خود را در قالب یک شی به سمت سرور ارسال کند:

interface Request{
    roomName: string;
    password: string;
    publicKey: string;
}
TypeScript

مسیر /api/join/🔗

در گام اول، کاربر اطلاعات مذکور را به آدرس /api/join/ ارسال می‌کند و با یکی از سه سناریوی زیر مواجه می‌شود:

  1. در صورتی که کاربر اطلاعات را به درستی وارد کرده باشد ولی با خطای 404 مواجه شود، باید همان اطلاعات را به مسیر /api/create/ ارسال کند و پس از ایجاد، مجدداً درخواست اول تکرار و ارسال شود.

  2. در صورتی که گذرواژه به اشتباه وارد شده باشد، باید پیغامی با محتوای زیر در div#request-status نمایش داده شود:

Bad Credentials!
Plain text
  1. در صورتی که کاربر امکان دسترسی به گروه درخواست شده را نداشته باشد، در همان div#request-status پیغام زیر نمایش داده شود:
Unauthorized Access!
Plain text

مسیر /api/create/🔗

کاربر اطلاعات زیر را به آدرس /api/create/ ارسال می‌کند تا یک اتاق جدید ایجاد کند:


interface CreateRequest {
    groupName: string;
    password: string;
    publicKey: string;
}
TypeScript

اگر ایجاد اتاق موفقیت‌آمیز باشد، سرور یک پیام موفقیت با محتوای زیر به کاربر بازمی‌گرداند:

Room created successfully!
Plain text

در غیر این صورت، باید خطای دریافتی از سمت سرور درو div#request-status‍‍ نمایش داده شود.

مدیریت اتاق🔗

الزاما داشتن نام اتاق و گذرواژه آن راه مطلوبی برای تضمین امنیت گروه‌ها نیست زیرا در صورتی که اطلاعات حساس اتاق وصل شود هر فردی می‌تواند به این اتاق دسترسی پیدا کند به همین منظور مهیار تصمیم گرفت که برای مدیریت این قضیه قابلیتی اضافه کند تا افرادی که دارای صلاحیت هستند را با استفاده از کلید عمومی آن‌ها به گروه اضافه کند برای این منظور باید به درخواست زیر ارسال شود:

// PUT /api/room/
interface AddUserRequest {
    Request;
    user: string; // new user public key
}
Plain text
  • در صورتی که اتاق وجود نداشت باید خطایی با پیغام زیر برگردانده شود:
Room Not Found!
Plain text
  • از آنجایی که فقط فردی که گروه را ایجاد کرده می‌تواند افراد جدید را اضافه کند، در صورتی که کاربر، ایجاد کننده اتاق نباشد باید خطایی با پیغام زیر برگردانده شود:
Forbidden: Not the owner of the room
Plain text

پیاده سازی🔗

  • موارد خواسته شده در خصوص API ها و ارتباط با سرور را در فایل room.ts تکمیل نمایید
  • همچنین برای رابط کاربری فایل main.ts مطابق فیلدهایی که در فایل index.html عملکرد مطلوب را پیاده سازی کنید

اتصال به سوکت برای ارسال و دریافت پیام🔗

برای ارتباط بلادرنگ، کاربران باید به یک WebSocket متصل شوند که پیاده سازی آن در فایل socket.ts باید صورت بگیرد. برای این کار، باید از آدرس زیر استفاده شود و باید token حاصل شده از بخش قبلی را در بخش احراز هویت Header ارسال نماید:

/ws/room/<ROOM_NAME>/
Plain text

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

شیوه ارسال پیام🔗

  • در گام صفرم کاربر باید محتوای پیام خود را درون input مربوطه بنویسید و سپس دکمه Send را فشرده تا روند اصلی ارسال آغاز شود.
  • در گام اول باید به ازای تمامی کاربران موجود در اتاق (به غیر از خودتان) پیام ارسالی مورد نظر آن‌ها را با توجه به کلید عمومی هر کاربر رمزگزاری کنید.
  • در گام بعد جهت اینکه مشخص شود این پیام توسط شما ارسال شده است باید آن را با استفاده از کلید خود امضا کنید.
  • در گام بعدی با توجه به به پیام رمزنگاری شده برای دیگر کاربران باید آن را در قالب یک رخداد message پیام رمزگذاری شده را به همراه امضا در قالب یک شی به همراه شناسه کاربر مورد نظر ارسال نمایید.

شیوه دریافت پیام🔗

  • پس از دریافت یک پیام در گام اول باید این امر صحت سنجی شود که با توجه به اطلاعات دریافتی (پیام رمزگذاری شده و امضای مذکور) آیا این پیام واقعا از آن کاربر آمده است یا خیر؟ در غیر این صورت اروری تحت عنوان زیر نمایش داده شود
    Message verification failed for sender <SENDER_USERNAME>
    Plain text
  • در گام بعد باید پیام دریافت شده باید با توجه به کلید شخصی کاربری که پیام را دریافت کرده است رمزگشایی شده و بازگردانده شود.
ارسال پاسخ برای این سؤال
در حال حاضر شما دسترسی ندارید.