-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmiddleware.ts
233 lines (202 loc) · 7.97 KB
/
middleware.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
import { NextRequest, NextResponse } from 'next/server'
import { getToken } from 'next-auth/jwt'
// Definição dos papéis de usuário como strings
type RoleType = 'SUPER_ADMIN' | 'COMPANY_ADMIN' | 'INSTRUCTOR' | 'STUDENT' | 'USER';
// Mapeamento de rotas para papéis autorizados
const routePermissions: Record<string, RoleType[]> = {
'/api/superadmin': ['SUPER_ADMIN'],
'/api/admin': ['SUPER_ADMIN', 'COMPANY_ADMIN'],
'/api/instructor': ['SUPER_ADMIN', 'COMPANY_ADMIN', 'INSTRUCTOR'],
'/api/student': ['SUPER_ADMIN', 'COMPANY_ADMIN', 'INSTRUCTOR', 'STUDENT'],
'/api/training': ['SUPER_ADMIN', 'COMPANY_ADMIN', 'INSTRUCTOR', 'STUDENT'],
'/superadmin': ['SUPER_ADMIN'],
'/admin': ['SUPER_ADMIN', 'COMPANY_ADMIN'],
'/treinamento': ['SUPER_ADMIN', 'COMPANY_ADMIN', 'INSTRUCTOR', 'STUDENT'],
};
// Função para verificar se a rota é protegida
function isProtectedRoute(pathname: string): boolean {
return (
pathname.startsWith('/api/admin') ||
pathname.startsWith('/api/superadmin') ||
pathname.startsWith('/api/training') ||
pathname.startsWith('/admin/dashboard') ||
pathname.startsWith('/superadmin/dashboard') ||
pathname.startsWith('/treinamento') && !pathname.startsWith('/treinamento/login')
)
}
// Função para verificar se a rota é de login
function isLoginRoute(pathname: string): boolean {
return pathname === '/admin/login' || pathname === '/superadmin/login' || pathname === '/treinamento/login'
}
// Configuração do middleware
const config = {
matcher: [
'/api/admin/:path*',
'/api/superadmin/:path*',
'/api/training/:path*',
'/admin/:path*',
'/superadmin/:path*',
'/treinamento/:path*',
],
}
export { config }
// Função para verificar se a rota é pública
function isPublicRoute(pathname: string, publicEndpoints: string[]): boolean {
return publicEndpoints.some(endpoint => {
// Verificar se o endpoint termina com * para correspondência parcial
if (endpoint.endsWith('*')) {
const baseEndpoint = endpoint.slice(0, -1)
return pathname.startsWith(baseEndpoint)
}
// Caso contrário, verificar correspondência exata
return pathname === endpoint
})
}
// Função para obter a URL base do ambiente atual
function getBaseUrl(request: NextRequest): string {
const host = request.headers.get('host') || 'localhost:3000'
const protocol = request.headers.get('x-forwarded-proto') || 'http'
// Usar NEXTAUTH_URL se estiver definido
if (process.env.NEXTAUTH_URL) {
return process.env.NEXTAUTH_URL
}
// Caso contrário, construir a URL base a partir dos headers
return `${protocol}://${host}`
}
// Middleware principal para autenticação e autorização
export async function middleware(request: NextRequest) {
// Obter o caminho da URL
const { pathname } = request.nextUrl
// Verificar se é uma página de login
if (isLoginRoute(pathname)) {
return NextResponse.next()
}
// Endpoints públicos que não exigem autenticação
const publicEndpoints = [
'/api/responses',
'/api/responses/save-progress',
'/api/responses/save-answers',
'/api/candidates/validate-invite',
'/teste/*',
'/test/*',
'/register/*',
'/api/teste/*',
'/api/candidates/*',
'/api/questions',
'/api/questions/*',
'/api/stages/*',
'/api/responses/*',
'/api/auth/*',
'/api/public/*',
'/_next/*',
'/favicon.ico',
'/images/*',
'/styles/*',
'/scripts/*',
'/',
];
// Verificar se é um endpoint público
if (isPublicRoute(pathname, publicEndpoints)) {
return NextResponse.next()
}
try {
// Obter informações do ambiente
const host = request.headers.get('host') || 'desconhecido'
const cookieHeader = request.headers.get('cookie')
const protocol = request.headers.get('x-forwarded-proto') || 'http'
const isProduction = process.env.NODE_ENV === 'production'
const cookiePrefix = isProduction
? process.env.NEXT_PUBLIC_COOKIE_PREFIX || 'prod_'
: process.env.NEXT_PUBLIC_COOKIE_PREFIX || 'dev_'
const baseUrl = getBaseUrl(request)
// Obter o token JWT da requisição
const token = await getToken({
req: request,
secret: process.env.NEXTAUTH_SECRET,
secureCookie: isProduction,
cookieName: `${cookiePrefix}next-auth.session-token`,
})
// Se o token não existir, redirecionar para a página de login
if (!token) {
// Determinar a página de login apropriada com base na rota
const loginUrl = pathname.startsWith('/superadmin')
? '/superadmin/login'
: pathname.startsWith('/treinamento') ? '/treinamento/login' : '/admin/login'
// Criar URL de redirecionamento com callbackUrl
const url = request.nextUrl.clone()
url.pathname = loginUrl
// Usar a URL base atual para o callbackUrl, não a URL da requisição
// Isso evita misturar domínios como localhost e admitto.com.br
const currentPath = request.nextUrl.pathname
const callbackUrl = new URL(currentPath, baseUrl).toString()
url.search = `?callbackUrl=${encodeURIComponent(callbackUrl)}`
return NextResponse.redirect(url)
}
// Verificar permissões para rotas de superadmin
if (pathname.startsWith('/api/superadmin') || pathname.startsWith('/superadmin')) {
if (token.role !== 'SUPER_ADMIN') {
return new NextResponse(
JSON.stringify({ success: false, message: 'Acesso negado. Permissão insuficiente.' }),
{ status: 403, headers: { 'content-type': 'application/json' } }
)
}
}
// Verificar permissões para rotas de admin
if (pathname.startsWith('/api/admin') || pathname.startsWith('/admin')) {
if (token.role !== 'SUPER_ADMIN' && token.role !== 'COMPANY_ADMIN') {
return new NextResponse(
JSON.stringify({ success: false, message: 'Acesso negado. Permissão insuficiente.' }),
{ status: 403, headers: { 'content-type': 'application/json' } }
)
}
}
// Verificar permissões para rotas de treinamento
if (pathname.startsWith('/api/training') || pathname.startsWith('/treinamento')) {
if (token.role !== 'SUPER_ADMIN' && token.role !== 'COMPANY_ADMIN' &&
token.role !== 'INSTRUCTOR' && token.role !== 'STUDENT') {
return new NextResponse(
JSON.stringify({ success: false, message: 'Acesso negado. Permissão insuficiente.' }),
{ status: 403, headers: { 'content-type': 'application/json' } }
)
}
}
// Determinar qual rota está sendo acessada
const routePrefix = Object.keys(routePermissions).find(
prefix => pathname.startsWith(prefix)
);
if (routePrefix) {
// Obter os papéis permitidos para esta rota
const allowedRoles = routePermissions[routePrefix];
// Verificar se o papel do usuário está na lista de papéis permitidos
if (!allowedRoles.includes(token.role as RoleType)) {
// Se for uma rota de API, retorna 403
if (pathname.startsWith('/api/')) {
return new NextResponse(
JSON.stringify({ message: 'Não autorizado' }),
{
status: 403,
headers: {
'Content-Type': 'application/json',
},
}
);
}
// Se for uma página, redireciona para dashboard apropriado
if (token.role === 'SUPER_ADMIN') {
return NextResponse.redirect(new URL('/superadmin/dashboard', request.url));
} else {
return NextResponse.redirect(new URL('/admin/dashboard', request.url));
}
}
}
return NextResponse.next()
} catch (error) {
// Em caso de erro, redirecionar para a página de login
const loginUrl = pathname.startsWith('/superadmin')
? '/superadmin/login'
: pathname.startsWith('/treinamento') ? '/treinamento/login' : '/admin/login'
const url = request.nextUrl.clone()
url.pathname = loginUrl
return NextResponse.redirect(url)
}
}