Note!
- This README is available in both English and Persian.
- این فایل شامل توضیحات به انگلیسی و فارسی موجود است
- Setup
- A brief explanation
- Entities
- How authentication works
- How authorization system works
- Give access or revoke access
- Modules
- Tools
- Challenges
To get started, you have two options:
Simply run the following command:
./install.sh
This script will set up the environment and start the application.
First, copy .env
file from .env.sample
:
cp .env.sample .env
Next, start the application using Docker Compose:
docker compose up --build -d
Feel free to review and adjust the settings in the .env
file as needed.
The Swagger documentation should be accessible at: http://localhost:4321/api/docs
This app offers comprehensive user management features, allowing administrators to define roles and permissions for different user groups. Users can perform basic CRUD operations to manage users, roles, and groups (permissions). Access to specific endpoints is determined by user groups (roles and permissions), providing a secure and customizable experience.
List of entities:
-
User: Represents a user of the application. Each user has a unique id, name, username, and password. Users are associated with a Role and can belong to multiple Groups.
-
Role: Defines the roles within the application. Roles have attributes such as name, luckyNumber (just for fun!), type, and priority. Each role can be associated with multiple users and groups.
-
Group: Represents a group of users with similar permissions. Groups have attributes like name, scopes, permissions, and isDefault. Each group is associated with a single role and can have multiple users.
-
UserGroup: Represents the relationship between users and groups. It contains the userId and groupId to establish a many-to-many relationship between users and groups.
- Authentication is required for all routes except sign-in and sign-up.
- Users authenticate using a unique username and password.
- Upon successful authentication, the user receives an access token and a refresh token.
- The access token grants access to all other routes.
- Authorization is then enforced to regulate user access to specific endpoints and functionalities.
Now comes the main part. The authorization system is centered around the concept of 'Groups', combining permissions and scopes. There are five permissions available:
- Create
- Read
- Update
- Delete
- All (combines all the above permissions)
Additionally, the application operates within six scopes:
- Auth
- User
- Role
- Group
- Permission (a dummy module)
- All (combines all the above scopes)
A 'Group' is formed by combining a scope with a permission. For example:
- Scope: User
- Permission: Read
- Group: (User : Read)
When such a group is assigned to a user, they gain access to the corresponding routes within the associated module (all get
routes in user
module).
For instance, if user X is assigned to the following groups:
- User: Read
- Auth, Role: Create, Read
Then user X has read
access to the User
module and both create and read
access to the Auth and Role
modules simultaneously.
In this system, access control is synonymous with 'Groups'. If a user is associated with the correct group, they possess the corresponding access rights. Therefore, the only method to grant access to a user is by assigning the appropriate group to them.
By default, only administrators have the authority to create groups. To proceed, simply log in using the administrator account credentials (username: admin, password: 123). Once authenticated, you gain the capability to create the desired groups. After creating a group, you can assign it to any user as needed.
It's essential to note that both the group and the user must belong to the same role category. Groups are designed for specific roles (e.g., admin, manager, employee), and both the user and the group must share the same role designation.
Furthermore, administrators retain the ability to revoke a group from a user entirely. Consequently, users can exist without any assigned group if necessary.
Every module (except Auth
) has these routes:
- Create
- Read single entity
- Read all entities with
pagination
andfilters
- Update
- Delete
Note! We use Redis for caching our data
- After user signs up, hash of their password (using salt and pepper) is saved in database (Bcrypt module)
- There is always one default role. (Now it's
Employee
). After user registered, they get default role and group. So they can access some routes from the beginning but it can change if admin change the default role to any other role with more/less groups (permissions) - We are also use Redis for fast accessing user's access token and refresh token
- There are three routes for updating a user profile:
- User update their own profile
- User update any other user's profile ONLY if they have access
- Administrator can update any user's profile
- By default, only Administrator can delete user. need to mention that by deleting a user, their groups are also deleted
-
As mentioned earlier, there are 5 static user types. Roles can be created and deleted, but they must belong to ONE user type only!
-
Priority: Roles have priorities:
- Administrator: 0 (Highest)
- Manager: 1
- Team leader: 2
- Employee: 3
- Supervisor: 4
This priority system is implemented to prioritize requests. For example, no employee can update any manager's information.
-
All important notes have been mentioned before, the only thing is:
- You can NOT delete the default group (You have to set another group as default first).
- By deleting any group, you are also deleting all user-groups with the same id (You are revoking that group from all users that had it).
My favorite module! It's just a dummy module. no functionality whatsoever.
Its only purpose is to serve as an empty module for testing grouping and assigning groups.
- Framework: NestJS
- Database: PostgreSQL
- In-memory Storage: Redis
- ORM: Prisma
- Documentation: Swagger
Additionally, various custom functions, classes and modules have been developed to enhance the overall process, which can be found in the Libs
folder.
-
Creating a RBAC System
- Problem: I didn't build a full RBAC system before. How can I create a role-based access control (RBAC) system?
- Full Description: I needed to implement an RBAC system to manage access control in my application. However, I lacked prior experience in building a full RBAC system.
- Approach: Spent approximately 1 and a half days researching various methods and selecting the most suitable one for the project requirements.
-
Container Networking
- Problem: How do my containers connect to each other? How can I establish a shared network between containers?
- Full Description: I encountered issues with container networking when trying to connect my application container with PostgreSQL and Redis containers. I'm using PostgreSQL and Redis Docker containers in the application. However, when I dockerized my app, it couldn't connect to the database and Redis by default.
- Approach: Spent approximately 6 hours researching Docker documentation and browsing through community discussions to understand container networking concepts and how to create a shared network so that all containers can communicate with each other.
-
Managing .env File
- Problem: Not copying the
.env
file in time for containers to use. - Full Description: I faced issues with the timing of copying the
.env
file into the container before it was needed by other containers. - Approach: Developed a bash script to ensure the
.env
file is copied before executing other commands, ensuring that containers have access to the required environment variables.
- Problem: Not copying the
🐼 Any contributions aimed at enhancing the system is welcome 🐼
- راه اندازی
- توضیح مختصر
- موجودیت ها
- چگونگی کار احراز هویت
- چگونگی کار سیستم مجوز سنجی
- انتصاب و لغر دسترسی
- ماژول ها
- ابزار
- چالش ها
برای شروع، دو روش زیر را دارید:
به راحتی دستور زیر را اجرا کنید:
./install.sh
ابتدا فایل .env
را از فایل .env.sample
کپی کنید:
cp .env.sample .env
سپس، برنامه را با استفاده از Docker Compose راهاندازی کنید:
docker compose up --build -d
در صورت نیاز به انجام تغییرات، می توانید فایل .env
را بررسی کنید
داکیومنت Swagger باید در ادرس http://localhost:4321/api/docs
قابل دسترسی باشد.
این برنامه امکانات مدیریت جامع کاربر را فراهم میکند و به مدیران اجازه میدهد تا نقشها و مجوزها را برای گروههای کاربری مختلف تعریف کنند. کاربران میتوانند عملیات CRUD ابتدایی را برای مدیریت کاربران، نقشها و گروهها (مجوزها) انجام دهند. دسترسی به نقاط پایانی خاص (مسیر) توسط گروههای کاربر (نقشها و مجوزها) تعیین میشود و تجربهای امن و قابل تنظیم را ارائه میدهد.
لیست موجودیت ها:
-
User: کاربر سیستم. هر کاربر دارای آیدی، نام کاربری و کلمه عبور خود بوده و می تواند به یک نقش و چند گروه وصل شود
-
Role: نقش کاربر در سیستم به واسطه این موجودیت تعریف می شود. تمامی نقش ها شامل نام، عدد شانس (صرفا جهت خنده!) نوع و اولویت هستند. همچنین هر نقش می تواند به هر تعداد کاربر و گروه وصل شود
-
Group: هسته مجوز سنجی سیستم. نمایانگر گروه دسترسی های کاربر. هر گروه شامل فیلدهای نام، بخش ها، دسترسی ها است. هر گروه می تواند به یک نقش و چند کاربر وصل شود
-
UserGroup: مدیریت نقش های وصل شده به کاربر. شامل دو فیلد آیدی کاربر و آیدی گروه که رابطه چند به چند این دو موجودیت را مدیریت می کند
- احراز هویت برای همه مسیرها به جز
ورود
وثبت نام
الزامی است - کاربران با استفاده از یک نام کاربری و رمز عبور منحصر به فرد احراز هویت می کنند
- پس از احراز هویت موفقیت آمیز، کاربر یک access token و یک refresh token دریافت می کند
- وجود access token دسترسی به تمام مسیرهای دیگر را ممکن می کند
- احراز هویت برای تنظیم دسترسی کاربر به مسیر های مختلف و عملکردهای خاص اعمال می شود
مهمترین بخش سیستم. سیستم مجوز سنجی حول مفهوم "گروه ها" متمرکز است که مجوزها و ماژول ها را ترکیب می کند. پنج مجوز موجود:
- ایجاد کردن
- خواندن
- ویرایش
- حذف
- همه (ترکیب تمام مجوزهای بالا)
علاوه بر این، برنامه در شش ماژول فعالیت می کند:
- احراز هویت (Auth)
- کاربر (User)
- نقش (Permission)
- گروه (Group)
- مجوز (یک ماژول الکی (!) و خالی) (Permission)
- همه (ترکیب تمام مازول های بالا)
یک "گروه" با ترکیب ماژول با مجوز تشکیل می شود. مثلا:
- ماژول: کاربر
- مجوز: خواندن
- گروه: (کاربر : خواندن)
هنگامی که چنین گروهی به یک کاربری اختصاص داده می شود، کاربر به مسیرهای مربوطه در ماژول مرتبط دسترسی پیدا می کنند (در مثال بالا، تمام مسیرهای «خواندن» در ماژول «کاربر»)
به عنوان مثال، اگر کاربر X به گروه های زیر اختصاص داده شود:
- کاربر: خواندن
- احراز هویت، نقش: ایجاد، خواندن
سپس کاربر X به ماژول "User" دسترسی "خواندن" و به ماژول های "Auth و Role" به طور همزمان، دسترسی "ایجاد کردن و خواندن" را دارد
در این سیستم، کنترل دسترسی مترادف با "گروه ها" است. اگر به یک کاربر گروهی اختصاص داده شود، کاربر دارای حق دسترسی مربوط به آن گروه هست. بنابراین، تنها روش اعطای دسترسی به یک کاربر، اختصاص گروه مناسب به آنها است
بهطور پیشفرض، فقط مدیر حق ایجاد گروه را دارد. می توانید با استفاده از مشخصات حساب مدیر (نام کاربری: admin، رمز عبور: 123) وارد شوید. پس از احراز هویت، توانایی ایجاد گروه های مورد نظر را خواهید داشت. پس از ایجاد یک گروه، می توانید آن را در صورت نیاز به هر کاربری اختصاص دهید.
لازم به ذکر است که هم گروه و هم کاربر باید به یک Role تعلق داشته باشند. گروهها برای نقشهای خاصی طراحی شدهاند (مثلاً مدیرکل، مدیر، کارمند)، و هم کاربر و هم گروه باید نقش یکسانی داشته باشند.
علاوه بر این، مدیران این دسترسی را دارند که یک گروه (یا دسترسی) را به طور کامل از یک کاربر سلب کنند. در نتیجه، کاربران می توانند در صورت لزوم بدون هیچ گروه اختصاص داده شده وجود داشته باشند.
تمام ماژول ها (به جز «Auth») این مسیرها را دارند:
- ساخت
- خواندن یک موجودیت
- خواندن تمام موجودیت ها، دارای «صفحه بندی» و «فیلتر»
- ویرایش
- حذف
نکته! از Redis برای ذخیره اطلاعات استفاده می شود
- پس از ثبت نام، پسورد کاربر هش شده (با استفاده از salt و pepper) و در دیتابیس ذخیره می شود (ماژول Bcrypt
- همیشه یک نقش پیش فرض در سیستم وجود دارد (در حال حاضر «کارمند»). پس از ثبت نام کاربر، نقش و گروه پیش فرض به او انتصاب داده می شود. بنابراین کاربر می تواند پس از ثبت نام به برخی از مسیرها دسترسی داشته باشد اما مدیرکل می تواند نقش پیش فرض را به هر نقش دیگری با گروه های بیشتر/کمتر (مجوزهای بیشتر یا کمتر) تغییر دهد.
- همچنین از Redis برای دسترسی سریع به access token کاربر و refresh token استفاده می شود
- سه مسیر برای به روز رسانی پروفایل کاربر وجود دارد:
- کاربر پروفایل خود را به روز کند
- کاربر (فقط در صورتی که دسترسی داشته باشد) اطلاعات هر کاربر دیگری را به روز کند
- مدیر می تواند اطلاعات هر کاربر را به روز کند
- به طور پیش فرض، فقط مدیر می تواند کاربر را حذف کند. لازم به ذکر است که با حذف یک کاربر، گروه های آنها نیز حذف می شوند
- همانطور که در بالا ذکر شد، 5 نوع کاربر ثابت وجود دارد. نقش ها را می توان ایجاد و حذف کرد، اما آنها باید فقط به یک نوع کاربر تعلق داشته باشند!
- اولویت: نقش ها اولویت دارند:
مدیرکل: 0 (بالاترین)
مدیر: 1
رهبر تیم: 2
کارمند: 3
ناظر: 4
این روش برای داشتن محدودیت در مورد درخواست ها ایجاد شده است. بنابراین هیچ کارمندی نمی تواند اطلاعات مدیر را ویرایش کند!
- تمام نکات مهم قبلا ذکر شده است، بجز:
- نمی توان گروه پیش فرض را حذف کرد (ابتدا باید گروه دیگری را به عنوان پیش فرض تنظیم کنیم)
- هر گروه را که حذف میکنید، دسترسیهایش از تمام کاربرانی که آن گروه و دسترسیهایش را داشتهاند، سلب میشود
تک ماژول محبوبم! فقط یک ماژول ساختگی. بدون هیچ عملکردی در سیستم
تنها هدف وجود این ماژول تست گروه بندی (و بررسی دسترسی ها) توسط شماست (یک ماژول خالی فقط برای تست گروه ها و اختصاص دادن آنها)
- Framework: NestJS
- Database: PostgreSQL
- In-memory Storage: Redis
- ORM: Prisma
- Documentation: Swagger
همچنین بسیاری تابع، کلاس و ماژول های کمکی برای پروسه ساده تر و پیشرفته تر نوشته شده اند که می توانید تمام آنها را در پوشه Libs
بررسی کنید
-
ایجاد یک سیستم RBAC
- مشکل: من قبلا یک سیستم RBAC کامل نساختم. چگونه می توانم یک سیستم کنترل دسترسی مبتنی بر نقش (RBAC) ایجاد کنم؟
- توضیحات کامل: من نیاز به پیاده سازی یک سیستم RBAC برای مدیریت کنترل دسترسی در برنامه خود داشتم. با این حال، من تجربه قبلی در ساخت یک سیستم RBAC کامل رو نداشتم
- رویکرد: تقریباً 1 و نیم روز را صرف تحقیق در مورد روشهای مختلف و انتخاب مناسبترین روش برای نیازهای پروژه کردم
-
ارتباط کانتینر ها
- مشکل: متصل نبودن کانتینر ها با یکدیگر
- توضیحات کامل: هنگام تلاش برای اتصال کانتینر برنامه خود با کانتینرهای PostgreSQL و Redis، با مشکل شبکه کانتینر مواجه شدم. من از کانتینر های PostgreSQL و Redis در برنامه استفاده می کنم. با این حال، وقتی برنامهام را dockerize کردم، به طور پیشفرض نمیتوانست به Postgresql و Redis متصل شود.
- رویکرد: تقریباً 6 ساعت را صرف تحقیق در مستندات Docker و مرور در بحث های community برای درک مفاهیم شبکه کانتینر و نحوه ایجاد یک شبکه اشتراکی کردم تا همه کانتینرها بتوانند با یکدیگر ارتباط برقرار کنند.
-
مدیریت فایل env
- مشکل: به موقع کپی نشدن فایل ".env" برای استفاده در کانتینرها
- توضیحات کامل: قبل از اینکه فایل ".env" کپی شود، باقی کانتینر ها سعی در خواندن آن داشتند
- رویکرد: یک اسکریپت bash نوشتم که فایل ".env" قبل از استفاده کانتینر های دیگر کپی کرده، که از اینکه کانتینرها به متغیرهای محیطی مورد نیاز دسترسی دارند اطمینان داشته باشم
🐼 از هر گونه مشارکتی که با هدف تقویت سیستم انجام شود، استقبال می شود 🐼