# resources here are used to configure keycloak instance for SSO
apiVersion: v1
kind: ServiceAccount
metadata:
  name: keycloak-config
  namespace: keycloak
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: keycloak-config
  namespace: keycloak
rules:
  - apiGroups: [""]
    resources: ["secrets"]
    verbs: ["get", "create", "update", "patch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: keycloak-config
  namespace: keycloak
subjects:
  - kind: ServiceAccount
    name: keycloak-config
    namespace: keycloak
roleRef:
  kind: Role
  name: keycloak-config
  apiGroup: rbac.authorization.k8s.io
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: keycloak-config
  namespace: argocd
rules:
  - apiGroups: [""]
    resources: ["secrets"]
    verbs: ["get"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: keycloak-config
  namespace: argocd
subjects:
  - kind: ServiceAccount
    name: keycloak-config
    namespace: keycloak
roleRef:
  kind: Role
  name: keycloak-config
  apiGroup: rbac.authorization.k8s.io
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: config-job
  namespace: keycloak
data:
  client-scope-groups-payload.json: |
    {
      "name": "groups",
      "description": "groups a user belongs to",
      "attributes": {
          "consent.screen.text": "Access to groups a user belongs to.",
          "display.on.consent.screen": "true",
          "include.in.token.scope": "true",
          "gui.order": ""
      },
      "type": "default",
      "protocol": "openid-connect"
    }    
  group-admin-payload.json: |
    {"name":"admin"}    
  group-base-user-payload.json: |
    {"name":"base-user"}    
  group-mapper-payload.json: |
    {
        "protocol": "openid-connect",
        "protocolMapper": "oidc-group-membership-mapper",
        "name": "groups",
        "config": {
          "claim.name": "groups",
          "full.path": "false",
          "id.token.claim": "true",
          "access.token.claim": "true",
          "userinfo.token.claim": "true"
        }
    }    
  realm-payload.json: |
    {"realm":"cnoe","enabled":true}    
  user-password.json: |
    {
        "temporary": false,
        "type": "password",
        "value": "${USER1_PASSWORD}"
    }    
  user-user1.json: |
    {
        "username": "user1",
        "email": "user1@user.de",
        "firstName": "user",
        "lastName": "one",
        "requiredActions": [],
        "emailVerified": true,
        "groups": [
          "/admin"
        ],
        "enabled": true
    }    
  user-user2.json: |
    {
        "username": "user2",
        "email": "user2@user.de",
        "firstName": "user",
        "lastName": "two",
        "requiredActions": [],
        "emailVerified": true,
        "groups": [
          "/base-user"
        ],
        "enabled": true
    }    
  argo-client-payload.json: |
    {
      "protocol": "openid-connect",
      "clientId": "argo-workflows",
      "name": "Argo Workflows Client",
      "description": "Used for Argo Workflows SSO",
      "publicClient": false,
      "authorizationServicesEnabled": false,
      "serviceAccountsEnabled": false,
      "implicitFlowEnabled": false,
      "directAccessGrantsEnabled": true,
      "standardFlowEnabled": true,
      "frontchannelLogout": true,
      "attributes": {
        "saml_idp_initiated_sso_url_name": "",
        "oauth2.device.authorization.grant.enabled": false,
        "oidc.ciba.grant.enabled": false
      },
      "alwaysDisplayInConsole": false,
      "rootUrl": "",
      "baseUrl": "",
      "redirectUris": [
        "https://:443/argo-workflows/oauth2/callback"
      ],
      "webOrigins": [
        "/*"
      ]
    }    

  backstage-client-payload.json: |
    {
      "protocol": "openid-connect",
      "clientId": "backstage",
      "name": "Backstage Client",
      "description": "Used for Backstage SSO",
      "publicClient": false,
      "authorizationServicesEnabled": false,
      "serviceAccountsEnabled": false,
      "implicitFlowEnabled": false,
      "directAccessGrantsEnabled": true,
      "standardFlowEnabled": true,
      "frontchannelLogout": true,
      "attributes": {
        "saml_idp_initiated_sso_url_name": "",
        "oauth2.device.authorization.grant.enabled": false,
        "oidc.ciba.grant.enabled": false
      },
      "alwaysDisplayInConsole": false,
      "rootUrl": "",
      "baseUrl": "",
      "redirectUris": [
        "https://:443/api/auth/keycloak-oidc/handler/frame"
      ],
      "webOrigins": [
        "/*"
      ]
    }    

  grafana-client-payload.json: |
    {
      "clientId": "grafana",
      "name": "Grafana Client",
      "description": "Used for Grafana SSO",
      "rootUrl": "https:///grafana",
      "adminUrl": "https:///grafana",
      "baseUrl": "https:///grafana",
      "alwaysDisplayInConsole": false,
      "redirectUris": [
        "http:///grafana/*"
      ],
      "webOrigins": [
        "https:///grafana"
      ],
      "standardFlowEnabled": true,
      "implicitFlowEnabled": false,
      "directAccessGrantsEnabled": true,
      "serviceAccountsEnabled": false,
      "publicClient": false,
      "frontchannelLogout": true,
      "protocol": "openid-connect",
      "attributes": {
        "saml_idp_initiated_sso_url_name": "",
        "oidc.ciba.grant.enabled": "false",
        "oauth2.device.authorization.grant.enabled": "false"
      },
      "defaultClientScopes": [
        "web-origins",
        "acr",
        "offline_access",
        "roles",
        "profile",
        "groups",
        "email"
      ]
    }

  argocd-client-payload.json: |
    {
      "protocol": "openid-connect",
      "clientId": "argocd",
      "name": "ArgoCD Client",
      "description": "Used for ArgoCD SSO",
      "publicClient": false,
      "authorizationServicesEnabled": false,
      "serviceAccountsEnabled": false,
      "implicitFlowEnabled": false,
      "directAccessGrantsEnabled": true,
      "standardFlowEnabled": true,
      "frontchannelLogout": true,
      "attributes": {
        "saml_idp_initiated_sso_url_name": "",
        "oauth2.device.authorization.grant.enabled": false,
        "oidc.ciba.grant.enabled": false
      },
      "alwaysDisplayInConsole": false,
      "rootUrl": "",
      "baseUrl": "",
      "redirectUris": [
        "https:///*"
      ],
      "webOrigins": [
        "/*"
      ]
    }      

  forgejo-client-payload.json: |
    {
      "protocol": "openid-connect",
      "clientId": "forgejo",
      "name": "Forgejo Client",
      "description": "Used for Forgejo SSO",
      "publicClient": false,
      "authorizationServicesEnabled": false,
      "serviceAccountsEnabled": false,
      "implicitFlowEnabled": false,
      "directAccessGrantsEnabled": true,
      "standardFlowEnabled": true,
      "frontchannelLogout": true,
      "attributes": {
        "saml_idp_initiated_sso_url_name": "",
        "oauth2.device.authorization.grant.enabled": false,
        "oidc.ciba.grant.enabled": false
      },
      "alwaysDisplayInConsole": false,
      "rootUrl": "",
      "baseUrl": "",
      "redirectUris": [
        "https://gitea./*"
      ],
      "webOrigins": [
        "/*"
      ]
    }      

---
apiVersion: batch/v1
kind: Job
metadata:
  name: config
  namespace: keycloak
  annotations:
    argocd.argoproj.io/hook: PostSync
spec:
  template:
    metadata:
      generateName: config
    spec:
      serviceAccountName: keycloak-config
      restartPolicy: Never
      volumes:
        - name: keycloak-config
          secret:
            secretName: keycloak-config
        - name: config-payloads
          configMap:
            name: config-job
      containers:
        - name: kubectl
          image: docker.io/library/ubuntu:22.04
          volumeMounts:
            - name: keycloak-config
              readOnly: true
              mountPath: "/var/secrets/"
            - name: config-payloads
              readOnly: true
              mountPath: "/var/config/"
          command: ["/bin/bash", "-c"]
          args:
            - |
              #! /bin/bash              
  
              set -ex -o pipefail
              
              apt -qq update && apt -qq install curl jq -y
              
              ADMIN_PASSWORD=$(cat /var/secrets/KEYCLOAK_ADMIN_PASSWORD)
              USER1_PASSWORD=$(cat /var/secrets/USER_PASSWORD)


              KEYCLOAK_URL=http://keycloak.keycloak.svc.cluster.local:8080/keycloak



              KEYCLOAK_TOKEN=$(curl -sS  --fail-with-body -X POST -H "Content-Type: application/x-www-form-urlencoded" \
                --data-urlencode "username=cnoe-admin" \
                --data-urlencode "password=${ADMIN_PASSWORD}" \
                --data-urlencode "grant_type=password" \
                --data-urlencode "client_id=admin-cli" \
                ${KEYCLOAK_URL}/realms/master/protocol/openid-connect/token | jq -e -r '.access_token')
                
              set +e
              
              curl --fail-with-body -H "Authorization: bearer ${KEYCLOAK_TOKEN}"  "${KEYCLOAK_URL}/admin/realms/cnoe"  &> /dev/null
              if [ $? -eq 0 ]; then
                exit 0
              fi
              set -e
              
              if [[ "$(uname -m)" == "x86_64" ]]; then
                curl -sS -LO "https://dl.k8s.io/release/v1.28.3//bin/linux/amd64/kubectl"
              else
                curl -sS -LO "https://dl.k8s.io/release/v1.28.3//bin/linux/arm64/kubectl"
              fi
              chmod +x kubectl
              
              echo "creating cnoe realm and groups"
              
              curl -sS -H "Content-Type: application/json" \
                -H "Authorization: bearer ${KEYCLOAK_TOKEN}" \
                -X POST --data @/var/config/realm-payload.json \
                ${KEYCLOAK_URL}/admin/realms
              
              curl -sS -H "Content-Type: application/json" \
                -H "Authorization: bearer ${KEYCLOAK_TOKEN}" \
                -X POST --data @/var/config/client-scope-groups-payload.json \
                ${KEYCLOAK_URL}/admin/realms/cnoe/client-scopes
              
              curl -sS -H "Content-Type: application/json" \
                -H "Authorization: bearer ${KEYCLOAK_TOKEN}" \
                -X POST --data @/var/config/group-admin-payload.json \
                ${KEYCLOAK_URL}/admin/realms/cnoe/groups
              
              curl -sS -H "Content-Type: application/json" \
                -H "Authorization: bearer ${KEYCLOAK_TOKEN}" \
                -X POST --data @/var/config/group-base-user-payload.json \
                ${KEYCLOAK_URL}/admin/realms/cnoe/groups
              
              # Create scope mapper
              echo 'adding group claim to tokens'
              CLIENT_SCOPE_GROUPS_ID=$(curl -sS -H "Content-Type: application/json" -H "Authorization: bearer ${KEYCLOAK_TOKEN}" -X GET  ${KEYCLOAK_URL}/admin/realms/cnoe/client-scopes | jq -e -r  '.[] | select(.name == "groups") | .id')
              
              curl -sS -H "Content-Type: application/json" \
                -H "Authorization: bearer ${KEYCLOAK_TOKEN}" \
                -X POST --data @/var/config/group-mapper-payload.json \
                ${KEYCLOAK_URL}/admin/realms/cnoe/client-scopes/${CLIENT_SCOPE_GROUPS_ID}/protocol-mappers/models
                
              echo "creating test users"
              curl -sS -H "Content-Type: application/json" \
                -H "Authorization: bearer ${KEYCLOAK_TOKEN}" \
                -X POST --data @/var/config/user-user1.json \
                ${KEYCLOAK_URL}/admin/realms/cnoe/users
              
              curl -sS -H "Content-Type: application/json" \
                -H "Authorization: bearer ${KEYCLOAK_TOKEN}" \
                -X POST --data @/var/config/user-user2.json \
                ${KEYCLOAK_URL}/admin/realms/cnoe/users
              
              USER1ID=$(curl -sS -H "Content-Type: application/json" \
                -H "Authorization: bearer ${KEYCLOAK_TOKEN}" "${KEYCLOAK_URL}/admin/realms/cnoe/users?lastName=one" | jq -r '.[0].id')
              USER2ID=$(curl -sS -H "Content-Type: application/json" \
                -H "Authorization: bearer ${KEYCLOAK_TOKEN}" "${KEYCLOAK_URL}/admin/realms/cnoe/users?lastName=two" | jq -r '.[0].id')
              
              echo "setting user passwords"
              jq -r --arg pass ${USER1_PASSWORD}  '.value = $pass' /var/config/user-password.json > /tmp/user-password-to-be-applied.json
              
              curl -sS -H "Content-Type: application/json" \
                -H "Authorization: bearer ${KEYCLOAK_TOKEN}" \
                -X PUT --data @/tmp/user-password-to-be-applied.json \
                ${KEYCLOAK_URL}/admin/realms/cnoe/users/${USER1ID}/reset-password
              
              curl -sS -H "Content-Type: application/json" \
                -H "Authorization: bearer ${KEYCLOAK_TOKEN}" \
                -X PUT --data @/tmp/user-password-to-be-applied.json \
                ${KEYCLOAK_URL}/admin/realms/cnoe/users/${USER2ID}/reset-password
            
              echo "creating Argo Workflows client"
              curl -sS -H "Content-Type: application/json" \
                -H "Authorization: bearer ${KEYCLOAK_TOKEN}" \
                -X POST --data @/var/config/argo-client-payload.json \
                ${KEYCLOAK_URL}/admin/realms/cnoe/clients
              
              CLIENT_ID=$(curl -sS -H "Content-Type: application/json" \
                -H "Authorization: bearer ${KEYCLOAK_TOKEN}" \
                -X GET ${KEYCLOAK_URL}/admin/realms/cnoe/clients | jq -e -r  '.[] | select(.clientId == "argo-workflows") | .id')
              CLIENT_SCOPE_GROUPS_ID=$(curl -sS -H "Content-Type: application/json" \
                -H "Authorization: bearer ${KEYCLOAK_TOKEN}" \
                -X GET  ${KEYCLOAK_URL}/admin/realms/cnoe/client-scopes | jq -e -r  '.[] | select(.name == "groups") | .id')
              
              curl -sS -H "Content-Type: application/json" \
                -H "Authorization: bearer ${KEYCLOAK_TOKEN}" \
                -X PUT  ${KEYCLOAK_URL}/admin/realms/cnoe/clients/${CLIENT_ID}/default-client-scopes/${CLIENT_SCOPE_GROUPS_ID}
              
              ARGO_WORKFLOWS_CLIENT_SECRET=$(curl -sS -H "Content-Type: application/json" \
                -H "Authorization: bearer ${KEYCLOAK_TOKEN}" \
                -X GET ${KEYCLOAK_URL}/admin/realms/cnoe/clients/${CLIENT_ID} | jq -e -r '.secret')

              echo "creating Grafana client"
              curl -sS -H "Content-Type: application/json" \
                -H "Authorization: bearer ${KEYCLOAK_TOKEN}" \
                -X POST --data @/var/config/grafana-client-payload.json \
                ${KEYCLOAK_URL}/admin/realms/cnoe/clients
              
              CLIENT_ID=$(curl -sS -H "Content-Type: application/json" \
                -H "Authorization: bearer ${KEYCLOAK_TOKEN}" \
                -X GET ${KEYCLOAK_URL}/admin/realms/cnoe/clients | jq -e -r  '.[] | select(.clientId == "grafana") | .id')
              
              CLIENT_SCOPE_GROUPS_ID=$(curl -sS -H "Content-Type: application/json" \
                -H "Authorization: bearer ${KEYCLOAK_TOKEN}" \
                -X GET  ${KEYCLOAK_URL}/admin/realms/cnoe/client-scopes | jq -e -r  '.[] | select(.name == "groups") | .id')
              
              curl -sS -H "Content-Type: application/json" \
                -H "Authorization: bearer ${KEYCLOAK_TOKEN}" \
                -X PUT  ${KEYCLOAK_URL}/admin/realms/cnoe/clients/${CLIENT_ID}/default-client-scopes/${CLIENT_SCOPE_GROUPS_ID}
              
              GRAFANA_CLIENT_SECRET=$(curl -sS -H "Content-Type: application/json" \
                -H "Authorization: bearer ${KEYCLOAK_TOKEN}" \
                -X GET ${KEYCLOAK_URL}/admin/realms/cnoe/clients/${CLIENT_ID} | jq -e -r '.secret')

              echo "creating Backstage client"
              curl -sS -H "Content-Type: application/json" \
                -H "Authorization: bearer ${KEYCLOAK_TOKEN}" \
                -X POST --data @/var/config/backstage-client-payload.json \
                ${KEYCLOAK_URL}/admin/realms/cnoe/clients
              
              CLIENT_ID=$(curl -sS -H "Content-Type: application/json" \
                -H "Authorization: bearer ${KEYCLOAK_TOKEN}" \
                -X GET ${KEYCLOAK_URL}/admin/realms/cnoe/clients | jq -e -r  '.[] | select(.clientId == "backstage") | .id')
              
              CLIENT_SCOPE_GROUPS_ID=$(curl -sS -H "Content-Type: application/json" \
                -H "Authorization: bearer ${KEYCLOAK_TOKEN}" \
                -X GET  ${KEYCLOAK_URL}/admin/realms/cnoe/client-scopes | jq -e -r  '.[] | select(.name == "groups") | .id')
              
              curl -sS -H "Content-Type: application/json" \
                -H "Authorization: bearer ${KEYCLOAK_TOKEN}" \
                -X PUT  ${KEYCLOAK_URL}/admin/realms/cnoe/clients/${CLIENT_ID}/default-client-scopes/${CLIENT_SCOPE_GROUPS_ID}
              
              BACKSTAGE_CLIENT_SECRET=$(curl -sS -H "Content-Type: application/json" \
                -H "Authorization: bearer ${KEYCLOAK_TOKEN}" \
                -X GET ${KEYCLOAK_URL}/admin/realms/cnoe/clients/${CLIENT_ID} | jq -e -r '.secret')

              echo "creating ArgoCD client"
              curl -sS -H "Content-Type: application/json" \
                -H "Authorization: bearer ${KEYCLOAK_TOKEN}" \
                -X POST --data @/var/config/argocd-client-payload.json \
                ${KEYCLOAK_URL}/admin/realms/cnoe/clients
              
              CLIENT_ID=$(curl -sS -H "Content-Type: application/json" \
                -H "Authorization: bearer ${KEYCLOAK_TOKEN}" \
                -X GET ${KEYCLOAK_URL}/admin/realms/cnoe/clients | jq -e -r  '.[] | select(.clientId == "argocd") | .id')
              
              CLIENT_SCOPE_GROUPS_ID=$(curl -sS -H "Content-Type: application/json" \
                -H "Authorization: bearer ${KEYCLOAK_TOKEN}" \
                -X GET  ${KEYCLOAK_URL}/admin/realms/cnoe/client-scopes | jq -e -r  '.[] | select(.name == "groups") | .id')
              
              curl -sS -H "Content-Type: application/json" \
                -H "Authorization: bearer ${KEYCLOAK_TOKEN}" \
                -X PUT  ${KEYCLOAK_URL}/admin/realms/cnoe/clients/${CLIENT_ID}/default-client-scopes/${CLIENT_SCOPE_GROUPS_ID}
              
              ARGOCD_CLIENT_SECRET=$(curl -sS -H "Content-Type: application/json" \
                -H "Authorization: bearer ${KEYCLOAK_TOKEN}" \
                -X GET ${KEYCLOAK_URL}/admin/realms/cnoe/clients/${CLIENT_ID} | jq -e -r '.secret')  
              
              echo "creating Forgejo client"
              curl -sS -H "Content-Type: application/json" \
                -H "Authorization: bearer ${KEYCLOAK_TOKEN}" \
                -X POST --data @/var/config/forgejo-client-payload.json \
                ${KEYCLOAK_URL}/admin/realms/cnoe/clients
              
              CLIENT_ID=$(curl -sS -H "Content-Type: application/json" \
                -H "Authorization: bearer ${KEYCLOAK_TOKEN}" \
                -X GET ${KEYCLOAK_URL}/admin/realms/cnoe/clients | jq -e -r  '.[] | select(.clientId == "forgejo") | .id')
              
              CLIENT_SCOPE_GROUPS_ID=$(curl -sS -H "Content-Type: application/json" \
                -H "Authorization: bearer ${KEYCLOAK_TOKEN}" \
                -X GET  ${KEYCLOAK_URL}/admin/realms/cnoe/client-scopes | jq -e -r  '.[] | select(.name == "groups") | .id')
              
              curl -sS -H "Content-Type: application/json" \
                -H "Authorization: bearer ${KEYCLOAK_TOKEN}" \
                -X PUT  ${KEYCLOAK_URL}/admin/realms/cnoe/clients/${CLIENT_ID}/default-client-scopes/${CLIENT_SCOPE_GROUPS_ID}
              
              FORGEJO_CLIENT_SECRET=$(curl -sS -H "Content-Type: application/json" \
                -H "Authorization: bearer ${KEYCLOAK_TOKEN}" \
                -X GET ${KEYCLOAK_URL}/admin/realms/cnoe/clients/${CLIENT_ID} | jq -e -r '.secret')  

              ARGOCD_PASSWORD=$(./kubectl -n argocd get secret argocd-initial-admin-secret -o go-template='{{.data.password | base64decode }}')
              
              ARGOCD_SESSION_TOKEN=$(curl -sS https:///argocd/api/v1/session -H 'Content-Type: application/json' -d "{\"username\":\"admin\",\"password\":\"${ARGOCD_PASSWORD}\"}" | jq -r .token)
              
              echo \
              "apiVersion: v1
              kind: Secret
              metadata:
                name: keycloak-clients
                namespace: keycloak
              type: Opaque
              stringData:
                ARGO_WORKFLOWS_CLIENT_SECRET: ${ARGO_WORKFLOWS_CLIENT_SECRET}
                ARGO_WORKFLOWS_CLIENT_ID: argo-workflows
                ARGOCD_SESSION_TOKEN: ${ARGOCD_SESSION_TOKEN}
                BACKSTAGE_CLIENT_SECRET: ${BACKSTAGE_CLIENT_SECRET}
                BACKSTAGE_CLIENT_ID: backstage
                GRAFANA_CLIENT_SECRET: ${GRAFANA_CLIENT_SECRET}
                GRAFANA_CLIENT_ID: grafana
                ARGOCD_CLIENT_SECRET: ${ARGOCD_CLIENT_SECRET}
                ARGOCD_CLIENT_ID: argocd
                FORGEJO_CLIENT_SECRET: ${FORGEJO_CLIENT_SECRET}
                FORGEJO_CLIENT_ID: forgejo
              " > /tmp/secret.yaml
              
              ./kubectl apply -f /tmp/secret.yaml
