Merge branch 'main' of git.savefamilygps.net:SaveFamily/sf-sim
This commit is contained in:
@@ -11,7 +11,7 @@ post {
|
||||
}
|
||||
|
||||
body:form-urlencoded {
|
||||
iccid: 8933201125065160380
|
||||
iccid: 8933201125065160406
|
||||
offer: SAVEFAMILY1
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
"scripts": {
|
||||
"test": "vitest watch",
|
||||
"build": "yarn workspaces foreach -A --exclude sim-consumidor-nos run build && cp .env dist/ && yarn setup:runtime",
|
||||
"setup:runtime": "mkdir -p dist/packages/node_modules && ln -sf ../sim-shared dist/packages/node_modules/sim-shared && ln -sf ../sim-consumidor-objenious dist/packages/node_modules/sim-consumidor-objenious && ln -sf ../sim-entrada-eventos dist/packages/node_modules/sim-entrada-eventos && ln -sf ../sim-objenious-cron dist/packages/node_modules/sim-objenious-cron",
|
||||
"setup:runtime": "mkdir -p dist/packages/node_modules && ln -sf ../sim-shared dist/packages/node_modules/sim-shared && ln -sf ../sf-consumidor-objenious dist/packages/node_modules/sim-consumidor-objenious",
|
||||
"start": "yarn setup:runtime && yarn workspaces foreach -Apiv --exclude sim-consumidor-nos run start",
|
||||
"typecheck": "npx tsc --noEmit",
|
||||
"dev": "yarn workspaces foreach -Apiv --exclude sim-consumidor-nos run dev ",
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// PEM ?
|
||||
|
||||
/**
|
||||
* TODO:
|
||||
* Está demasiado acoplado a objenious, hay que sacar un servicio jwt general para
|
||||
@@ -10,7 +8,9 @@ import { env } from "#config/env/index.js";
|
||||
import fs from "fs"
|
||||
|
||||
import {
|
||||
JWTToken
|
||||
JWTToken,
|
||||
JWTHeader,
|
||||
IJWTService
|
||||
} from "sim-shared/domain/JWT.js"
|
||||
import axios, { AxiosError } from "axios";
|
||||
|
||||
@@ -32,15 +32,6 @@ type TokensRequestResponse = {
|
||||
"scope": string
|
||||
}
|
||||
|
||||
type AuthHeaders = {
|
||||
content_type: string,
|
||||
sub: string,
|
||||
iss: string,
|
||||
aud: string,
|
||||
jti: string,
|
||||
iat: number,
|
||||
exp: number,
|
||||
}
|
||||
|
||||
const PRIVATE_KEY_PATH = env.OBJ_PEM_PATH
|
||||
|
||||
@@ -64,7 +55,7 @@ const DEFAULT_HEADERS = {
|
||||
}
|
||||
|
||||
function addIATHeaders(authHeaders: Object) {
|
||||
const headers = <AuthHeaders>{
|
||||
const headers = <JWTHeader>{
|
||||
...authHeaders,
|
||||
sub: env.OBJ_CLIENT_ID,
|
||||
iss: env.OBJ_CLIENT_ID,
|
||||
@@ -76,6 +67,7 @@ function addIATHeaders(authHeaders: Object) {
|
||||
return headers
|
||||
}
|
||||
|
||||
export type ObjeniousTokenBody = any
|
||||
|
||||
/**
|
||||
* El servicio gestiona un par de tokens auth - refresh para las
|
||||
@@ -85,10 +77,10 @@ function addIATHeaders(authHeaders: Object) {
|
||||
* Debe tener un cliente HTTP propio para que no le afecten los
|
||||
* interceptores, sino puede haber bucles de refresco de token
|
||||
*/
|
||||
export class JWTService {
|
||||
export class JWTService implements IJWTService<ObjeniousTokenBody> {
|
||||
public isRefreshing: boolean = false;
|
||||
public authToken: JWTToken<{}> | undefined
|
||||
private refreshToken?: JWTToken<{}>
|
||||
public authToken: JWTToken<ObjeniousTokenBody> | undefined;
|
||||
private refreshToken?: JWTToken<ObjeniousTokenBody> | undefined;
|
||||
|
||||
constructor(args?: {
|
||||
token?: string // si se partiese de un token existente,
|
||||
@@ -122,7 +114,7 @@ export class JWTService {
|
||||
return token
|
||||
}
|
||||
|
||||
public async getNewTokens() {
|
||||
public async getNewAuthToken() {
|
||||
const bodyWithtoken = {
|
||||
...DEFAULT_BODY,
|
||||
client_assertion: this.buildJwtBody()
|
||||
@@ -139,8 +131,8 @@ export class JWTService {
|
||||
let res;
|
||||
try {
|
||||
res = (await req).data as TokensRequestResponse;
|
||||
this.authToken = new JWTToken(res.access_token)
|
||||
this.refreshToken = new JWTToken(res.refresh_token)
|
||||
this.authToken = new JWTToken<ObjeniousTokenBody>(res.access_token)
|
||||
this.refreshToken = new JWTToken<ObjeniousTokenBody>(res.refresh_token)
|
||||
return this.authToken
|
||||
} catch (e) {
|
||||
const errorString = "No se ha podido conseguir el token de acceso de OBJENIOUS"
|
||||
@@ -163,7 +155,7 @@ export class JWTService {
|
||||
}
|
||||
|
||||
// Caso 3: Ningún token es valido
|
||||
await this.getNewTokens()
|
||||
await this.getNewAuthToken()
|
||||
|
||||
if (this.authToken == undefined) throw new Error("Error obteniendo tokens de auth")
|
||||
|
||||
@@ -190,8 +182,8 @@ export class JWTService {
|
||||
let res;
|
||||
try {
|
||||
res = (await req).data as TokensRequestResponse;
|
||||
this.authToken = new JWTToken(res.access_token)
|
||||
this.refreshToken = new JWTToken(res.refresh_token)
|
||||
this.authToken = new JWTToken<ObjeniousTokenBody>(res.access_token)
|
||||
this.refreshToken = new JWTToken<ObjeniousTokenBody>(res.refresh_token)
|
||||
return this.authToken
|
||||
} catch (e) {
|
||||
const errorString = "No se ha podido conseguir el token de acceso de OBJENIOUS"
|
||||
|
||||
@@ -2,7 +2,7 @@ import { ActionData, ActivationData } from "#domain/DTOs/objeniousapi.js"
|
||||
import { HttpClient } from "sim-shared/infrastructure/HTTPClient.js"
|
||||
import { AxiosError } from "axios"
|
||||
import { Result } from "sim-shared/domain/Result.js"
|
||||
import { ObjeniousOperation, IOperationsRepository as OperationsRepositoryPort } from "#domain/operationsRepository.port.js"
|
||||
import { ObjeniousOperation, IOperationsRepository as OperationsRepositoryPort } from "sim-shared/domain/operationsRepository.port.js"
|
||||
|
||||
// TODO:
|
||||
// - Pasar a un archivo de DTOs
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
|
||||
import { OperationsRepository } from "#adapters/OperationRepository.js"
|
||||
import { OperationsRepository } from "sim-shared/infrastructure/OperationRepository.js"
|
||||
import { startRMQClient } from "#config/eventBus.config.js"
|
||||
import { httpInstance } from "#config/httpClient.config.js"
|
||||
import { pgPool } from "#config/postgreConfig.js"
|
||||
|
||||
@@ -68,6 +68,7 @@
|
||||
"cors": "*",
|
||||
"dotenv": "*",
|
||||
"express": "*",
|
||||
"sim-consumidor-objenious": "sim-consumidor-objenious:*",
|
||||
"sim-shared": "sim-shared:*",
|
||||
"typescript": "*"
|
||||
},
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { HttpClient } from "sim-shared/infrastructure/HTTPClient.js"
|
||||
import { JWTService } from "sim-consumidor-objenious/aplication/JWT.service.js"
|
||||
import { env } from "./env/index.js"
|
||||
import { JWTService } from "packages/sim-consumidor-objenious/aplication/JWT.service.js"
|
||||
|
||||
const OBJ_BASE_URL = env.OBJ_BASE_URL
|
||||
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
|
||||
import { pgPool } from "./config/postgreConfig.js"
|
||||
import { PgClient } from "sim-shared/infrastructure/PgClient.js"
|
||||
import { OperationsRepository } from "../sim-consumidor-objenious/infrastructure/OperationRepository.js"
|
||||
import { httpInstance } from "./config/httpClient.config.js"
|
||||
import { CheckObjeniousRequests } from "./tasks/check_objenious_request.js"
|
||||
import { OperationsRepository } from "sim-shared/infrastructure/OperationRepository.js"
|
||||
|
||||
async function startCron() {
|
||||
const commonSettings = {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { IOperationsRepository, Objenious, ObjeniousOperation, ObjeniousOperationChange, StatusEnum } from "sim-consumidor-objenious/domain/operationsRepository.port.js"
|
||||
import { IOperationsRepository, Objenious, ObjeniousOperation, ObjeniousOperationChange, StatusEnum } from "sim-shared/domain/operationsRepository.port.js";
|
||||
import { HttpClient } from "sim-shared/infrastructure/HTTPClient.js";
|
||||
|
||||
export class CheckObjeniousRequests {
|
||||
|
||||
@@ -1,11 +1,45 @@
|
||||
/**
|
||||
* Herramientas para gestionar todos los trabajos con JWT, los tipos
|
||||
* definidos aquí deben de ser sufcientemente generales para usarse
|
||||
* en todos los proyectos.
|
||||
*
|
||||
* Las cabeceras de un token y la firma son standard para todos, pero
|
||||
* el body puede variar con contenido nuevo.
|
||||
*/
|
||||
|
||||
import { sign } from "node:crypto"
|
||||
|
||||
export type JWTHeader = {
|
||||
alg: string,
|
||||
typ: string,
|
||||
kid: string
|
||||
export interface IJWTService<T> {
|
||||
/* Obtener un token de auth sin comprobar nada */
|
||||
getNewAuthToken: () => Promise<JWTToken<T>>,
|
||||
/* Obtener un token valido -> sino refrescar -> sin pillar un token nuevo */
|
||||
getAccessToken: () => Promise<JWTToken<T>>,
|
||||
/* Intenta refrescar el token actual */
|
||||
tryRefreshToken: () => Promise<JWTToken<T>>,
|
||||
}
|
||||
|
||||
export type JWTHeader = {
|
||||
/* algoritmo de firma */
|
||||
alg: string,
|
||||
/* tipo de token */
|
||||
typ: string,
|
||||
/* key ID */
|
||||
kid?: string
|
||||
/* content type */
|
||||
cty?: string
|
||||
/**/
|
||||
iss?: string,
|
||||
sub?: string,
|
||||
aud?: string,
|
||||
jti?: string,
|
||||
iat?: number,
|
||||
exp?: number,
|
||||
}
|
||||
|
||||
/*
|
||||
* Define los campos basicos del payload de un token, en <T> se
|
||||
* añaden los campos espeficos de la comunicacion
|
||||
* */
|
||||
export type JWTPayload<T> = {
|
||||
/** (Issuer) Quién emitió el token */
|
||||
iss?: string;
|
||||
@@ -45,29 +79,33 @@ export type JWT<T> = {
|
||||
}
|
||||
|
||||
// todo pasar a la clase JWT
|
||||
function signJWT(args: {
|
||||
algorythm: "sha256" | string,
|
||||
data: string,
|
||||
privateKey: string
|
||||
}) {
|
||||
function signJWT(args: SignatureOptions) {
|
||||
const signature = sign(
|
||||
args.algorythm,
|
||||
Buffer.from(args.data),
|
||||
args.privateKey
|
||||
)
|
||||
return signature
|
||||
return signature.toString("base64url")
|
||||
}
|
||||
|
||||
export type SignatureOptions = {
|
||||
algorythm: "sha256" | string,
|
||||
data: string,
|
||||
privateKey: string,
|
||||
}
|
||||
|
||||
export class JWTToken<T> {
|
||||
|
||||
public rawToken: string
|
||||
private decodedPayload: JWTPayload<T> | undefined
|
||||
private signatureFunc = signJWT
|
||||
|
||||
constructor(
|
||||
token: string
|
||||
token: string,
|
||||
externalSignatureFunc?: (args: SignatureOptions) => string
|
||||
) {
|
||||
this.rawToken = token
|
||||
this.decodedPayload = this.decodePayload()
|
||||
if (externalSignatureFunc != undefined) this.signatureFunc = externalSignatureFunc
|
||||
}
|
||||
|
||||
public static fromParts<T>(args: {
|
||||
@@ -76,10 +114,13 @@ export class JWTToken<T> {
|
||||
sigantureData?: {
|
||||
privateKey: string,
|
||||
algorythm: string
|
||||
}
|
||||
},
|
||||
signatureFunc?: (args: SignatureOptions) => string
|
||||
}) {
|
||||
const strHeader = JSON.stringify(args.header)
|
||||
const base64Header = Buffer.from(strHeader).toString("base64url")
|
||||
const signatureFunc = args.signatureFunc ?? signJWT
|
||||
|
||||
let token = base64Header
|
||||
|
||||
if (args.payload != undefined) {
|
||||
@@ -89,11 +130,11 @@ export class JWTToken<T> {
|
||||
}
|
||||
|
||||
if (args.sigantureData != undefined) {
|
||||
const base64signature = signJWT({
|
||||
const base64signature = signatureFunc({
|
||||
algorythm: args.sigantureData.algorythm,
|
||||
privateKey: args.sigantureData.privateKey,
|
||||
data: token
|
||||
}).toString("base64url")
|
||||
data: token,
|
||||
})
|
||||
token += ("." + base64signature)
|
||||
}
|
||||
|
||||
@@ -116,4 +157,14 @@ export class JWTToken<T> {
|
||||
if (expirationDate * 1000 <= now.getTime()) return true
|
||||
return false
|
||||
}
|
||||
|
||||
public isValid() {
|
||||
throw new Error("No implementado")
|
||||
}
|
||||
|
||||
public verifySignature() {
|
||||
throw new Error("No implementado")
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import axios, { AxiosInstance } from "axios"
|
||||
import { JWTToken } from "../domain/JWT.js"
|
||||
import { IJWTService, JWTToken } from "../domain/JWT.js"
|
||||
|
||||
// Cambiar por IJWRGeneralService
|
||||
|
||||
export type JWTProvider<T> = {
|
||||
/** El servidor está solicitando un token nuevo o refrescando el actual*/
|
||||
@@ -13,11 +15,13 @@ export class HttpClient {
|
||||
|
||||
public client: AxiosInstance
|
||||
private jwtManager: JWTProvider<{}>
|
||||
private jwtService: IJWTService<any> | undefined;
|
||||
|
||||
constructor(args: {
|
||||
baseURL: string,
|
||||
headers: Object,
|
||||
jwtManager: JWTProvider<{}> // todo: asociar el tipo de token
|
||||
jwtManager: JWTProvider<{}> // todo: asociar el tipo de token,
|
||||
jwtService?: IJWTService<any>
|
||||
}) {
|
||||
this.client = axios.create({
|
||||
...args
|
||||
@@ -25,6 +29,7 @@ export class HttpClient {
|
||||
|
||||
this.jwtManager = args.jwtManager
|
||||
|
||||
if (args.jwtService != undefined) this.jwtService = args.jwtService
|
||||
|
||||
this.client.interceptors.request.use(
|
||||
async (config) => {
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import { IOperationsRepository, ObjeniousOperation, ObjeniousOperationChange } from "#domain/operationsRepository.port.js";
|
||||
import { IOperationsRepository, ObjeniousOperation, ObjeniousOperationChange } from "sim-shared/domain/operationsRepository.port.js";
|
||||
import { Result } from "sim-shared/domain/Result.js";
|
||||
import { PgClient } from "sim-shared/infrastructure/PgClient.js";
|
||||
|
||||
|
||||
|
||||
export class OperationsRepository implements IOperationsRepository {
|
||||
|
||||
constructor(
|
||||
@@ -9,6 +9,6 @@
|
||||
],
|
||||
"include": [
|
||||
"**/*.ts",
|
||||
"../../packages/sim-shared/**/*.ts"
|
||||
"../../packages/sim-shared/**/*.ts",
|
||||
]
|
||||
}
|
||||
|
||||
@@ -2769,7 +2769,7 @@ __metadata:
|
||||
languageName: unknown
|
||||
linkType: soft
|
||||
|
||||
"sim-consumidor-objenious@workspace:packages/sim-consumidor-objenious":
|
||||
"sim-consumidor-objenious@sim-consumidor-objenious:*, sim-consumidor-objenious@workspace:packages/sim-consumidor-objenious":
|
||||
version: 0.0.0-use.local
|
||||
resolution: "sim-consumidor-objenious@workspace:packages/sim-consumidor-objenious"
|
||||
dependencies:
|
||||
@@ -2784,6 +2784,7 @@ __metadata:
|
||||
dotenv: "npm:*"
|
||||
express: "npm:*"
|
||||
prettier: "npm:*"
|
||||
sim-consumidor-objenious: "sim-consumidor-objenious:*"
|
||||
sim-shared: "sim-shared:*"
|
||||
supertest: "npm:*"
|
||||
tsc-alias: "npm:^1.8.16"
|
||||
|
||||
Reference in New Issue
Block a user