rbac-react-system
v1.0.4
Published
Enterprise-grade Role-Based Access Control library for React with Clean Architecture
Maintainers
Readme
rbac-react-system
Библиотека управления доступом на основе ролей (RBAC) для React с чистой архитектурой
Версия: 1.0.4 Дата: 2025-11-30 Автор: TM Project Лицензия: MIT
🎯 Функции
- Чистая архитектура - Полное разделение бизнес-логики от React фреймворка
- TypeScript - Полная типизация со строгим режимом
- Без зависимостей - Только React как peer dependency
- Гибкий API - Несколько способов использования (хуки, HOC, компоненты)
- Продвинутое RBAC - Наследование ролей, условные разрешения, динамические условия
- Tree Shakable - Оптимизированные ES модули с минимальным размером бандла
- SSR совместимость - Работает со server-side rendering
- Двойной формат - Поддержка ESM и CommonJS
📦 Установка
npm install rbac-react-systemyarn add rbac-react-systempnpm add rbac-react-system🚀 Быстрый старт
1. Настройка провайдера
Оберните приложение компонентом RbacProvider:
import { RbacProvider } from 'rbac-react-system'
import {
MockUserRepository,
MockRoleRepository,
MockPermissionRepository
} from 'rbac-react-system'
const userRepository = new MockUserRepository([
{
id: 'user-1',
roles: ['admin'],
permissions: ['posts:read', 'posts:write'],
attributes: { department: 'IT' }
}
])
const roleRepository = new MockRoleRepository([
{
id: 'admin',
name: 'Администратор',
permissions: [
{ id: 'p1', resource: 'users', action: 'read' },
{ id: 'p2', resource: 'users', action: 'write' }
],
inheritedRoles: ['editor']
}
])
const permissionRepository = new MockPermissionRepository()
export function App() {
return (
<RbacProvider
config={{
userRepository,
roleRepository,
permissionRepository,
initialUserId: 'user-1'
}}
>
<YourApp />
</RbacProvider>
)
}2. Используйте хуки
import { useAccess, usePermission, useRole } from 'rbac-react-system'
function UserProfile() {
const canEditProfile = useAccess('users', 'write')
const hasAdminRole = useRole('admin')
const canDeletePosts = usePermission('posts:delete')
return (
<div>
{canEditProfile && <button>Редактировать профиль</button>}
{hasAdminRole && <AdminPanel />}
{canDeletePosts && <button>Удалить пост</button>}
</div>
)
}3. Используйте компоненты
import { Protected } from 'rbac-react-system'
function Dashboard() {
return (
<div>
<Protected
resource="analytics"
action="view"
fallback={<div>Доступ запрещен</div>}
>
<AnalyticsDashboard />
</Protected>
</div>
)
}4. Используйте HOC
import { withPermission, withRole } from 'rbac-react-system'
const AdminSettings = withRole('admin', {
fallback: <div>Требуется доступ администратора</div>
})(SettingsComponent)
const UserEditor = withPermission('users:write')(EditorComponent)📚 Основные концепции
Архитектура
Библиотека следует принципам Clean Architecture с четырьмя слоями:
┌──────────────────────────────────────┐
│ Слой представления (React) │
│ (Хуки, HOC, Компоненты) │
└──────────────────┬───────────────────┘
│
┌──────────────────▼───────────────────┐
│ Прикладной слой │
│ (Use Cases, Services) │
└──────────────────┬───────────────────┘
│
┌──────────────────▼───────────────────┐
│ Доменный слой │
│ (Сущности, Интерфейсы, Исключения) │
└──────────────────────────────────────┘
▲
┌──────────────────┴───────────────────┐
│ Инфраструктурный слой │
│ (Реализации репозиториев) │
└──────────────────────────────────────┘Сущности
User (Пользователь)
Представляет пользователя в системе RBAC:
interface User {
id: string
roles: string[]
permissions: string[]
attributes: Record<string, unknown>
metadata?: Record<string, unknown>
}Role (Роль)
Группирует разрешения и поддерживает наследование:
interface Role {
id: string
name: string
permissions: Permission[]
inheritedRoles?: string[]
metadata?: Record<string, unknown>
}Permission (Разрешение)
Определяет доступ к ресурсу с возможными условиями:
interface Permission {
id: string
action: string
resource: string
conditions?: Condition[]
metadata?: Record<string, unknown>
}Condition (Условие)
Динамический контроль доступа на основе контекста:
interface Condition {
id: string
type: string
field: string
operator: 'equals' | 'notEquals' | 'contains' | 'greaterThan' | 'lessThan' | 'in' | 'notIn'
value: string | number | boolean | string[] | number[]
metadata?: Record<string, unknown>
}📖 Справочник API
Провайдер
RbacProvider
<RbacProvider config={config}>
{children}
</RbacProvider>Свойства:
config:RbacConfig- Объект конфигурацииuserRepository: РеализацияIUserRepositoryroleRepository: РеализацияIRoleRepositorypermissionRepository: РеализацияIPermissionRepositoryinitialUserId?: ID пользователя для загрузки при инициализации
Хуки
useRbac
Возвращает полный контекст RBAC:
const {
user,
isLoading,
error,
canAccess,
hasRole,
hasPermission,
refreshPermissions,
setUser
} = useRbac()Пример:
function Dashboard() {
const { user, isLoading, canAccess } = useRbac()
if (isLoading) return <Spinner />
const handleDelete = async () => {
const allowed = await canAccess('posts', 'delete')
if (allowed) {
// Удалить пост
}
}
return <button onClick={handleDelete}>Удалить</button>
}useAccess
Проверяет доступ к ресурсу с действием:
const canDelete = useAccess('posts', 'delete')Пример:
function PostActions({ post }) {
const canEdit = useAccess('posts', 'edit')
const canDelete = useAccess('posts', 'delete')
return (
<div>
{canEdit && <button>✏️ Редактировать</button>}
{canDelete && <button>🗑️ Удалить</button>}
</div>
)
}usePermission
Проверяет прямое разрешение:
const hasPermission = usePermission('posts:delete')Пример:
function AdminFeature() {
const hasAdminAccess = usePermission('admin:access')
return hasAdminAccess ? <AdminPanel /> : null
}useRole
Проверяет членство в роли:
const isAdmin = useRole('admin')Пример:
function UserGreeting() {
const isAdmin = useRole('admin')
const isEditor = useRole('editor')
return (
<div>
<p>Добро пожаловать!</p>
{isAdmin && <p>Вы администратор 👑</p>}
{isEditor && <p>Вы редактор 📝</p>}
</div>
)
}Компоненты
Protected
Условный рендеринг на основе разрешений:
<Protected
resource="users"
action="read"
fallback={<AccessDenied />}
loading={<Spinner />}
>
<UserList />
</Protected>Свойства:
resource: string - Название ресурсаaction: string - Название действияfallback?: ReactNode - Рендерится когда доступ запрещенloading?: ReactNode - Рендерится во время проверки доступаchildren: ReactNode - Рендерится когда доступ разрешен
Пример:
function SettingsPanel() {
return (
<div>
<Protected resource="settings" action="read">
<h2>Параметры</h2>
<SettingsList />
</Protected>
<Protected
resource="settings"
action="write"
fallback={<p>Редактирование недоступно</p>}
>
<SettingsEditor />
</Protected>
</div>
)
}HOC (Higher-Order Components)
withPermission
withPermission<Props>(
permission: string,
options?: { fallback?: ReactNode }
)Пример:
const AdminPanel = withPermission('admin:access', {
fallback: <div>⛔ Только для администраторов</div>
})(AdminComponent)
// Использование:
<AdminPanel someProps="value" />withRole
withRole<Props>(
role: string,
options?: { fallback?: ReactNode }
)Пример:
const EditorPanel = withRole('editor', {
fallback: <div>⛔ Только для редакторов</div>
})(EditorComponent)
// Использование:
<EditorPanel someProps="value" />🔧 Продвинутое использование
Custom репозиторий (REST API)
Замените mock репозитории на ваши собственные реализации:
import { IUserRepository, User } from 'rbac-react-system'
class ApiUserRepository implements IUserRepository {
async findUserById(userId: string): Promise<User | null> {
try {
const response = await fetch(`/api/users/${userId}`)
if (!response.ok) return null
return await response.json()
} catch (error) {
console.error('Failed to fetch user:', error)
return null
}
}
async getUserRoles(userId: string): Promise<string[]> {
try {
const response = await fetch(`/api/users/${userId}/roles`)
return await response.json()
} catch (error) {
console.error('Failed to fetch roles:', error)
return []
}
}
async updateUser(userId: string, userData: Partial<User>): Promise<User> {
const response = await fetch(`/api/users/${userId}`, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(userData)
})
return await response.json()
}
}Условные разрешения (Conditions)
Определите разрешения с условиями:
const permission = {
id: 'edit-own-posts',
resource: 'posts',
action: 'edit',
conditions: [
{
id: 'c1',
type: 'attribute-based',
field: 'department',
operator: 'equals',
value: 'Content'
}
]
}Пример с временными ограничениями:
const permission = {
id: 'docs-read-business-hours',
resource: 'documents',
action: 'read',
conditions: [
{
id: 'time-check',
type: 'time-based',
field: 'currentHour',
operator: 'in',
value: [9, 10, 11, 12, 13, 14, 15, 16, 17] // 9:00 - 17:00
}
]
}Наследование ролей
Создайте иерархию ролей:
const roles = [
{
id: 'viewer',
name: 'Зритель',
permissions: [{ id: 'p1', resource: 'posts', action: 'read' }]
},
{
id: 'editor',
name: 'Редактор',
permissions: [{ id: 'p2', resource: 'posts', action: 'write' }],
inheritedRoles: ['viewer'] // Наследует все разрешения зрителя
},
{
id: 'admin',
name: 'Администратор',
permissions: [{ id: 'p3', resource: 'posts', action: 'delete' }],
inheritedRoles: ['editor'] // Наследует разрешения редактора и зрителя
}
]Динамическое переключение пользователей
function UserSwitcher() {
const { setUser, isLoading } = useRbac()
const switchUser = async (userId: string) => {
await setUser(userId)
}
return (
<select onChange={(e) => switchUser(e.target.value)}>
<option value="user-1">Пользователь 1</option>
<option value="user-2">Пользователь 2</option>
<option value="user-3">Администратор</option>
</select>
)
}Обновление разрешений
function RefreshPermissions() {
const { refreshPermissions, isLoading } = useRbac()
return (
<button onClick={() => refreshPermissions()} disabled={isLoading}>
{isLoading ? 'Обновление...' : 'Обновить разрешения'}
</button>
)
}📝 Примеры
Защищенные маршруты
import { useAccess } from 'rbac-react-system'
import { Navigate } from 'react-router-dom'
function ProtectedRoute({ children, resource, action }) {
const hasAccess = useAccess(resource, action)
if (!hasAccess) {
return <Navigate to="/unauthorized" />
}
return children
}
// Использование
<Route
path="/admin"
element={
<ProtectedRoute resource="admin" action="access">
<AdminDashboard />
</ProtectedRoute>
}
/>Условные элементы UI
function PostActions({ post }) {
const canEdit = useAccess('posts', 'edit')
const canDelete = useAccess('posts', 'delete')
const isAuthor = usePermission(`posts:${post.id}:edit`)
return (
<div>
{canEdit && isAuthor && <button>✏️ Редактировать</button>}
{canDelete && <button>🗑️ Удалить</button>}
<button>👁️ Просмотреть</button>
</div>
)
}Разрешения для полей форм
import { Protected } from 'rbac-react-system'
function UserForm() {
return (
<form>
<input name="name" placeholder="Имя" />
<input name="email" placeholder="Email" />
<Protected resource="users" action="change-role">
<select name="role">
<option value="user">Пользователь</option>
<option value="editor">Редактор</option>
<option value="admin">Администратор</option>
</select>
</Protected>
<Protected resource="users" action="change-status">
<select name="status">
<option value="active">Активный</option>
<option value="blocked">Заблокирован</option>
</select>
</Protected>
<button type="submit">Сохранить</button>
</form>
)
}Матрица разрешений
function PermissionMatrix() {
const { canAccess } = useRbac()
const [matrix, setMatrix] = useState({})
useEffect(() => {
const checkPermissions = async () => {
const result = {}
const resources = ['posts', 'users', 'settings']
const actions = ['read', 'write', 'delete']
for (const resource of resources) {
result[resource] = {}
for (const action of actions) {
result[resource][action] = await canAccess(resource, action)
}
}
setMatrix(result)
}
checkPermissions()
}, [canAccess])
return (
<table>
<thead>
<tr>
<th>Ресурс</th>
<th>Read</th>
<th>Write</th>
<th>Delete</th>
</tr>
</thead>
<tbody>
{Object.entries(matrix).map(([resource, actions]) => (
<tr key={resource}>
<td>{resource}</td>
<td>{actions.read ? '✅' : '❌'}</td>
<td>{actions.write ? '✅' : '❌'}</td>
<td>{actions.delete ? '✅' : '❌'}</td>
</tr>
))}
</tbody>
</table>
)
}📂 Папка примеров
В папке examples/ находятся подробные примеры для каждого сценария:
1-basic-setup.tsx - Базовая настройка ⭐
- Создание провайдера
- Mock репозитории
- Базовая структура данных
2-hooks-usage.tsx - Использование хуков ⭐⭐
- useRbac(), usePermission(), useAccess(), useRole()
- Примеры для каждого хука
3-hoc-and-protected.tsx - HOC и компоненты ⭐⭐
- withPermission, withRole
- Protected компонент
- Комбинированные примеры
4-advanced-scenarios.tsx - Продвинутые сценарии ⭐⭐⭐
- Условия доступа (Conditions)
- Иерархия ролей
- Динамическое переключение пользователей
5-api-integration.tsx - REST API интеграция ⭐⭐⭐
- Custom репозитории
- Кэширование
- Retry логика
- Обработка ошибок
6-testing-examples.ts - Тестирование ⭐⭐⭐
- Unit тесты
- Integration тесты
- Mock setup
7-error-handling.tsx - Обработка ошибок ⭐⭐
- Error Boundary
- Graceful degradation
- Error logger
📖 Полное руководство по примерам: examples/README.md
📑 Полный индекс примеров: examples/INDEX.md
💻 TypeScript поддержка
Библиотека полностью типизирована:
import type {
User,
Role,
Permission,
Condition,
IUserRepository,
IRoleRepository,
IPermissionRepository,
RbacConfig,
RbacContextValue
} from 'rbac-react-system'⚡ Производительность
- Мемоизация - Все хуки используют React memoization
- Ленивая загрузка - Разрешения загружаются по требованию
- Минимальные перерисовки - Обновления контекста оптимизированы
- Tree Shaking - Импортируйте только то, что нужно
🌍 Поддерживаемые браузеры
- Chrome (последняя версия)
- Firefox (последняя версия)
- Safari (последняя версия)
- Edge (последняя версия)
🧪 Локальное тестирование
Хотите протестировать библиотеку в своем проекте БЕЗ публикации в npm?
- 🚀 QUICK_START_LOCAL.md - Быстрый старт (5 минут)
- 📚 LOCAL_TESTING.md - Полное руководство
Самый быстрый способ:
# В библиотеке
npm run pack:local
# В вашем проекте
npm install /path/to/rbac-react-system/rbac-react-system-1.0.4.tgzДля активной разработки (рекомендуется):
# Установите yalc (один раз)
npm install -g yalc
# В библиотеке
npm run yalc:setup
# В вашем проекте
yalc add rbac-react-system
# При изменениях - автоматическое обновление
npm run dev:watch🤝 Вклад в проект
Мы приветствуем вклад в проект!
Для разработчиков:
- 📖 CONTRIBUTING.md - Полное руководство для разработчиков
- ⚡ DEVELOPMENT.md - Быстрый старт для разработки
- 🧪 LOCAL_TESTING.md - Локальное тестирование
Процесс:
- Fork проекта
- Создайте ветку (
git checkout -b feature/amazing-feature) - Внесите изменения и добавьте тесты
- Убедитесь, что тесты проходят (
npm test) - Закоммитьте (
git commit -m 'feat: add amazing feature') - Push в ветку (
git push origin feature/amazing-feature) - Создайте Pull Request
📄 Лицензия
MIT © TM Project
🔗 Ссылки
📚 Дополнительная документация
- Спецификация - Полное техническое задание
- Описание архитектуры - Архитектурные решения
- Алгоритмы - Детальные алгоритмы
- CI/CD Pipeline - Настройка и использование автоматической публикации
❓ FAQ
Q: С какого примера начать? A: Начните с 1-basic-setup.tsx для понимания базовой конфигурации.
Q: Как интегрировать с REST API? A: Смотрите 5-api-integration.tsx для примера custom репозиториев.
Q: Как написать тесты? A: Используйте 6-testing-examples.ts как шаблон.
Q: Поддерживается ли наследование ролей? A: Да! Смотрите 4-advanced-scenarios.tsx для примеров.
Q: Как обновить разрешения пользователя?
A: Вызовите refreshPermissions() из хука useRbac().
Спасибо за использование rbac-react-system! 🎉
