Skip to content

Autenticar e autorizar requisições por ação e recurso #517

Description

@edvaldoszy

Visão geral

Hoje tokensMiddleware([TIPOS_USUARIOS.CURADOR, …]) em src/routes/*.js. Depois de /auth/*, as rotas passam a authenticate + authorize(action, resource) nos dois stacks. Esta issue só o mecanismo + um modelo; famílias nas #518#523.

_spec/auth-and-authorization.md §10.

Fora de escopo

  • Migrar todas as famílias
  • Remover POST /login
  • Carregar entidade dentro do authorize da rota
  • Painel

Critérios de aceite

  • authenticate no legado (Express) e como RequestHandler no /v1
  • authorize('create', 'Tombo') (exemplo) recusa sem registro; 403 via ForbiddenError
  • Sem access / sessão apagada / usuário inativo → 401
  • Access expirado → 401 com código distinto (retry após refresh)
  • Uma família legado e uma rota /v1 usam o par como modelo
  • GET /v1/paises e GET /v1/estados continuam públicos
  • Demais rotas inalteradas

Detalhes técnicos

Fluxo: verificar JWT → linha em usuarios_sessoes existe → usuário ativocreateRules(user)new Manager({ rules }) em request.user e request.auth. Nunca autorizar com rules enviadas pelo cliente.

Legado:

app.route('/tombos')
  .post([
    authenticate,
    authorize('create', 'Tombo'),
    validacoesMiddleware(cadastrarTomboEsquema),
    cadastro,
  ])

Não adicionar request.usuario.

Novo (src/application/*, chain em ExpressApplication.endpoint):

{
  method: Method.Post,
  path: '/v1/tombos',
  handlers: [
    authenticate,
    authorize('create', 'Tombo'),
    new CriarTomboController({ criarTomboUseCase }),
  ]
}

Estender HttpRequest (src/library/http/common.ts) com user e auth opcionais.

authorize na rota: só can(action, resource). Registro: use case/controller depois do find (can(action, resource, record)). Collections não autorizam.

CASL sem registro = “pode esta ação neste recurso para algum item”. Conditions de linha só depois de carregar.

Esboço authenticate (RequestHandler novo stack):

async handle(request, next) {
  const token = bearer(request.headers.Authorization)
  if (!token) return new UnauthorizedError({ message: '…' })
  const payload = verifyAccess(token) // TokenExpiredError → código de expirado
  const session = await sessions.findById(payload.sid)
  if (!session) return new UnauthorizedError({ message: '…' })
  const user = await users.findActive(payload.sub)
  if (!user) return new UnauthorizedError({ message: '…' })
  request.user = { id: user.id, nome: user.nome, email: user.email, tipo_usuario_id: user.tipo_usuario_id }
  request.auth = new Manager({ rules: createRules(request.user) })
  return next()
}

Esboço authorize (fábrica):

export function authorize(action: string, resource: string): RequestHandler {
  return {
    async handle(request, next) {
      if (!request.auth?.can(action, resource)) {
        return new ForbiddenError({ message: '…' })
      }
      return next()
    },
  }
}

Use case (depois do load):

const tombo = await collection.findByHcf(hcf)
if (!tombo) return notFound
if (!auth.can('update', 'Tombo', tombo)) return forbidden

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    Projects

    • Status
      to do

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions