diff --git a/kubernetes/keycloak/install.sh b/kubernetes/keycloak/install.sh index dd330de..3c7643d 100755 --- a/kubernetes/keycloak/install.sh +++ b/kubernetes/keycloak/install.sh @@ -35,6 +35,7 @@ if [ "$KEYCLOAK_INIT_ENABLED" != "false" ]; then export OPENG2P_MINIO_CLIENT_SECRET=$(kubectl -n $NS get secret keycloak-client-secrets -o jsonpath={.data.openg2p_minio_client_secret} | base64 --decode) export OPENG2P_KAFKA_CLIENT_SECRET=$(kubectl -n $NS get secret keycloak-client-secrets -o jsonpath={.data.openg2p_kafka_client_secret} | base64 --decode) export OPENG2P_KIBANA_CLIENT_SECRET=$(kubectl -n $NS get secret keycloak-client-secrets -o jsonpath={.data.openg2p_kibana_client_secret} | base64 --decode) + export OPENG2P_SUPERSET_CLIENT_SECRET=$(kubectl -n $NS get secret keycloak-client-secrets -o jsonpath={.data.openg2p_superset_client_secret} | base64 --decode) envsubst \ '${KEYCLOAK_HOSTNAME} @@ -43,7 +44,8 @@ if [ "$KEYCLOAK_INIT_ENABLED" != "false" ]; then ${OPENG2P_SERVICEPROVIDER_CLIENT_SECRET} ${OPENG2P_MINIO_CLIENT_SECRET} ${OPENG2P_KAFKA_CLIENT_SECRET} - ${OPENG2P_KIBANA_CLIENT_SECRET}' < ${KEYCLOAK_REALM_NAME}-realm.json > /tmp/${KEYCLOAK_REALM_NAME}-realm.json + ${OPENG2P_KIBANA_CLIENT_SECRET} + ${OPENG2P_SUPERSET_CIENT_SECRET}' < ${KEYCLOAK_REALM_NAME}-realm.json > /tmp/${KEYCLOAK_REALM_NAME}-realm.json keycloak_import_realm \ "$(keycloak_get_admin_token)" \ diff --git a/kubernetes/keycloak/keycloak-init/values.yaml b/kubernetes/keycloak/keycloak-init/values.yaml index f5b818c..4d8f04b 100644 --- a/kubernetes/keycloak/keycloak-init/values.yaml +++ b/kubernetes/keycloak/keycloak-init/values.yaml @@ -11,6 +11,8 @@ clientSecrets: secret: "" - name: openg2p_kibana_client_secret secret: "" + - name: openg2p_superset_client_secret + secret: "" - name: mosip_abis_client_secret secret: "" - name: mosip_admin_client_secret @@ -64,4 +66,4 @@ clientSecrets: - name: mosip_testrig_client_secret secret: "" - name: mpartner_default_opencrvs_secret - secret: "" \ No newline at end of file + secret: "" diff --git a/kubernetes/keycloak/openg2p-realm.json b/kubernetes/keycloak/openg2p-realm.json index 1faad71..4585f0c 100644 --- a/kubernetes/keycloak/openg2p-realm.json +++ b/kubernetes/keycloak/openg2p-realm.json @@ -218,6 +218,22 @@ "clientRole": false, "containerId": "6fda1fc8-16cb-4be1-9375-eb47e0fe4fc4", "attributes": {} + }, + { + "name": "superset_Admin", + "description": "", + "composite": false, + "clientRole": false, + "containerId": "6fda1fc8-16cb-4be1-9375-eb47e0fe4fc4", + "attributes": {} + }, + { + "name": "superset_Public", + "description": "", + "composite": false, + "clientRole": false, + "containerId": "6fda1fc8-16cb-4be1-9375-eb47e0fe4fc4", + "attributes": {} } ], "client": { @@ -695,6 +711,22 @@ ], "notBefore": 0, "groups": [] + }, + { + "id": "be449dec-3675-4a0b-8b6e-a32d3715f801", + "createdTimestamp": 1699248524251, + "username": "service-account-openg2p-superset-client", + "enabled": true, + "totp": false, + "emailVerified": false, + "serviceAccountClientId": "openg2p-superset-client", + "disableableCredentialTypes": [], + "requiredActions": [], + "realmRoles": [ + "default-roles-openg2p" + ], + "notBefore": 0, + "groups": [] } ], "scopeMappings": [ @@ -3145,6 +3177,362 @@ "offline_access", "microprofile-jwt" ] + }, + { + "clientId": "openg2p-superset-client", + "name": "OpenG2P Superset", + "description": "", + "rootUrl": "", + "adminUrl": "", + "baseUrl": "", + "surrogateAuthRequired": false, + "enabled": true, + "alwaysDisplayInConsole": false, + "clientAuthenticatorType": "client-secret", + "secret": "${OPENG2P_SUPERSET_CLIENT_SECRET}", + "redirectUris": [ + "*" + ], + "webOrigins": [], + "notBefore": 0, + "bearerOnly": false, + "consentRequired": false, + "standardFlowEnabled": true, + "implicitFlowEnabled": false, + "directAccessGrantsEnabled": true, + "serviceAccountsEnabled": true, + "publicClient": false, + "frontchannelLogout": true, + "protocol": "openid-connect", + "attributes": { + "oidc.ciba.grant.enabled": "false", + "oauth2.device.authorization.grant.enabled": "false", + "client.secret.creation.time": "1699248524", + "backchannel.logout.session.required": "true", + "backchannel.logout.revoke.offline.tokens": "false" + }, + "authenticationFlowBindingOverrides": {}, + "fullScopeAllowed": true, + "nodeReRegistrationTimeout": -1, + "protocolMappers": [ + { + "name": "phone number", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "aggregate.attrs": "false", + "userinfo.token.claim": "true", + "multivalued": "false", + "user.attribute": "phoneNumber", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "phone_number", + "jsonType.label": "String" + } + }, + { + "name": "birthdate", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "birthdate", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "birthdate", + "jsonType.label": "String" + } + }, + { + "name": "middle name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "middleName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "middle_name", + "jsonType.label": "String" + } + }, + { + "name": "given name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "firstName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "given_name", + "jsonType.label": "String" + } + }, + { + "name": "Client ID", + "protocol": "openid-connect", + "protocolMapper": "oidc-usersessionmodel-note-mapper", + "consentRequired": false, + "config": { + "user.session.note": "clientId", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "clientId", + "jsonType.label": "String" + } + }, + { + "name": "email verified", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "emailVerified", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "email_verified", + "jsonType.label": "boolean" + } + }, + { + "name": "full name", + "protocol": "openid-connect", + "protocolMapper": "oidc-full-name-mapper", + "consentRequired": false, + "config": { + "id.token.claim": "true", + "access.token.claim": "true", + "userinfo.token.claim": "true" + } + }, + { + "name": "Client Host", + "protocol": "openid-connect", + "protocolMapper": "oidc-usersessionmodel-note-mapper", + "consentRequired": false, + "config": { + "user.session.note": "clientHost", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "clientHost", + "jsonType.label": "String" + } + }, + { + "name": "email", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "email", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "email", + "jsonType.label": "String" + } + }, + { + "name": "Audience", + "protocol": "openid-connect", + "protocolMapper": "oidc-audience-mapper", + "consentRequired": false, + "config": { + "included.client.audience": "openg2p-superset-client", + "id.token.claim": "true", + "access.token.claim": "true" + } + }, + { + "name": "family name", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "lastName", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "family_name", + "jsonType.label": "String" + } + }, + { + "name": "picture", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "picture", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "picture", + "jsonType.label": "String" + } + }, + { + "name": "Client IP Address", + "protocol": "openid-connect", + "protocolMapper": "oidc-usersessionmodel-note-mapper", + "consentRequired": false, + "config": { + "user.session.note": "clientAddress", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "clientAddress", + "jsonType.label": "String" + } + }, + { + "name": "profile", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "profile", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "profile", + "jsonType.label": "String" + } + }, + { + "name": "phone number verified", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "phoneNumberVerified", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "phone_number_verified", + "jsonType.label": "boolean" + } + }, + { + "name": "locale", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "locale", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "locale", + "jsonType.label": "String" + } + }, + { + "name": "username", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-property-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "username", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "preferred_username", + "jsonType.label": "String" + } + }, + { + "name": "gender", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "gender", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "gender", + "jsonType.label": "String" + } + }, + { + "name": "nickname", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "nickname", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "nickname", + "jsonType.label": "String" + } + }, + { + "name": "address", + "protocol": "openid-connect", + "protocolMapper": "oidc-address-mapper", + "consentRequired": false, + "config": { + "user.attribute.formatted": "formatted", + "user.attribute.country": "country", + "user.attribute.postal_code": "postal_code", + "userinfo.token.claim": "true", + "user.attribute.street": "street", + "id.token.claim": "true", + "user.attribute.region": "region", + "access.token.claim": "true", + "user.attribute.locality": "locality" + } + }, + { + "name": "zoneinfo", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-attribute-mapper", + "consentRequired": false, + "config": { + "userinfo.token.claim": "true", + "user.attribute": "zoneinfo", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "zoneinfo", + "jsonType.label": "String" + } + }, + { + "name": "groups", + "protocol": "openid-connect", + "protocolMapper": "oidc-usermodel-realm-role-mapper", + "consentRequired": false, + "config": { + "multivalued": "true", + "user.attribute": "foo", + "id.token.claim": "true", + "access.token.claim": "true", + "claim.name": "groups", + "jsonType.label": "String" + } + } + ], + "defaultClientScopes": [ + "web-origins", + "acr", + "roles", + "profile", + "email" + ], + "optionalClientScopes": [ + "address", + "phone", + "offline_access", + "microprofile-jwt" + ] } ], "clientScopes": [ diff --git a/kubernetes/mojaloop/values-ml-ttk.yaml b/kubernetes/mojaloop/values-ml-ttk.yaml index b3f2403..c1b049c 100644 --- a/kubernetes/mojaloop/values-ml-ttk.yaml +++ b/kubernetes/mojaloop/values-ml-ttk.yaml @@ -7,15 +7,15 @@ ml-testing-toolkit-backend: "partyInfo" : { "provisionedParties": [ # Configure the wallet login credential and data here. - # { - # "displayName": "Shibu S Narayanan", - # "firstName": "Shibu", - # "middleName": "S", - # "lastName": "Naryanan", - # "dateOfBirth": "1974-05-14", - # "idType": "ACCOUNT_ID", - # "idValue": "7967395049" - # } + { + "displayName": "Maria Cristina Gonzales", + "firstName": "Maria", + "middleName": "Cristina", + "lastName": "Gonzales", + "dateOfBirth": "1974-07-01", + "idType": "ACCOUNT_ID", + "idValue": "5218119193" + } ] } } @@ -46,6 +46,224 @@ ml-testing-toolkit-backend: } rules_response__default.json: [ + { + "type": "response", + "version": 1, + "ruleId": 1, + "priority": 1, + "description": "post /settlementWindows/{id}", + "apiVersion": { + "minorVersion": 0, + "majorVersion": 1, + "type": "settlements", + "asynchronous": false + }, + "conditions": { + "all": [ + { + "fact": "operationPath", + "operator": "equal", + "value": "/settlementWindows/{id}" + }, + { + "fact": "method", + "operator": "equal", + "value": "post" + } + ] + }, + "event": { + "method": null, + "path": null, + "params": { + "body": { + "state": "OPEN" + }, + "statusCode": "200" + }, + "delay": 0, + "type": "MOCK_RESPONSE" + } + }, + { + "type": "response", + "version": 1, + "ruleId": 3, + "priority": 1, + "description": "post /settlements", + "apiVersion": { + "minorVersion": 0, + "majorVersion": 1, + "type": "settlements", + "asynchronous": false + }, + "conditions": { + "all": [ + { + "fact": "operationPath", + "operator": "equal", + "value": "/settlements" + }, + { + "fact": "method", + "operator": "equal", + "value": "post" + } + ] + }, + "event": { + "method": null, + "path": null, + "params": { + "body": { + "id": "123", + "state": "PENDING_SETTLEMENT", + "settlementWindows": [ + [ + { + "id": 123, + "createdDate": "1954-11-03", + "state": "PENDING_SETTLEMENT", + "reason": "do", + "changedDate": "1954-11-03" + } + ] + ], + "participants": [ + { + "id": -91450113, + "accounts": [ + { + "id": 81795155, + "reason": "veniam est proident commodo aliqua", + "state": "PENDING_SETTLEMENT", + "netSettlementAmount": { + "amount": 100, + "currency": "USD" + } + } + ] + } + ] + }, + "statusCode": "200" + }, + "delay": 0, + "type": "FIXED_RESPONSE" + } + }, + { + "type": "response", + "version": 1, + "ruleId": 4, + "priority": 1, + "description": "get /settlements/{id}", + "apiVersion": { + "minorVersion": 0, + "majorVersion": 1, + "type": "settlements", + "asynchronous": false + }, + "conditions": { + "all": [ + { + "fact": "operationPath", + "operator": "equal", + "value": "/settlements/{id}" + }, + { + "fact": "method", + "operator": "equal", + "value": "get" + } + ] + }, + "event": { + "method": null, + "path": null, + "params": { + "body": { + "id": "{$request.params.id}", + "state": "PS_TRANSFERS_RECORDED", + "settlementWindows": [ + [ + { + "createdDate": "2020-02-10", + "id": "{$request.params.id}", + "state": "PS_TRANSFERS_RECORDED", + "reason": "amet cillum culpa v", + "changedDate": "2020-02-10" + } + ] + ], + "participants": [ + { + "id": -35933071, + "accounts": [ + { + "id": -14386227, + "reason": "aliquip aliqua nulla deserunt", + "state": "PS_TRANSFERS_RECORDED", + "netSettlementAmount": { + "amount": 100, + "currency": "USD" + } + } + ] + } + ] + }, + "statusCode": "200" + }, + "delay": 0, + "type": "FIXED_RESPONSE" + } + }, + { + "type": "response", + "version": 1, + "ruleId": 5, + "priority": 1, + "description": "get /settlementWindows", + "apiVersion": { + "minorVersion": 0, + "majorVersion": 1, + "type": "settlements", + "asynchronous": false + }, + "conditions": { + "all": [ + { + "fact": "operationPath", + "operator": "equal", + "value": "/settlementWindows" + }, + { + "fact": "method", + "operator": "equal", + "value": "get" + } + ] + }, + "event": { + "method": null, + "path": null, + "params": { + "body": [ + { + "createdDate": "2020-02-10", + "id": 123, + "state": "{$request.query.state}", + "reason": "string", + "changedDate": "2020-02-10" + } + ], + "statusCode": "200" + }, + "delay": 0, + "type": "FIXED_RESPONSE" + } + }, { "ruleId": 6, "priority": 1, diff --git a/kubernetes/social-payments-account-registry/values.yaml b/kubernetes/social-payments-account-registry/values.yaml index 1b00c8a..325047b 100644 --- a/kubernetes/social-payments-account-registry/values.yaml +++ b/kubernetes/social-payments-account-registry/values.yaml @@ -17,7 +17,8 @@ postgresql: sunbird-rc-g2p-mapper-registry: registry: envVars: - connectionInfo_uri: "jdbc:postgresql://postgres-postgresql.postgres:5432/spar-mapper-registry" + connectionInfo_uri: "jdbc:postgresql://postgres-postgresql.postgres:5432/spardb" + connectionInfo_username: "sparuser" envVarsFrom: connectionInfo_password: valueFrom: diff --git a/kubernetes/superset/install.sh b/kubernetes/superset/install.sh new file mode 100755 index 0000000..241731c --- /dev/null +++ b/kubernetes/superset/install.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash + +. ../utils/common.sh + +export SANDBOX_HOSTNAME=${SANDBOX_HOSTNAME:-openg2p.sandbox.net} +export KEYCLOAK_HOSTNAME=${KEYCLOAK_HOSTNAME:-keycloak.$SANDBOX_HOSTNAME} +export SUPERSET_HOSTNAME=${SUPERSET_HOSTNAME:-superset.$SANDBOX_HOSTNAME} +export KEYCLOAK_REALM_NAME=${KEYCLOAK_REALM_NAME:-openg2p} +export SUPERSET_SECRET_KEY=$(generate_random_secret) + +helm repo add superset https://apache.github.io/superset +helm repo update + +COPY_UTIL=../utils/copy_cm_func.sh +NS=superset + +echo Create $NS namespace +kubectl create ns $NS + +$COPY_UTIL secret keycloak-client-secrets keycloak $NS + +envsubst < values-superset.template.yaml | helm -n $NS upgrade --install superset superset/superset --version 0.11.2 --wait $@ -f - + +envsubst < istio-virtualservice.template.yaml | kubectl -n $NS apply -f - \ No newline at end of file diff --git a/kubernetes/superset/istio-virtualservice.template.yaml b/kubernetes/superset/istio-virtualservice.template.yaml new file mode 100644 index 0000000..b4f136b --- /dev/null +++ b/kubernetes/superset/istio-virtualservice.template.yaml @@ -0,0 +1,19 @@ +apiVersion: networking.istio.io/v1beta1 +kind: VirtualService +metadata: + name: superset +spec: + gateways: + - istio-system/all-hosts + hosts: + - ${SUPERSET_HOSTNAME} + http: + - route: + - destination: + host: superset + port: + number: 8088 + headers: + request: + set: + x-forwarded-proto: https diff --git a/kubernetes/superset/values-superset.template.yaml b/kubernetes/superset/values-superset.template.yaml new file mode 100644 index 0000000..dbf36b0 --- /dev/null +++ b/kubernetes/superset/values-superset.template.yaml @@ -0,0 +1,78 @@ +init: + createAdmin: false + +bootstrapScript: | + #!/bin/bash + pip install authlib==1.3.0 + +extraSecretEnv: + SUPERSET_SECRET_KEY: ${SUPERSET_SECRET_KEY} + +extraEnvRaw: + - name: OAUTH_CLIENT_ID + value: openg2p-superset-client + - name: OAUTH_CLIENT_SECRET + valueFrom: + secretKeyRef: + name: keycloak-client-secrets + key: openg2p_superset_client_secret + + +configOverrides: + enable_oauth: | + # This will make sure the redirect_uri is properly computed, even with SSL offloading + ENABLE_PROXY_FIX = True + + from flask_appbuilder.security.manager import AUTH_OAUTH + AUTH_TYPE = AUTH_OAUTH + OAUTH_PROVIDERS = [ + { + "name": "keycloak", + "icon": "fa-key", + "token_key": "access_token", + "remote_app": { + "client_id": os.getenv("OAUTH_CLIENT_ID"), + "client_secret": os.getenv("OAUTH_CLIENT_SECRET"), + "api_base_url": "http://keycloak.keycloak/realms/${KEYCLOAK_REALM_NAME}/protocol/openid-connect", + "client_kwargs": {"scope": "email profile openid"}, + "access_token_url": "http://keycloak.keycloak/realms/${KEYCLOAK_REALM_NAME}/protocol/openid-connect/token", + "jwks_uri": "http://keycloak.keycloak/realms/${KEYCLOAK_REALM_NAME}/protocol/openid-connect/certs", + "authorize_url": "https://${KEYCLOAK_HOSTNAME}/realms/${KEYCLOAK_REALM_NAME}/protocol/openid-connect/auth", + "request_token_url": None + }, + } + ] + + # Map Authlib roles to superset roles + AUTH_ROLES_MAPPING = { + "superset_Public": ["Public"], + "superset_Admin": ["Admin"], + } + + AUTH_ROLES_SYNC_AT_LOGIN = True + + # Will allow user self registration, allowing to create Flask users from Authorized User + AUTH_USER_REGISTRATION = True + + # The default user self registration role + # AUTH_USER_REGISTRATION_ROLE = "Public" + + from superset.security import SupersetSecurityManager + class CustomSsoSecurityManager(SupersetSecurityManager): + def oauth_user_info(self, provider, response=None): + if provider == "keycloak": + me = self.appbuilder.sm.oauth_remotes[provider].get( + "openid-connect/userinfo" + ) + me.raise_for_status() + data = me.json() + return { + "username": data.get("preferred_username", ""), + "first_name": data.get("given_name", ""), + "last_name": data.get("family_name", ""), + "email": data.get("email", ""), + "role_keys": data.get("groups", []), + } + return {} + + CUSTOM_SECURITY_MANAGER = CustomSsoSecurityManager