najm-auth 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +8 -0
- package/.env.example +6 -0
- package/README.md +501 -0
- package/bun.lock +648 -0
- package/drizzle.config.ts +24 -0
- package/package.json +68 -0
- package/src/database/enums/index.ts +47 -0
- package/src/database/schemas/index.ts +71 -0
- package/src/guards/PermissionGuards.ts +51 -0
- package/src/guards/RoleGuards.ts +104 -0
- package/src/index.ts +60 -0
- package/src/repositories/PermissionRepository.ts +67 -0
- package/src/repositories/RoleRepository.ts +40 -0
- package/src/repositories/TokenRepository.ts +142 -0
- package/src/services/AuthService.ts +50 -0
- package/src/services/CookieService.ts +24 -0
- package/src/services/EncryptionService.ts +19 -0
- package/src/services/PermissionService.ts +113 -0
- package/src/services/RoleService.ts +62 -0
- package/src/services/TokenService.ts +158 -0
- package/src/types/index.ts +107 -0
- package/src/validators/PermissionValidator.ts +65 -0
- package/src/validators/RoleValidator.ts +48 -0
- package/tsconfig.json +16 -0
package/.env.example
ADDED
package/README.md
ADDED
|
@@ -0,0 +1,501 @@
|
|
|
1
|
+
# najm-auth
|
|
2
|
+
|
|
3
|
+
Authentication and authorization library for the najm framework.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- 🔐 **JWT Authentication** - Access and refresh token management
|
|
8
|
+
- 👤 **Role-Based Access Control (RBAC)** - Simple role-based guards
|
|
9
|
+
- 🔑 **Permission-Based Authorization** - Fine-grained permission system with wildcard support
|
|
10
|
+
- 🍪 **Cookie Management** - Secure refresh token cookie handling
|
|
11
|
+
- 🔒 **Password Encryption** - bcrypt password hashing
|
|
12
|
+
- 🎯 **Decorator-Based** - Clean, declarative API using TypeScript decorators
|
|
13
|
+
- 🗄️ **Database Agnostic** - Implement your own repository interfaces
|
|
14
|
+
|
|
15
|
+
## Installation
|
|
16
|
+
|
|
17
|
+
```bash
|
|
18
|
+
npm install najm-auth
|
|
19
|
+
# or
|
|
20
|
+
bun add najm-auth
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Quick Start
|
|
24
|
+
|
|
25
|
+
### 1. Environment Variables
|
|
26
|
+
|
|
27
|
+
Create a `.env` file:
|
|
28
|
+
|
|
29
|
+
```env
|
|
30
|
+
JWT_ACCESS_SECRET=your-secret-access-key
|
|
31
|
+
ACCESS_EXPIRES_IN=15m
|
|
32
|
+
JWT_REFRESH_SECRET=your-secret-refresh-key
|
|
33
|
+
REFRESH_EXPIRES_IN=1y
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### 2. Database Setup
|
|
37
|
+
|
|
38
|
+
Create the required database tables:
|
|
39
|
+
|
|
40
|
+
```sql
|
|
41
|
+
-- Users table
|
|
42
|
+
CREATE TABLE users (
|
|
43
|
+
id UUID PRIMARY KEY,
|
|
44
|
+
email VARCHAR(255) UNIQUE NOT NULL,
|
|
45
|
+
password VARCHAR(255) NOT NULL,
|
|
46
|
+
role_id UUID REFERENCES roles(id),
|
|
47
|
+
status VARCHAR(50),
|
|
48
|
+
created_at TIMESTAMP DEFAULT NOW(),
|
|
49
|
+
updated_at TIMESTAMP DEFAULT NOW()
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
-- Roles table
|
|
53
|
+
CREATE TABLE roles (
|
|
54
|
+
id UUID PRIMARY KEY,
|
|
55
|
+
name VARCHAR(100) UNIQUE NOT NULL,
|
|
56
|
+
description TEXT,
|
|
57
|
+
created_at TIMESTAMP DEFAULT NOW(),
|
|
58
|
+
updated_at TIMESTAMP DEFAULT NOW()
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
-- Permissions table
|
|
62
|
+
CREATE TABLE permissions (
|
|
63
|
+
id UUID PRIMARY KEY,
|
|
64
|
+
name VARCHAR(100) UNIQUE NOT NULL,
|
|
65
|
+
action VARCHAR(50) NOT NULL,
|
|
66
|
+
resource VARCHAR(100) NOT NULL,
|
|
67
|
+
description TEXT,
|
|
68
|
+
created_at TIMESTAMP DEFAULT NOW(),
|
|
69
|
+
updated_at TIMESTAMP DEFAULT NOW()
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
-- Role-Permission junction table
|
|
73
|
+
CREATE TABLE role_permissions (
|
|
74
|
+
role_id UUID REFERENCES roles(id) ON DELETE CASCADE,
|
|
75
|
+
permission_id UUID REFERENCES permissions(id) ON DELETE CASCADE,
|
|
76
|
+
PRIMARY KEY (role_id, permission_id)
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
-- Tokens table
|
|
80
|
+
CREATE TABLE tokens (
|
|
81
|
+
user_id UUID PRIMARY KEY REFERENCES users(id) ON DELETE CASCADE,
|
|
82
|
+
token TEXT NOT NULL,
|
|
83
|
+
expires_at TIMESTAMP NOT NULL
|
|
84
|
+
);
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### 3. Implement Repository Interfaces
|
|
88
|
+
|
|
89
|
+
You need to implement the repository interfaces for your database/ORM:
|
|
90
|
+
|
|
91
|
+
```typescript
|
|
92
|
+
import { Repository } from 'najm-api';
|
|
93
|
+
import { ITokenRepository, TokenData, AuthUser } from 'najm-auth';
|
|
94
|
+
import { db } from './db'; // your database instance
|
|
95
|
+
|
|
96
|
+
@Repository()
|
|
97
|
+
export class TokenRepository implements ITokenRepository {
|
|
98
|
+
declare db: any;
|
|
99
|
+
|
|
100
|
+
async storeRefreshToken(tokenData: TokenData): Promise<any> {
|
|
101
|
+
return await this.db
|
|
102
|
+
.insert(tokens)
|
|
103
|
+
.values(tokenData)
|
|
104
|
+
.onConflictDoUpdate({
|
|
105
|
+
target: tokens.userId,
|
|
106
|
+
set: { token: tokenData.token, expiresAt: tokenData.expiresAt }
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async getRefreshToken(userId: string | number): Promise<string | null> {
|
|
111
|
+
const [token] = await this.db
|
|
112
|
+
.select()
|
|
113
|
+
.from(tokens)
|
|
114
|
+
.where(eq(tokens.userId, userId));
|
|
115
|
+
return token?.token || null;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
async revokeToken(userId: string | number): Promise<any> {
|
|
119
|
+
return await this.db.delete(tokens).where(eq(tokens.userId, userId));
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async isUserExists(userId: string | number): Promise<boolean> {
|
|
123
|
+
const [user] = await this.db
|
|
124
|
+
.select({ id: users.id })
|
|
125
|
+
.from(users)
|
|
126
|
+
.where(eq(users.id, userId));
|
|
127
|
+
return !!user;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
async getRoleNameById(userId: string | number): Promise<string | null> {
|
|
131
|
+
const [role] = await this.db
|
|
132
|
+
.select({ roleName: roles.name })
|
|
133
|
+
.from(users)
|
|
134
|
+
.leftJoin(roles, eq(users.roleId, roles.id))
|
|
135
|
+
.where(eq(users.id, userId));
|
|
136
|
+
return role?.roleName || null;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
async getUserPermissions(userId: string | number): Promise<string[]> {
|
|
140
|
+
const [user] = await this.db
|
|
141
|
+
.select({ roleId: users.roleId })
|
|
142
|
+
.from(users)
|
|
143
|
+
.where(eq(users.id, userId));
|
|
144
|
+
|
|
145
|
+
if (!user?.roleId) return [];
|
|
146
|
+
|
|
147
|
+
const perms = await this.db
|
|
148
|
+
.select({ name: permissions.name })
|
|
149
|
+
.from(rolePermissions)
|
|
150
|
+
.leftJoin(permissions, eq(rolePermissions.permissionId, permissions.id))
|
|
151
|
+
.where(eq(rolePermissions.roleId, user.roleId));
|
|
152
|
+
|
|
153
|
+
return perms.map(p => p.name).filter(Boolean);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
async getUser(userId: string | number): Promise<AuthUser | null> {
|
|
157
|
+
const [user] = await this.db
|
|
158
|
+
.select({
|
|
159
|
+
id: users.id,
|
|
160
|
+
email: users.email,
|
|
161
|
+
status: users.status,
|
|
162
|
+
roleId: users.roleId,
|
|
163
|
+
roleName: roles.name,
|
|
164
|
+
})
|
|
165
|
+
.from(users)
|
|
166
|
+
.leftJoin(roles, eq(users.roleId, roles.id))
|
|
167
|
+
.where(eq(users.id, userId));
|
|
168
|
+
|
|
169
|
+
return user ? { ...user, role: user.roleName } : null;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
### 4. Create Controllers
|
|
175
|
+
|
|
176
|
+
```typescript
|
|
177
|
+
import { Controller, Get, Post, Body, User, Params } from 'najm-api';
|
|
178
|
+
import { isAuth, Role, Permission, AuthService, EncryptionService } from 'najm-auth';
|
|
179
|
+
|
|
180
|
+
@Controller('/api/auth')
|
|
181
|
+
export class AuthController {
|
|
182
|
+
constructor(
|
|
183
|
+
private authService: AuthService,
|
|
184
|
+
private encryptionService: EncryptionService,
|
|
185
|
+
private userService: UserService // Your user service
|
|
186
|
+
) {}
|
|
187
|
+
|
|
188
|
+
@Post('/login')
|
|
189
|
+
async login(@Body() body: { email: string; password: string }) {
|
|
190
|
+
const user = await this.userService.getByEmail(body.email);
|
|
191
|
+
|
|
192
|
+
// Verify password
|
|
193
|
+
const isValid = await this.encryptionService.comparePassword(
|
|
194
|
+
body.password,
|
|
195
|
+
user.password
|
|
196
|
+
);
|
|
197
|
+
|
|
198
|
+
if (!isValid) {
|
|
199
|
+
throw new Error('Invalid credentials');
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Generate tokens
|
|
203
|
+
const tokens = await this.authService.loginUser(user.id);
|
|
204
|
+
|
|
205
|
+
return {
|
|
206
|
+
data: tokens,
|
|
207
|
+
message: 'Login successful',
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
@Get('/refresh')
|
|
212
|
+
async refresh() {
|
|
213
|
+
const tokens = await this.authService.refreshTokens();
|
|
214
|
+
return { data: tokens };
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
@Get('/logout/:id')
|
|
218
|
+
async logout(@Params('id') id: string) {
|
|
219
|
+
await this.authService.logoutUser(id);
|
|
220
|
+
return { message: 'Logged out successfully' };
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
@Get('/me')
|
|
224
|
+
@isAuth()
|
|
225
|
+
async getProfile(@User() user: any) {
|
|
226
|
+
const profile = await this.authService.getUserProfile(user);
|
|
227
|
+
return { data: profile };
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
## Usage Examples
|
|
233
|
+
|
|
234
|
+
### Role-Based Guards
|
|
235
|
+
|
|
236
|
+
```typescript
|
|
237
|
+
import { Controller, Get } from 'najm-api';
|
|
238
|
+
import { isAuth, Role, isAdmin, isTeacher, isStaff } from 'najm-auth';
|
|
239
|
+
|
|
240
|
+
@Controller('/api/users')
|
|
241
|
+
export class UserController {
|
|
242
|
+
// Require authentication
|
|
243
|
+
@Get('/profile')
|
|
244
|
+
@isAuth()
|
|
245
|
+
async getProfile() {
|
|
246
|
+
return { message: 'Authenticated user only' };
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// Require specific role
|
|
250
|
+
@Get('/admin-only')
|
|
251
|
+
@Role('admin')
|
|
252
|
+
async adminRoute() {
|
|
253
|
+
return { message: 'Admin only' };
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Multiple roles allowed
|
|
257
|
+
@Get('/staff-only')
|
|
258
|
+
@Role('admin', 'principal', 'teacher')
|
|
259
|
+
async staffRoute() {
|
|
260
|
+
return { message: 'Staff members only' };
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
// Shorthand decorators
|
|
264
|
+
@Get('/teachers')
|
|
265
|
+
@isTeacher()
|
|
266
|
+
async teachersOnly() {
|
|
267
|
+
return { message: 'Teachers only' };
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
@Get('/admins')
|
|
271
|
+
@isAdmin()
|
|
272
|
+
async adminsOnly() {
|
|
273
|
+
return { message: 'Admins only' };
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
@Get('/all-staff')
|
|
277
|
+
@isStaff()
|
|
278
|
+
async allStaff() {
|
|
279
|
+
return { message: 'All staff members' };
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
### Permission-Based Guards
|
|
285
|
+
|
|
286
|
+
Permissions follow the `action:resource` format (e.g., `read:users`, `write:classes`).
|
|
287
|
+
|
|
288
|
+
```typescript
|
|
289
|
+
import { Controller, Get, Post, Put, Delete } from 'najm-api';
|
|
290
|
+
import { Permission } from 'najm-auth';
|
|
291
|
+
|
|
292
|
+
@Controller('/api/users')
|
|
293
|
+
export class UserController {
|
|
294
|
+
@Get('/')
|
|
295
|
+
@Permission('read:users')
|
|
296
|
+
async listUsers() {
|
|
297
|
+
return { data: [] };
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
@Post('/')
|
|
301
|
+
@Permission('write:users')
|
|
302
|
+
async createUser() {
|
|
303
|
+
return { message: 'User created' };
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
@Put('/:id')
|
|
307
|
+
@Permission('update:users')
|
|
308
|
+
async updateUser() {
|
|
309
|
+
return { message: 'User updated' };
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
@Delete('/:id')
|
|
313
|
+
@Permission('delete:users')
|
|
314
|
+
async deleteUser() {
|
|
315
|
+
return { message: 'User deleted' };
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
```
|
|
319
|
+
|
|
320
|
+
### Wildcard Permissions
|
|
321
|
+
|
|
322
|
+
```typescript
|
|
323
|
+
// Permission patterns:
|
|
324
|
+
// "read:*" - Can read any resource
|
|
325
|
+
// "*:users" - Can perform any action on users
|
|
326
|
+
// "*:*" - Super admin (all permissions)
|
|
327
|
+
|
|
328
|
+
@Controller('/api/admin')
|
|
329
|
+
export class AdminController {
|
|
330
|
+
@Get('/dashboard')
|
|
331
|
+
@Permission('*:*')
|
|
332
|
+
async dashboard() {
|
|
333
|
+
return { message: 'Super admin dashboard' };
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
### Password Hashing
|
|
339
|
+
|
|
340
|
+
```typescript
|
|
341
|
+
import { EncryptionService } from 'najm-auth';
|
|
342
|
+
|
|
343
|
+
@Service()
|
|
344
|
+
export class UserService {
|
|
345
|
+
constructor(private encryptionService: EncryptionService) {}
|
|
346
|
+
|
|
347
|
+
async createUser(data: { email: string; password: string }) {
|
|
348
|
+
// Hash password before storing
|
|
349
|
+
const hashedPassword = await this.encryptionService.hashPassword(data.password);
|
|
350
|
+
|
|
351
|
+
return await this.userRepository.create({
|
|
352
|
+
email: data.email,
|
|
353
|
+
password: hashedPassword,
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
async validatePassword(plainPassword: string, hashedPassword: string) {
|
|
358
|
+
return await this.encryptionService.comparePassword(plainPassword, hashedPassword);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
### Role Management
|
|
364
|
+
|
|
365
|
+
```typescript
|
|
366
|
+
import { RoleService } from 'najm-auth';
|
|
367
|
+
|
|
368
|
+
@Service()
|
|
369
|
+
export class SetupService {
|
|
370
|
+
constructor(private roleService: RoleService) {}
|
|
371
|
+
|
|
372
|
+
async seedRoles() {
|
|
373
|
+
const defaultRoles = [
|
|
374
|
+
{ name: 'admin', description: 'Administrator' },
|
|
375
|
+
{ name: 'teacher', description: 'Teacher' },
|
|
376
|
+
{ name: 'student', description: 'Student' },
|
|
377
|
+
];
|
|
378
|
+
|
|
379
|
+
return await this.roleService.seedDefaultRoles(defaultRoles);
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
```
|
|
383
|
+
|
|
384
|
+
### Permission Management
|
|
385
|
+
|
|
386
|
+
```typescript
|
|
387
|
+
import { PermissionService } from 'najm-auth';
|
|
388
|
+
|
|
389
|
+
@Service()
|
|
390
|
+
export class SetupService {
|
|
391
|
+
constructor(private permissionService: PermissionService) {}
|
|
392
|
+
|
|
393
|
+
async seedPermissions() {
|
|
394
|
+
const defaultPermissions = [
|
|
395
|
+
{ name: 'read:users', action: 'read', resource: 'users' },
|
|
396
|
+
{ name: 'write:users', action: 'write', resource: 'users' },
|
|
397
|
+
{ name: 'delete:users', action: 'delete', resource: 'users' },
|
|
398
|
+
];
|
|
399
|
+
|
|
400
|
+
await this.permissionService.seedDefaultPermissions(defaultPermissions);
|
|
401
|
+
|
|
402
|
+
// Assign permissions to roles
|
|
403
|
+
const rolePermissions = [
|
|
404
|
+
{
|
|
405
|
+
roleName: 'admin',
|
|
406
|
+
permissions: ['*:*'], // Admin gets all permissions
|
|
407
|
+
},
|
|
408
|
+
{
|
|
409
|
+
roleName: 'teacher',
|
|
410
|
+
permissions: ['read:users', 'read:classes'],
|
|
411
|
+
},
|
|
412
|
+
];
|
|
413
|
+
|
|
414
|
+
return await this.permissionService.seedDefaultRolePermissions(rolePermissions);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
## API Reference
|
|
420
|
+
|
|
421
|
+
### Services
|
|
422
|
+
|
|
423
|
+
#### `AuthService`
|
|
424
|
+
- `loginUser(userId)` - Generate tokens for user
|
|
425
|
+
- `refreshTokens()` - Refresh access and refresh tokens
|
|
426
|
+
- `logoutUser(userId)` - Revoke user's refresh token
|
|
427
|
+
- `getUserProfile(userData)` - Get user profile with language
|
|
428
|
+
|
|
429
|
+
#### `TokenService`
|
|
430
|
+
- `generateTokens(userId)` - Generate access and refresh tokens
|
|
431
|
+
- `refreshTokens()` - Refresh both tokens
|
|
432
|
+
- `verifyAccessToken(token)` - Verify access token
|
|
433
|
+
- `verifyRefreshToken(token)` - Verify refresh token
|
|
434
|
+
- `getUserIdByAccessToken(header)` - Extract user ID from token
|
|
435
|
+
- `getUser(auth)` - Get user from access token
|
|
436
|
+
- `getUserRole(auth)` - Get user role from token
|
|
437
|
+
- `getUserPermissions(auth)` - Get user permissions from token
|
|
438
|
+
|
|
439
|
+
#### `EncryptionService`
|
|
440
|
+
- `hashPassword(password)` - Hash password with bcrypt
|
|
441
|
+
- `comparePassword(password, hash)` - Compare password with hash
|
|
442
|
+
|
|
443
|
+
#### `RoleService`
|
|
444
|
+
- `getAll()` - Get all roles
|
|
445
|
+
- `getById(id)` - Get role by ID
|
|
446
|
+
- `getByName(name)` - Get role by name
|
|
447
|
+
- `create(data)` - Create new role
|
|
448
|
+
- `update(id, data)` - Update role
|
|
449
|
+
- `delete(id)` - Delete role
|
|
450
|
+
- `seedDefaultRoles(roles)` - Seed initial roles
|
|
451
|
+
|
|
452
|
+
#### `PermissionService`
|
|
453
|
+
- `getAll()` - Get all permissions
|
|
454
|
+
- `getById(id)` - Get permission by ID
|
|
455
|
+
- `getByName(name)` - Get permission by name
|
|
456
|
+
- `create(data)` - Create permission
|
|
457
|
+
- `update(id, data)` - Update permission
|
|
458
|
+
- `delete(id)` - Delete permission
|
|
459
|
+
- `assignPermissionToRole(roleId, permissionId)` - Grant permission to role
|
|
460
|
+
- `removePermissionFromRole(roleId, permissionId)` - Revoke permission from role
|
|
461
|
+
- `seedDefaultPermissions(permissions)` - Seed initial permissions
|
|
462
|
+
- `seedDefaultRolePermissions(mapping)` - Assign permissions to roles
|
|
463
|
+
|
|
464
|
+
### Guards
|
|
465
|
+
|
|
466
|
+
#### Role Guards
|
|
467
|
+
- `@isAuth()` - Require authentication
|
|
468
|
+
- `@Role(...roles)` - Require specific role(s)
|
|
469
|
+
- `@isAdmin()` - Shorthand for admin role
|
|
470
|
+
- `@isTeacher()` - Shorthand for teacher role
|
|
471
|
+
- `@isStudent()` - Shorthand for student role
|
|
472
|
+
- `@isStaff()` - Allow admin, principal, accounting, secretary, teacher
|
|
473
|
+
|
|
474
|
+
#### Permission Guards
|
|
475
|
+
- `@Permission(permission)` - Require specific permission
|
|
476
|
+
|
|
477
|
+
### Constants
|
|
478
|
+
|
|
479
|
+
```typescript
|
|
480
|
+
ROLES = {
|
|
481
|
+
ADMIN: 'admin',
|
|
482
|
+
PRINCIPAL: 'principal',
|
|
483
|
+
ACCOUNTING: 'accounting',
|
|
484
|
+
SECRETARY: 'secretary',
|
|
485
|
+
TEACHER: 'teacher',
|
|
486
|
+
STUDENT: 'student',
|
|
487
|
+
PARENT: 'parent',
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
ROLE_GROUPS = {
|
|
491
|
+
ADMINISTRATORS: ['admin', 'principal'],
|
|
492
|
+
FINANCIAL: ['admin', 'accounting'],
|
|
493
|
+
STAFF: ['admin', 'principal', 'accounting', 'secretary', 'teacher'],
|
|
494
|
+
END_USERS: ['student', 'parent'],
|
|
495
|
+
ALL: [...all roles],
|
|
496
|
+
}
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
## License
|
|
500
|
+
|
|
501
|
+
MIT
|