diff --git a/.github/workflows/deploy-manually.yml b/.github/workflows/deploy-manually.yml index ba42f7f..b947858 100644 --- a/.github/workflows/deploy-manually.yml +++ b/.github/workflows/deploy-manually.yml @@ -142,45 +142,11 @@ jobs: docker system prune --volumes -a -f docker system df - k8s-setup: - needs: [extract_environment] - uses: ./.github/workflows/k8s-setup.yml + k8s-deploy: + needs: [extract_environment, semantic_release, build_ecr_image] + uses: ./.github/workflows/k8s-deploy.yml with: - cloud: azure + cloud: ${{ (needs.extract_environment.outputs.environment == 'prd' && 'azure' ) || 'oracle' }} environment: ${{ needs.extract_environment.outputs.environment }} + image: ${{ needs.semantic_release.outputs.new_release_version }} secrets: inherit - - helmfile-deploy: - needs: [extract_environment, semantic_release, build_ecr_image, k8s-setup] - runs-on: [self-hosted, "prd-azure"] - environment: ${{ needs.extract_environment.outputs.environment }} - - steps: - - name: Checkout code - uses: actions/checkout@v3 - - - name: Set up Helm - uses: azure/setup-helm@v1 - with: - version: 'v3.9.0' - - - name: Set up Python - uses: actions/setup-python@v4 - with: - python-version: '3.8' - - - name: Install Helmfile - run: | - wget https://github.com/helmfile/helmfile/releases/download/v0.148.0/helmfile_0.148.0_linux_amd64.tar.gz - tar -xzf helmfile_0.148.0_linux_amd64.tar.gz - mv helmfile /usr/local/bin/ - helmfile --version - - - name: Install Helm Diff Plugin - run: helm plugin install https://github.com/databus23/helm-diff || true - - - name: Run Helmfile Apply - env: - ENV: ${{ needs.extract_environment.outputs.environment }} - IMAGE_TAG: ${{ needs.semantic_release.outputs.new_release_version }} - run: helmfile -f deploy/helmfiles/${ENV}.yaml sync --set image.tag=$IMAGE_TAG diff --git a/.github/workflows/k8s-setup.yml b/.github/workflows/k8s-deploy.yml similarity index 55% rename from .github/workflows/k8s-setup.yml rename to .github/workflows/k8s-deploy.yml index 8b3631d..d3d469d 100644 --- a/.github/workflows/k8s-setup.yml +++ b/.github/workflows/k8s-deploy.yml @@ -1,4 +1,4 @@ -name : K8s Setup kube config to deploy +name : K8s deploy on: workflow_call: @@ -13,12 +13,24 @@ on: required: true default: "prd" type: string + image: + description: "Image Tag" + required: true + type: string jobs: azure: if: inputs.cloud == 'azure' runs-on: [self-hosted, "prd-azure"] steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Set up Helm + uses: azure/setup-helm@v1 + with: + version: 'v3.9.0' + - name: Install Azure ClI run: | curl -sL https://aka.ms/InstallAzureCLIDeb | bash @@ -36,12 +48,45 @@ jobs: uses: azure/setup-kubectl@v1 with: version: 'v1.30.1' + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.8' - oci: - if: inputs.cloud == 'oci' + - name: Install Helmfile + run: | + wget https://github.com/helmfile/helmfile/releases/download/v0.148.0/helmfile_0.148.0_linux_amd64.tar.gz + tar -xzf helmfile_0.148.0_linux_amd64.tar.gz + mv helmfile /usr/local/bin/ + helmfile --version + + - name: Install Helm Diff Plugin + run: helm plugin install https://github.com/databus23/helm-diff || true + + - name: Run Helmfile Apply + env: + ENV: ${{ inputs.environment }} + IMAGE_TAG: ${{ inputs.image }} + run: helmfile -f deploy/helmfiles/${ENV}.yaml sync --set image.tag=$IMAGE_TAG + + oracle: + if: inputs.cloud == 'oracle' runs-on: [self-hosted, "prd-oracle"] + env: + HOME: /home/runner steps: + - name: Checkout code + uses: actions/checkout@v3 + + - name: Set up Helm + uses: azure/setup-helm@v1 + with: + version: 'v3.9.0' + - name: Install OCI CLI + env: + HOME: /home/runner run: | bash -c "$(curl -L https://raw.githubusercontent.com/oracle/oci-cli/master/scripts/install/install.sh)" -- --accept-all-defaults echo "$HOME/bin" >> $GITHUB_PATH @@ -52,13 +97,27 @@ jobs: echo "${{ secrets.OCI_CONFIG }}" > ~/.oci/config echo "${{ secrets.OCI_PRIVATE_KEY }}" > ~/.oci/oci_api_key.pem chmod 600 ~/.oci/oci_api_key.pem - + + - name: Set up Python + uses: actions/setup-python@v4 + with: + python-version: '3.8' + + - name: Install Helmfile + run: | + wget https://github.com/helmfile/helmfile/releases/download/v0.148.0/helmfile_0.148.0_linux_amd64.tar.gz + tar -xzf helmfile_0.148.0_linux_amd64.tar.gz + sudo mv helmfile /usr/local/bin/ + helmfile --version + + - name: Install Helm Diff Plugin + run: helm plugin install https://github.com/databus23/helm-diff || true + - name: Authenticate with OKE cluster env: ENV: ${{ inputs.environment }} STG_CLUSTER_ID: "ocid1.cluster.oc1.sa-saopaulo-1.aaaaaaaagh3jvln52a3ebm3dodx6emmhv5bmfs7i7sv2k4zkbcbrzcl6v37q" PRD_CLUSTER_ID: "ocid1.cluster.oc1.sa-saopaulo-1.aaaaaaaanf3vptl6hc2tzd4enfd2hfpsht3wikxww5xejc3l7cwfm6l3sndq" - HOME: /home/runner run: | if [ "$ENV" = "stg" ]; then CLUSTER_ID=$STG_CLUSTER_ID @@ -70,8 +129,9 @@ jobs: fi oci ce cluster create-kubeconfig --cluster-id ${CLUSTER_ID} --file $HOME/.kube/config --region sa-saopaulo-1 --token-version 2.0.0 --kube-endpoint PRIVATE_ENDPOINT - - - name: Setup kubectl - uses: azure/setup-kubectl@v1 - with: - version: 'v1.30.1' + + - name: Run Helmfile Apply + env: + ENV: ${{ inputs.environment }} + IMAGE_TAG: ${{ inputs.image }} + run: helmfile -f deploy/helmfiles/${ENV}.yaml sync --set image.tag=$IMAGE_TAG \ No newline at end of file diff --git a/deploy/helm-chart/values-stg.yaml b/deploy/helm-chart/values-stg.yaml new file mode 100644 index 0000000..9bcd584 --- /dev/null +++ b/deploy/helm-chart/values-stg.yaml @@ -0,0 +1,22 @@ +maestro: + env: stg + duc_url: duc.stg.dadosfera.ai + pi_factory_url: pi-factory.stg.dadosfera.ai + in_factory_url: in-factory.stg.dadosfera.ai + tr_factory_url: in-factory.stg.dadosfera.ai + open_customer_id: b3e3dfe5-b992-4586-a73c-c0b0c00f615d + open_group_id: e3f98a2f-7748-4981-8505-7695c8ca8218 + +hostname: maestro.stg.dadosfera.ai + +replicaCount: 1 + +affinity: + nodeAffinity: + requiredDuringSchedulingIgnoredDuringExecution: + nodeSelectorTerms: + - matchExpressions: + - key: name + operator: In + values: + - general \ No newline at end of file diff --git a/deploy/helm-chart/values.yaml b/deploy/helm-chart/values.yaml index 8554a08..9ed722c 100644 --- a/deploy/helm-chart/values.yaml +++ b/deploy/helm-chart/values.yaml @@ -60,7 +60,7 @@ affinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - - key: application + - key: applications operator: In values: - general diff --git a/deploy/helmfiles/stg.yaml b/deploy/helmfiles/stg.yaml index 6dbfe05..362d061 100644 --- a/deploy/helmfiles/stg.yaml +++ b/deploy/helmfiles/stg.yaml @@ -3,23 +3,8 @@ charts: chart: ../helm-chart values: - ../helm-chart/values.yaml - set: - - name: maestro.duc_url - value: duc.stg.dadosfera.ai - - name: hostname - value: maestro.stg.dadosfera.ai - - name: maestro.pi_factory_url - value: pi-factory.stg.dadosfera.ai - - name: maestro.in_factory_url - value: in-factory.stg.dadosfera.ai - - name: maestro.tr_factory_url - value: in-factory.stg.dadosfera.ai - - name: maestro.open_customer_id - value: b3e3dfe5-b992-4586-a73c-c0b0c00f615d - - name: maestro.open_group_id - value: e3f98a2f-7748-4981-8505-7695c8ca8218 - - name: replicaCount - value: 1 + - ../helm-chart/values-stg.yaml + # Environment to test Network Policies # - name: private-maestro diff --git a/src/modules/identity-provider/identity-provider.controller.ts b/src/modules/identity-provider/identity-provider.controller.ts index de22e1c..c612f4e 100644 --- a/src/modules/identity-provider/identity-provider.controller.ts +++ b/src/modules/identity-provider/identity-provider.controller.ts @@ -153,12 +153,27 @@ export class IdentityProviderController { throw new Error(ErrorCodes.IDENTITY_PROVIDER.INVALID_RESPONSE); } - const origin = req.headers['origin'] as string; + try { + const origin = req.headers['origin'] as string; + this.logger.info('Header Origin: ' + origin); - const lang = language.substring(0, 2) + language.substring(2).toUpperCase(); - const callbackUrl = process.env.ENV !== "prd" ? `${origin}/auth/callback` : `${origin}/${lang}/auth/callback`; + const lang = + language.substring(0, 2) + language.substring(2).toUpperCase(); + const callbackUrl = + process.env.ENV !== 'prd' + ? `${origin}/auth/callback` + : `${origin}/${lang}/auth/callback`; - return await this.identityProviderService.getTokenByIdp(code, state, callbackUrl); + this.logger.info('Callback URL: ' + callbackUrl); + return await this.identityProviderService.getTokenByIdp( + code, + state, + callbackUrl, + ); + } catch (error) { + this.logger.error(error); + throw error; + } } @Get('/links') @@ -166,41 +181,66 @@ export class IdentityProviderController { async providerLinks(@Req() req: Request) { this.logger.info('GET /identity-providers/links'); - const frontDomain = req.headers['origin'] as string; + try { + const frontDomain = req.headers['origin'] as string; + this.logger.info('Header Origin: ' + frontDomain); - if (!frontDomain) { - throw new Error(ErrorCodes.IDENTITY_PROVIDER.INVALID_HEADER); + if (!frontDomain) { + this.logger.info('Not found front domain'); + throw new Error(ErrorCodes.IDENTITY_PROVIDER.INVALID_HEADER); + } + + const result = + await this.identityProviderService.identityProvidersLinksPerDomain( + frontDomain, + ); + return result; + } catch (error) { + this.logger.error(error); + throw error; } - - const result = - await this.identityProviderService.identityProvidersLinksPerDomain( - frontDomain, - ); - return result; } @Get(':id') @HttpCode(HttpStatus.OK) @Redirect() - async loginIdp(@Param('id') id: string, @Req() req: Request, @Language() language: LanguageEnum) { + async loginIdp( + @Param('id') id: string, + @Req() req: Request, + @Language() language: LanguageEnum, + ) { this.logger.info('GET /identity-providers/:id'); + try { + const frontDomain = + (req.headers['origin'] as string) || (req.headers['referer'] as string); + this.logger.info(`Front domain: ${frontDomain}`); + const host = + frontDomain.lastIndexOf('/') !== -1 + ? frontDomain.substring(0, frontDomain.lastIndexOf('/')) + : frontDomain; - const frontDomain = req.headers['origin'] as string || req.headers['referer'] as string; - this.logger.info(`Front domain: ${frontDomain}`); - const host = frontDomain.lastIndexOf('/') !== -1 - ? frontDomain.substring(0, frontDomain.lastIndexOf('/')) - : frontDomain; + const lang = + language.substring(0, 2) + language.substring(2).toUpperCase(); + const callbackUrl = + process.env.ENV !== 'prd' + ? `${host}/auth/callback` + : `${host}/${lang}/auth/callback`; - const lang = language.substring(0, 2) + language.substring(2).toUpperCase(); - const callbackUrl = process.env.ENV !== "prd" ? `${host}/auth/callback` : `${host}/${lang}/auth/callback`; + this.logger.info('Callback URL: ' + callbackUrl); + const redirectUrl = + await this.identityProviderService.loginIdentityProvider( + id, + callbackUrl, + ); - const redirectUrl = - await this.identityProviderService.loginIdentityProvider(id, callbackUrl); - - this.logger.info(`Redirecting to: ${redirectUrl}`); - return { - url: redirectUrl, - }; + this.logger.info(`Redirecting to: ${redirectUrl}`); + return { + url: redirectUrl, + }; + } catch (error) { + this.logger.error(error); + throw error; + } } } diff --git a/src/modules/identity-provider/identity-provider.service.ts b/src/modules/identity-provider/identity-provider.service.ts index 4cca0fe..a2e7084 100644 --- a/src/modules/identity-provider/identity-provider.service.ts +++ b/src/modules/identity-provider/identity-provider.service.ts @@ -14,16 +14,21 @@ import { Metadata } from '@grpc/grpc-js'; import { Issuer, generators } from 'openid-client'; import { SsoSignInDto } from './dto/sso-signin.dto'; import { CacheService } from 'src/services/cache.service'; -import { Request } from 'express'; import { CreateIdentityProvider } from './dto/identity-provider.dto'; +import DadosferaLogger from '@dadosfera/dadosfera-logs'; @Injectable() export class IdentityProviderService implements OnModuleInit { + private logger: DadosferaLogger; private identityProviderService: IdentityProviderProtoService; constructor( @Inject(DucClient.name) private readonly grpcClient: ClientGrpc, private readonly cacheService: CacheService, - ) {} + @Inject(DadosferaLogger) + private dadosferaLoggger: DadosferaLogger + ) { + this.logger = dadosferaLoggger.logger; + } onModuleInit() { this.identityProviderService = @@ -33,22 +38,26 @@ export class IdentityProviderService implements OnModuleInit { } async create(body: IdentityProviderRequest, metadata: Metadata) { + this.logger.info("Call IdentityProvider GRPC Create") return await lastValueFrom( this.identityProviderService.Create(body, metadata), ); } async getList(metadata: Metadata) { + this.logger.info("Call IdentityProvider GRPC GetList") return await lastValueFrom( this.identityProviderService.GetList({}, metadata), ); } async loginIdentityProvider(id: string, callbackUrl: string) { + this.logger.info("Call IdentityProvider GRPC FindIdentityProvider with: " + id); const idp = await lastValueFrom( this.identityProviderService.FindIdentityProvider({ id }), ); + this.logger.info("Discovery issueURL: " + idp.issuerUrl) const issuer = await Issuer.discover(idp.issuerUrl); const client = new issuer.Client({ client_id: idp.clientId, @@ -57,14 +66,19 @@ export class IdentityProviderService implements OnModuleInit { response_types: ['code'], }); + this.logger.info("Generate Challenge") const code_verifier: string = generators.codeVerifier(); const code_challenge: string = generators.codeChallenge(code_verifier); + this.logger.info("Generate State") const state = generators.state(); + + this.logger.info("Generate Nonce") const nonce = generators.nonce(); // Using state because it is returned in the callback // and we can use it to retrieve the code_verifier and nonce + this.logger.info("Save Login parameters in redis") await this.cacheService.set(state, { codeVerifier: code_verifier, nonce, @@ -76,6 +90,7 @@ export class IdentityProviderService implements OnModuleInit { redirectUrls: idp.redirectUrls, }); + this.logger.info("Generate Authorization URL") const url = client.authorizationUrl({ scope: 'openid email', response_type: 'code', @@ -86,16 +101,20 @@ export class IdentityProviderService implements OnModuleInit { redirect_uri: callbackUrl, }); const idpUrl = url + '&identity_provider=' + idp.name; + this.logger.info(idpUrl) return idpUrl; } async getTokenByIdp(code: string, state: string, callbackUrl: string) { + this.logger.info("Get login parameters in redis") const ssoSign = await this.cacheService.get(state); if (!ssoSign) { + this.logger.info("Login Parameters Not Found") throw new BadRequestException('SSO sign-in is expired or not found'); } + this.logger.info("Discovery Issue URL: " + ssoSign.issuerUrl) const issuer = await Issuer.discover(ssoSign.issuerUrl); const client = new issuer.Client({ client_id: ssoSign.clientId, @@ -103,22 +122,22 @@ export class IdentityProviderService implements OnModuleInit { redirect_uris: ssoSign.redirectUrls, }); - if (ssoSign === null) { - throw new BadRequestException('SSO sign-in is expired or not found'); - } - const params = client.callbackParams( `${callbackUrl}?code=${code}&state=${state}`, ); try { + + this.logger.info("Get Token Set"); const tokenSet = await client.callback(callbackUrl, params, { nonce: ssoSign.nonce, code_verifier: ssoSign.codeVerifier, state: ssoSign.state }); + this.logger.info("Delete parameters in redis"); await this.cacheService.delete(ssoSign.state); + this.logger.info("Call IdentityProvider GRPC SignInUser"); return await lastValueFrom( this.identityProviderService.SignInUser({ accessToken: tokenSet.access_token, @@ -128,12 +147,13 @@ export class IdentityProviderService implements OnModuleInit { }), ); } catch (error) { - console.log(error); + this.logger.error(error); throw error; } } async deleteIdentityProvider(id: string, metadata: Metadata) { + this.logger.info("Call IdentityProvider GRPC Delete with: " + id) return await lastValueFrom( this.identityProviderService.DeleteIdentityProvider({ id }, metadata), ); @@ -144,6 +164,7 @@ export class IdentityProviderService implements OnModuleInit { body: CreateIdentityProvider, metadata: Metadata, ) { + this.logger.info("Call IdentityProvider GRPC Update with: " + id) return await lastValueFrom( this.identityProviderService.UpdateIdentityProvider( { @@ -156,6 +177,7 @@ export class IdentityProviderService implements OnModuleInit { } async identityProvidersLinksPerDomain(frontDomain: string) { + this.logger.info("Call IdentityProvider GRPC LinksPerDomain with: " + frontDomain) return await lastValueFrom( this.identityProviderService.GetProviderLinksFromDomain({ frontDomain }), ); diff --git a/src/services/service.module.ts b/src/services/service.module.ts index d129eaf..0ce735f 100644 --- a/src/services/service.module.ts +++ b/src/services/service.module.ts @@ -7,7 +7,7 @@ import { redisStore } from 'cache-manager-ioredis-yet'; CacheModule.registerAsync({ useFactory: async () => ({ store: await redisStore({ - ttl: 1000 * 60, //1 minute + ttl: 5 * 1000 * 60, // 5 minute host: process.env.REDIS_HOST, port: process.env.REDIS_PORT && Number(process.env.REDIS_PORT), db: process.env.REDIS_DATABASE && Number(process.env.REDIS_DATABASE),