name: Testing PKI CA

on: workflow_call

env:
  NAMESPACE: ${{ vars.REGISTRY_NAMESPACE || 'dogtagpki' }}
  DS_IMAGE: ${{ vars.DS_IMAGE || 'quay.io/389ds/dirsrv' }}

jobs:
  test:
    name: Test
    runs-on: ubuntu-latest
    env:
      SHARED: /tmp/workdir/jss
    steps:
      - name: Clone repository
        uses: actions/checkout@v4

      - name: Retrieve JSS images
        uses: actions/cache@v4
        with:
          key: jss-images-${{ github.sha }}
          path: jss-images.tar

      - name: Load JSS images
        run: docker load --input jss-images.tar

      - name: Create network
        run: docker network create example

      - name: Set up DS container
        run: |
          tests/bin/ds-create.sh \
              --image=${{ env.DS_IMAGE }} \
              --hostname=ds.example.com \
              --password=Secret.123 \
              --network=example \
              --network-alias=ds.example.com \
              ds

      - name: Set up server container
        run: |
          tests/bin/runner-init.sh \
              --hostname=pki.example.com \
              pki

          docker network connect example pki \
              --alias pki.example.com \
              --alias server.example.com

      - name: Set up secondary server container
        run: |
          tests/bin/runner-init.sh \
              --hostname=pki2.example.com \
              --network=example \
              --network-alias=pki2.example.com \
              pki2

      - name: Set up client container for non-blocking socket factory
        run: |
          tests/bin/runner-init.sh \
              --hostname=client.example.com \
              --network=example \
              --network-alias=client.example.com \
              client

      - name: Import LDAP SDK packages
        run: |
          docker create --name=ldapjdk-dist quay.io/$NAMESPACE/ldapjdk-dist:5.6
          docker cp ldapjdk-dist:/root/RPMS/. /tmp/RPMS/
          docker rm -f ldapjdk-dist

      - name: Import PKI packages
        run: |
          docker create --name=pki-dist quay.io/$NAMESPACE/pki-dist:11.9
          docker cp pki-dist:/root/RPMS/. /tmp/RPMS/
          docker rm -f pki-dist

      - name: Install packages
        run: |
          # install packages on server
          docker cp /tmp/RPMS/. pki:/root/RPMS/
          docker exec pki bash -c "dnf install -y /root/RPMS/*"

          # install packages on secondary server
          docker cp /tmp/RPMS/. pki2:/root/RPMS/
          docker exec pki2 bash -c "dnf install -y /root/RPMS/*"

          # install packages on client
          docker cp /tmp/RPMS/. client:/root/RPMS/
          docker exec client bash -c "dnf install -y /root/RPMS/*"

      - name: Install CA
        run: |
          docker exec pki pkispawn \
              -f /usr/share/pki/server/examples/installation/ca.cfg \
              -s CA \
              -D pki_ds_url=ldap://ds.example.com:3389 \
              -v

          # set buffer size to 0 so that revocation takes effect immediately
          docker exec pki pki-server ca-config-set auths.revocationChecking.bufferSize 0

          # enable signed audit log
          docker exec pki pki-server ca-config-set log.instance.SignedAudit.logSigning true

          # restart PKI server
          docker exec pki pki-server restart --wait

      - name: Run PKI healthcheck
        run: docker exec pki pki-healthcheck --debug

      - name: Initialize PKI client
        run: |
          docker exec pki pki-server cert-export ca_signing --cert-file $SHARED/ca_signing.crt

          docker exec pki pki nss-cert-import \
              --cert $SHARED/ca_signing.crt \
              --trust CT,C,C \
              ca_signing

          docker exec pki pki info

      - name: Check CA certs
        run: |
          docker exec pki /usr/share/pki/tests/ca/bin/test-ca-signing-cert.sh
          docker exec pki /usr/share/pki/tests/ca/bin/test-subsystem-cert.sh
          docker exec pki /usr/share/pki/tests/ca/bin/test-ca-certs.sh

      - name: Check CA admin
        run: |
          docker exec pki pki pkcs12-import \
              --pkcs12 /root/.dogtag/pki-tomcat/ca_admin_cert.p12 \
              --pkcs12-password Secret.123
          docker exec pki pki -n caadmin ca-user-show caadmin

      - name: Check CA agent
        run: |
          docker exec pki /usr/share/pki/tests/ca/bin/ca-agent-create.sh
          docker exec pki /usr/share/pki/tests/ca/bin/ca-agent-cert-create.sh
          docker exec pki /usr/share/pki/tests/ca/bin/ca-agent-cert-revoke.sh
          docker exec pki /usr/share/pki/tests/ca/bin/ca-agent-cert-unrevoke.sh

      - name: Check CA auditor
        run: |
          docker exec pki /usr/share/pki/tests/ca/bin/test-ca-auditor-create.sh
          docker exec pki /usr/share/pki/tests/ca/bin/test-ca-auditor-cert.sh
          docker exec pki /usr/share/pki/tests/ca/bin/test-ca-auditor-logs.sh

      - name: Check client with unknown issuer
        run: |
          # run PKI CLI
          echo n | docker exec -i client pki \
              -U https://pki.example.com:8443 \
              info \
              > >(tee stdout) 2> >(tee stderr >&2) || true

          # check stdout
          cat > expected << EOF
            Server URL: https://pki.example.com:8443
          EOF

          # check stderr
          # TODO: fix missing SSL alert
          cat > expected << EOF
          WARNING: UNKNOWN_ISSUER encountered on 'CN=pki.example.com,OU=pki-tomcat,O=EXAMPLE' indicates an unknown CA cert 'CN=CA Signing Certificate,OU=pki-tomcat,O=EXAMPLE'
          Trust this certificate (y/N)? SEVERE: FATAL: SSL alert sent: UNKNOWN_CA
          IOException: Unable to write to socket: Unable to validate CN=pki.example.com, OU=pki-tomcat, O=EXAMPLE: Unknown issuer: CN=CA Signing Certificate, OU=pki-tomcat, O=EXAMPLE
          EOF

          diff expected stderr

          # the cert should not be stored
          docker exec client pki nss-cert-find | tee output

          diff /dev/null output

      - name: Check client with unknown issuer and wrong hostname
        run: |
          # run PKI CLI
          echo n | docker exec -i client pki \
              -U https://server.example.com:8443 \
              info \
              > >(tee stdout) 2> >(tee stderr >&2) || true

          # check stdout
          cat > expected << EOF
            Server URL: https://server.example.com:8443
          EOF

          diff expected stdout

          # check stderr
          # TODO: fix missing SSL alert
          cat > expected << EOF
          WARNING: BAD_CERT_DOMAIN encountered on 'CN=pki.example.com,OU=pki-tomcat,O=EXAMPLE' indicates a common-name mismatch
          WARNING: UNKNOWN_ISSUER encountered on 'CN=pki.example.com,OU=pki-tomcat,O=EXAMPLE' indicates an unknown CA cert 'CN=CA Signing Certificate,OU=pki-tomcat,O=EXAMPLE'
          Trust this certificate (y/N)? SEVERE: FATAL: SSL alert sent: ACCESS_DENIED
          IOException: Unable to write to socket: Unable to validate CN=pki.example.com, OU=pki-tomcat, O=EXAMPLE: Bad certificate domain: CN=pki.example.com, OU=pki-tomcat, O=EXAMPLE
          EOF

          diff expected stderr

          # the cert should not be stored
          docker exec client pki nss-cert-find | tee output

          diff /dev/null output

      - name: Check client with newly trusted server cert
        run: |
          # run PKI CLI
          echo y | docker exec -i client pki \
              -U https://pki.example.com:8443 \
              info \
              > >(tee stdout) 2> >(tee stderr >&2) || true

          # check stdout
          cat > expected << EOF
            Server URL: https://pki.example.com:8443
            Server Name: Dogtag Certificate System
          EOF

          # ignore version number
          sed -i \
              -e '/^ *Server Version:/d' \
              stdout

          diff expected stdout

          # check stderr
          cat > expected << EOF
          WARNING: UNKNOWN_ISSUER encountered on 'CN=pki.example.com,OU=pki-tomcat,O=EXAMPLE' indicates an unknown CA cert 'CN=CA Signing Certificate,OU=pki-tomcat,O=EXAMPLE'
          Trust this certificate (y/N)?
          EOF

          # remove trailing whitespace
          sed -i 's/ *$//' stderr

          # append end of line
          echo >> stderr

          diff expected stderr

          # the cert should be stored and trusted
          docker exec client pki nss-cert-find | tee output

          sed -i \
              -e '/^ *Serial Number:/d' \
              -e '/^ *Not Valid Before:/d' \
              -e '/^ *Not Valid After:/d' \
              output

          cat > expected << EOF
            Nickname: CN=pki.example.com,OU=pki-tomcat,O=EXAMPLE
            Subject DN: CN=pki.example.com,OU=pki-tomcat,O=EXAMPLE
            Issuer DN: CN=CA Signing Certificate,OU=pki-tomcat,O=EXAMPLE
            Trust Flags: P,,
          EOF

          diff expected output

      - name: Check client with trusted server cert and wrong hostname
        run: |
          # run PKI CLI
          docker exec client pki \
              -U https://server.example.com:8443 \
              info \
              > >(tee stdout) 2> >(tee stderr >&2) || true

          # check stdout
          cat > expected << EOF
            Server URL: https://server.example.com:8443
            Server Name: Dogtag Certificate System
          EOF

          # ignore version number
          sed -i \
              -e '/^ *Server Version:/d' \
              stdout

          diff expected stdout

          # check stderr
          cat > expected << EOF
          WARNING: BAD_CERT_DOMAIN encountered on 'CN=pki.example.com,OU=pki-tomcat,O=EXAMPLE' indicates a common-name mismatch
          EOF

          diff expected stderr

      - name: Check client with already trusted server cert
        run: |
          # run PKI CLI
          docker exec client pki \
              -U https://pki.example.com:8443 \
              info \
              > >(tee stdout) 2> >(tee stderr >&2) || true

          # check stdout
          cat > expected << EOF
            Server URL: https://pki.example.com:8443
            Server Name: Dogtag Certificate System
          EOF

          # ignore version number
          sed -i \
              -e '/^ *Server Version:/d' \
              stdout

          diff expected stdout

          # check stderr
          diff /dev/null stderr

      - name: Check client with self-signed user cert
        run: |
          # generate user CSR
          docker exec client pki nss-cert-request \
              --subject "UID=testuser" \
              --ext /usr/share/pki/server/certs/admin.conf \
              --csr testuser.csr

          # issue self-signed user cert
          docker exec client pki nss-cert-issue \
              --csr testuser.csr \
              --ext /usr/share/pki/server/certs/admin.conf \
              --cert testuser.crt

          # import user cert
          docker exec client pki nss-cert-import \
              --cert testuser.crt \
              testuser

          # run PKI CLI
          docker exec client pki \
              -U https://pki.example.com:8443 \
              -n testuser \
              ca-user-find \
              > >(tee stdout) 2> >(tee stderr >&2) || true

          # check stderr
          # TODO: fix excessive SSL alert
          cat > expected << EOF
          SEVERE: FATAL: SSL alert received: UNKNOWN_CA
          SEVERE: FATAL: SSL alert sent: BAD_RECORD_MAC
          IOException: Unable to read from socket: Unexpected return from PR.Read(): SSL_ERROR_UNKNOWN_CA_ALERT (-12195)
          EOF

          diff expected stderr

      - name: Revoke sslserver certificate
        run: |
          SERIAL=$(docker exec pki  pki -d /etc/pki/pki-tomcat/alias nss-cert-show sslserver | sed -ne 's/^  Serial Number: \(.*\)$/\1/p')
          docker exec -i pki pki -n caadmin ca-cert-hold --force  $SERIAL

      - name: Check client with already trusted server cert revoked
        run: |
          # run PKI CLI
          docker exec client pki \
              -U https://pki.example.com:8443 \
              info \
              > >(tee stdout) 2> >(tee stderr >&2) || true

          # check stdout
          # the certificate is not verified because it is set as trusted peer
          cat > expected << EOF
            Server URL: https://pki.example.com:8443
            Server Name: Dogtag Certificate System
          EOF

          # ignore version number
          sed -i \
              -e '/^ *Server Version:/d' \
              stdout

          diff expected stdout

          # check stderr
          diff /dev/null stderr

      - name: Check client with trusted root and revoked server cert
        run: |
          # remove nss DB
          docker exec client rm -rf /root/.dogtag

          # init nss DB with trusted root
          docker exec client pki nss-cert-import \
              --cert $SHARED/ca_signing.crt \
              --trust CT,C,C \
              ca_signing

          docker exec client pki nss-cert-find

          # run PKI CLI
          docker exec client pki \
              -U https://pki.example.com:8443 \
              info \
              > >(tee stdout) 2> >(tee stderr >&2) || true

          # check stdout
          # the certificate is verified and the operation fails
          cat > expected << EOF
            Server URL: https://pki.example.com:8443
          EOF

          diff expected stdout

          # check stderr
          cat > expected << EOF
          ERROR: REVOKED_CERTIFICATE encountered on 'CN=pki.example.com,OU=pki-tomcat,O=EXAMPLE' results in a denied SSL server cert!
          SEVERE: FATAL: SSL alert sent: CERTIFICATE_REVOKED
          IOException: Unable to write to socket: Unable to validate CN=pki.example.com, OU=pki-tomcat, O=EXAMPLE: Revoked certificate: CN=pki.example.com, OU=pki-tomcat, O=EXAMPLE
          EOF

          diff expected stderr

      - name: Check client with trusted root and revoked server cert but disabled check
        run: |
          # run PKI CLI
          docker exec client pki \
              --ignore-cert-status REVOKED_CERTIFICATE \
              -U https://pki.example.com:8443 \
              info \
              > >(tee stdout) 2> >(tee stderr >&2) || true

          # check stdout
          # the certificatei revocation is not verified so it is trusted
          cat > expected << EOF
            Server URL: https://pki.example.com:8443
            Server Name: Dogtag Certificate System
          EOF

          # ignore version number
          sed -i \
              -e '/^ *Server Version:/d' \
              stdout

          diff expected stdout

          # check stderr
          diff /dev/null stderr

      - name: Release sslserver certificate
        run: |
          SERIAL=$(docker exec pki  pki -d /etc/pki/pki-tomcat/alias nss-cert-show sslserver | sed -ne 's/^  Serial Number: \(.*\)$/\1/p')
          docker exec -i pki pki \
              --ignore-cert-status REVOKED_CERTIFICATE \
              -n caadmin \
              ca-cert-release-hold \
              --force \
              $SERIAL

      - name: Check client with trusted root
        run: |
          # run PKI CLI
          docker exec client pki \
              -U https://pki.example.com:8443 \
              info \
              > >(tee stdout) 2> >(tee stderr >&2) || true

          # check stdout
          cat > expected << EOF
            Server URL: https://pki.example.com:8443
            Server Name: Dogtag Certificate System
          EOF

          # ignore version number
          sed -i \
              -e '/^ *Server Version:/d' \
              stdout

          diff expected stdout

          # check stderr
          diff /dev/null stderr

      - name: Create secondary server instance
        run: |
          docker exec pki2 pki-server create -v
          docker exec pki2 pki-server nss-create --no-password
          docker exec pki2 pki \
              -d /var/lib/pki/pki-tomcat/alias \
              nss-cert-import \
              --cert $SHARED/ca_signing.crt \
              --trust CT,C,C \
              ca_signing

      - name: Generate certificate for secondary server
        run: |
          docker exec pki2 pki \
              -d /var/lib/pki/pki-tomcat/alias \
              nss-cert-request \
              --subject "CN=pki2.example.com" \
              --ext /usr/share/pki/server/certs/sslserver.conf \
              --csr $SHARED/sslserver2.csr

          docker exec pki pki \
              -n caadmin \
              ca-cert-issue \
              --profile caServerCert \
              --csr-file $SHARED/sslserver2.csr \
              --output-file $SHARED/sslserver2.crt

          docker exec pki2 pki \
              -d /var/lib/pki/pki-tomcat/alias \
              nss-cert-import \
              --cert $SHARED/sslserver2.crt \
              sslserver

      - name: Configure and start secondary server
        run: |
          docker exec pki2 pki-server jss-enable
          docker exec pki2 pki-server http-connector-add \
              --port 8443 \
              --scheme https \
              --secure true \
              --sslEnabled true \
              --sslProtocol SSL \
              --sslImpl org.dogtagpki.jss.tomcat.JSSImplementation \
              Secure

          docker exec pki2 pki-server http-connector-cert-add \
              --keyAlias sslserver \
              --keystoreType pkcs11 \
              --keystoreProvider Mozilla-JSS

          docker exec pki2 pki-server webapp-deploy \
              --descriptor /usr/share/pki/server/conf/Catalina/localhost/ROOT.xml \
              ROOT

          docker exec pki2 pki-server webapp-deploy \
              --descriptor /usr/share/pki/server/conf/Catalina/localhost/pki.xml \
              pki
          docker exec pki2 pki-server start --wait

      - name: Secondary check client with trusted root
        run: |
          # run PKI CLI
          docker exec client pki \
              -U https://pki2.example.com:8443 \
              info \
              > >(tee stdout) 2> >(tee stderr >&2) || true

          # check stdout
          cat > expected << EOF
            Server URL: https://pki2.example.com:8443
            Server Name: Dogtag Certificate System
          EOF

          # ignore version number
          sed -i \
              -e '/^ *Server Version:/d' \
              stdout

          diff expected stdout

          # check stderr
          diff /dev/null stderr

      - name: Disable OCSP responder from the server
        run: |
          docker exec pki pki-server stop --wait

      - name: Secondary check client with trusted root and disabled OCSP
        run: |
          # run PKI CLI
          docker exec client pki \
              -U https://pki2.example.com:8443 \
              info \
              > >(tee stdout) 2> >(tee stderr >&2) || true

          # check stdout
          # the certificate cannot be verified and the operation fails
          cat > expected << EOF
            Server URL: https://pki2.example.com:8443
          EOF

          diff expected stdout

          # check stderr
          cat > expected << EOF
          ERROR: OCSP_SERVER_ERROR encountered on 'CN=pki2.example.com' results in a denied SSL server cert!
          SEVERE: FATAL: SSL alert sent: ACCESS_DENIED
          IOException: Unable to write to socket: Unable to validate CN=pki2.example.com: Invalid certificate: CN=pki2.example.com
          EOF

          diff expected stderr

      - name: Secondary check client with trusted root and disabled OCSP but disabled check
        run: |
          # run PKI CLI
          docker exec client pki \
              --ignore-cert-status OCSP_SERVER_ERROR \
              -U https://pki2.example.com:8443 \
              info \
              > >(tee stdout) 2> >(tee stderr >&2) || true

          # check stdout
          # the OCSP server failure is ignored so it is trusted
          cat > expected << EOF
            Server URL: https://pki2.example.com:8443
            Server Name: Dogtag Certificate System
          EOF

          # ignore version number
          sed -i \
              -e '/^ *Server Version:/d' \
              stdout

          diff expected stdout

          # check stderr
          diff /dev/null stderr

      - name: Remove CA
        run: |
          docker exec pki pki-server start --wait
          docker exec pki pkidestroy -i pki-tomcat -s CA -v

      - name: Check DS server systemd journal
        if: always()
        run: |
          docker exec ds journalctl -x --no-pager -u dirsrv@localhost.service

      - name: Check DS container logs
        if: always()
        run: |
          docker logs ds

      - name: Check PKI server systemd journal
        if: always()
        run: |
          docker exec pki journalctl -x --no-pager -u pki-tomcatd@pki-tomcat.service

      - name: Check CA debug log
        if: always()
        run: |
          docker exec pki find /var/lib/pki/pki-tomcat/logs/ca -name "debug.*" -exec cat {} \;

      - name: Gather artifacts
        if: always()
        run: |
          tests/bin/ds-artifacts-save.sh ds
          tests/bin/pki-artifacts-save.sh pki
        continue-on-error: true

      - name: Upload artifacts
        if: always()
        uses: actions/upload-artifact@v4
        with:
          name: pki-ca-test
          path: /tmp/artifacts
