File

src/middleware/keyclock.middleware.ts

Index

Properties

Properties

email
Type : string
email_verified
Type : string
name
Type : string
realm
Type : any
roles
Type : any
username
Type : string
usuarioId
Type : string
import { HttpService } from '@nestjs/axios';
import { HttpStatus, Injectable, Logger, NestMiddleware } from '@nestjs/common';
import { configENV } from '../../config-env';
import jwt, { JwtPayload } from 'jsonwebtoken';
const jwktopem = require('jwk-to-pem');
import moment from 'moment';
import { ExceptionError } from '../util/exception-error';
import { catchError, firstValueFrom } from 'rxjs';

const logForDev = (msg) => {
  console.log('DEBUG', msg);
};


export class Session {
  email_verified: string;
  name: string;
  email: string;
  usuarioId: string;
  username: string;
  realm: any;
  roles: any;
}


const freeAccess = (originalUrl) => {
  if (originalUrl?.includes('/importa/')) return true;

  switch (true) {
    case originalUrl === '/':
    case originalUrl === '/api-docs':
    case originalUrl === '/application/client':
    // case originalUrl.startsWith('/'):
      return true;

    default:
      return false;
  }
};

const getPayloadToken = (token) => jwt.decode(token, { complete: true });

const getRealm = (token) => {
  const tokenDecoded = getPayloadToken(token) as any;

  if (tokenDecoded) {
    return tokenDecoded.payload.iss.split('/').slice(-1)[0];
  }

  return null;
};

const getRoles = (token) => {
  const { payload }: JwtPayload = jwt.decode(token, { complete: true });
  if (payload?.group && payload?.group.length) {
    const roles = payload.group.map((group) =>
      group.split('/')[2].toLowerCase(),
    );
    return roles;
  }
  const resource: { roles: string[] }[] = Object.values(
    payload.resource_access,
  );
  return resource[0].roles;
};

// const roleVerified = (payloadToken, searchElement = 'Analista') => {
//   const roles = Object.values(payloadToken.resource_access)[0].roles;

//   if (roles.roles.length) {
//     return roles.includes(searchElement);
//   }

//   logForDev('A permissão não é igual a "Analista"');
//   return false;
// }
@Injectable()
export class MiddlewareKeyclock implements NestMiddleware {
  private readonly logger = new Logger(MiddlewareKeyclock.name);
  async use(req, res, next) {
    try {
      if (freeAccess(req.originalUrl)) {
        next();
        return;
      }

      const { headers } = req;
      const { authorization } = headers;
      if (!authorization) {
        logForDev('Não recebeu o token');
        throw ExceptionError(
          'Você não está autorizado(a)',
          HttpStatus.FORBIDDEN,
        );
      }

      // Bearer lkasdjfksdfaDJKÇLÇLKASDA
      const parts = authorization.split(' ');

      if (parts.length !== 2) {
        throw ExceptionError('Token error', HttpStatus.FORBIDDEN);
      }

      const [schema, token] = parts;

      // Verifica se Schema tem a palavra Bearer
      if (!/^Bearer$/i.test(schema)) {
        throw ExceptionError('Token mal formado', HttpStatus.FORBIDDEN);
      }

      const tokenDecoded = getPayloadToken(token) as any;

      if (!tokenDecoded.payload.exp) {
        throw ExceptionError('Token inválido', HttpStatus.FORBIDDEN);
      }

      if (moment() > moment.unix(tokenDecoded.payload.exp)) {
        throw ExceptionError('Sessão expirada', HttpStatus.UNAUTHORIZED);
      }

      // if (!roleVerified(tokenDecoded.payload)) {
      //   throw ExceptionError('Você não está autorizado(a)', 426);
      // }

      const realm = getRealm(token);
      const roles = getRoles(token);

      if (!realm) {
        throw ExceptionError(
          'Realm não encontrado no token',
          HttpStatus.UNAUTHORIZED,
        );
      }

      const get = async (urlPublicKey) => {
        const httpService = new HttpService();
        const { data } = await firstValueFrom(
          httpService.get(urlPublicKey).pipe(
            catchError((error) => {
              this.logger.debug('Erro ao buscar certs');
              this.logger.error(error.response.data);
              throw { message: 'Algo deu errado' };
            }),
          ),
        );

        return data;
      };

      const {
        metodo_aceso,
        ip_servidor,
        endpoint_keycloak_realms,
        endpoint_keycloak_certs,
      } = configENV;

      const urlPublicKey =
        metodo_aceso +
        ip_servidor +
        endpoint_keycloak_realms +
        realm +
        endpoint_keycloak_certs;
      const data = await get(urlPublicKey);
      const { keys } = data;
      let firstKey = undefined;

      if (keys[0].alg == 'RS256') {
        firstKey = keys[0];
      } else {
        firstKey = keys[1];
      }

      const pkey = jwktopem(firstKey);

      const validacao = jwt.verify(token, pkey) as any;

      if (validacao.sub) {
        req.session = {
          email_verified: validacao.email_verified,
          name: validacao.name,
          email: validacao.email,
          usuarioId: validacao.sub,
          username: validacao.preferred_username,
          realm,
          roles,
        };

        next();
      }
    } catch (error) {
      const status = error.code ? error.code : HttpStatus.FORBIDDEN;
      res.status(status).json(error);

      // return res.sendStatus(error.code ? error.code : 426).json(error);
    }
  }
}

results matching ""

    No results matching ""